Commit 69433232c5

69433232c5aae80adb658861d358b08c0418f002

parent: e3cd3c4236

Verified · cmc ci/build: success ci/sonar: success ci/test: success ci/vuln: success

cmc <hello@cleberg.net> · 2026-09-05 07:01 UTC

httpd: open registration in the checkOrigin route walk so POST /register is reachable

config.Default() leaves Registration.Mode at "closed", and routes.go only
registers /register when it isn't — so TestMutatingRoutesRequireCheckOrigin
never iterated POST /register at all, the one route the production instance
(registration = "open") actually serves that this walk should have caught.
Set Registration.Mode = "open" and fix the "superset of routes" comment that
made the gap easy to miss.

TestTopLevelRouteWordsAreReserved in routes_test.go carried the identical
comment and the identical omission; fixed it here too since it's the same
one-line gap in the same package, not a change worth a separate commit.

Ref #157

Layout: unified · split

internal/httpd/checkorigin_test.go +9 −1
@@ -30,8 +30,16 @@ var checkOriginAllowlist = map[string]string{
30 30
31func TestMutatingRoutesRequireCheckOrigin(t *testing.T) { 31func TestMutatingRoutesRequireCheckOrigin(t *testing.T) {
32 cfg := config.Default() 32 cfg := config.Default()
33 cfg.Web.Mode = "accounts" // superset of routes 33 cfg.Web.Mode = "accounts" // superset of accounts-mode routes
34 cfg.API.Enabled = true 34 cfg.API.Enabled = true
35 // /register is gated on registration.mode != "closed" (routes.go), which
36 // config.Default() leaves at "closed" — the production instance runs
37 // "open", and that is the one deployed value the route table hides its
38 // routes behind if this test's cfg does not open it too. "open" and
39 // "invite" gate the route identically (both are just != "closed"), so
40 // there is no second code path in routes.go for looping over both to
41 // reach; "open" alone matches production and is enough.
42 cfg.Registration.Mode = "open"
35 s := New(cfg, nil) 43 s := New(cfg, nil)
36 44
37 for _, r := range s.Routes() { 45 for _, r := range s.Routes() {
internal/httpd/routes_test.go +5 −1
@@ -77,7 +77,11 @@ func TestAccountsModeHasLoginRoute(t *testing.T) {
77// unclaimable username. 77// unclaimable username.
78func TestTopLevelRouteWordsAreReserved(t *testing.T) { 78func TestTopLevelRouteWordsAreReserved(t *testing.T) {
79 cfg := config.Default() 79 cfg := config.Default()
80 cfg.Web.Mode = "accounts" // superset of routes 80 cfg.Web.Mode = "accounts" // superset of accounts-mode routes
81 // /register only registers when registration.mode != "closed"
82 // (config.Default() leaves it "closed"); open it so this walk actually
83 // reaches the route the production instance runs with.
84 cfg.Registration.Mode = "open"
81 s := New(cfg, nil) 85 s := New(cfg, nil)
82 for _, r := range s.Routes() { 86 for _, r := range s.Routes() {
83 seg := strings.TrimPrefix(r.Pattern, "/") 87 seg := strings.TrimPrefix(r.Pattern, "/")