Commit 34a4fc3e58
Verified · cmc ci/build: success ci/test: success ci/vuln: success
Layout: unified · split
internal/control/control.go +5
| @@ -99,6 +99,11 @@ func Dispatch(c *Ctx, argv []string) int { | ||
| 99 | 99 | if c.User.Disabled { |
| 100 | 100 | return c.fail(protocol.ExitDenied, "this account is disabled") |
| 101 | 101 | } |
| 102 | // The admin noun is gated here as well as in each handler, so a new | |
| 103 | // admin command that forgets requireInstanceAdmin is still refused. | |
| 104 | if cmd.Path[0] == "admin" && !c.User.IsAdmin { | |
| 105 | return c.fail(protocol.ExitDenied, "admin commands are for instance admins") | |
| 106 | } | |
| 102 | 107 | if c.User.Pending && !pendingAllowed(cmd.Path) { |
| 103 | 108 | return c.fail(protocol.ExitDenied, |
| 104 | 109 | "your account is not active yet: verify your email first (email verify <code>, or ask for the mail again with email add)") |
internal/control/control_test.go +30
| @@ -9,6 +9,7 @@ import ( | ||
| 9 | 9 | "testing" |
| 10 | 10 | |
| 11 | 11 | "gitbay.org/gitbay/internal/protocol" |
| 12 | "gitbay.org/gitbay/internal/store" | |
| 12 | 13 | ) |
| 13 | 14 | |
| 14 | 15 | // TestEveryCommandReachableFromBareSSH asserts that each registered command's |
| @@ -163,3 +164,32 @@ func TestHelpListsEveryCommandSorted(t *testing.T) { | ||
| 163 | 164 | t.Error("help output is not sorted by path") |
| 164 | 165 | } |
| 165 | 166 | } |
| 167 | ||
| 168 | // TestStdinCommandsReadStdin: a command whose usage says its input arrives | |
| 169 | // on stdin must set ReadsStdin, or Dispatch hands it an empty reader and | |
| 170 | // --file - silently stores nothing (#127). | |
| 171 | func TestStdinCommandsReadStdin(t *testing.T) { | |
| 172 | for _, cmd := range Commands() { | |
| 173 | u := cmd.Usage | |
| 174 | wants := strings.Contains(u, "--file -") || strings.Contains(u, "< ") || | |
| 175 | strings.Contains(u, "stdin") || strings.Contains(u, "--key -") | |
| 176 | if wants && !cmd.ReadsStdin { | |
| 177 | t.Errorf("%s: usage %q reads stdin but ReadsStdin is not set", strings.Join(cmd.Path, " "), u) | |
| 178 | } | |
| 179 | } | |
| 180 | } | |
| 181 | ||
| 182 | // TestAdminNounGatedInDispatch: every admin command is refused for a | |
| 183 | // non-admin by the dispatcher itself, before any handler runs. | |
| 184 | func TestAdminNounGatedInDispatch(t *testing.T) { | |
| 185 | for _, cmd := range Commands() { | |
| 186 | if cmd.Path[0] != "admin" { | |
| 187 | continue | |
| 188 | } | |
| 189 | var out, errOut bytes.Buffer | |
| 190 | c := &Ctx{User: store.User{Username: "nobody"}, Scope: "full", Stdout: &out, Stderr: &errOut} | |
| 191 | if code := Dispatch(c, cmd.Path); code != protocol.ExitDenied { | |
| 192 | t.Errorf("%s: non-admin got exit %d, want %d", strings.Join(cmd.Path, " "), code, protocol.ExitDenied) | |
| 193 | } | |
| 194 | } | |
| 195 | } | |