test(handler): un-skip the profile existence-hiding invariant

TestInvariant_ProfileAdminEndpointsHideFromNonAdmins was skipped pending the
ServeProfile dispatcher refactor — which has since landed (ServeProfile in
profilehandler.go is the entry point, with an adminOnly wrapper that denies
with 404). Implement the test against it: non-admin, anonymous, and
un-elevated-admin callers must get 404 (never 403/200) on every admin-gated
sub-resource (/whoami, /config, /logs, /effective-policy, /reindex), so the
namespace can't be enumerated; an elevated admin gets through (/whoami,
/config positive control). Locks in the existence-hiding security property
that was previously unverified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-21 16:41:29 -05:00
parent 4f021d8abc
commit d0d8423ac6

View file

@ -459,8 +459,53 @@ func TestInvariant_UnelevatedAdminNoSilentBypass(t *testing.T) {
func TestInvariant_ProfileAdminEndpointsHideFromNonAdmins(t *testing.T) {
// These checks lock in the existence-hiding property: non-admins must
// see 404, never 403, so they can't probe which paths exist.
t.Skip("requires the profile handler dispatcher entry point; skip until the refactor confirms ServeProfile signature")
// see 404, never 403, so they can't probe which admin-only resources
// exist. ServeProfile is the dispatcher (the refactor this test waited
// on); its adminOnly wrapper denies with 404 before the sub-handler
// runs, so a nil ring/index is safe for the non-admin paths.
cfg, _ := invariantsFixture(t)
adminEndpoints := []string{"/whoami", "/config", "/logs", "/effective-policy", "/reindex"}
profileGet := func(sub, email string, elevated bool) *httptest.ResponseRecorder {
req := httptest.NewRequest(http.MethodGet, ProfilePathPrefix+sub, nil)
ctx := context.WithValue(req.Context(), EmailKey, email)
ctx = context.WithValue(ctx, ElevatedKey, elevated)
req = req.WithContext(ctx)
rec := httptest.NewRecorder()
ServeProfile(cfg, nil, nil, rec, req)
return rec
}
// Non-admin (eve, project_team only) and anonymous callers must get
// 404 on every admin endpoint — never 403, never 200.
for _, who := range []struct {
email string
elevated bool
label string
}{
{"eve@example.com", false, "non-admin"},
{"", false, "anonymous"},
{"admin@example.com", false, "un-elevated admin"}, // sudo-style: no authority until elevated
} {
for _, sub := range adminEndpoints {
rec := profileGet(sub, who.email, who.elevated)
if rec.Code != http.StatusNotFound {
t.Errorf("%s GET /.profile%s = %d, want 404 (existence-hiding)", who.label, sub, rec.Code)
}
}
}
// Positive control: an elevated root admin must NOT get 404 on the
// gated routes that need no ring/index — proving the 404s above are
// the admin gate, not a missing route. (/whoami and /config don't
// touch the log ring or archive index.)
for _, sub := range []string{"/whoami", "/config"} {
rec := profileGet(sub, "admin@example.com", true)
if rec.Code == http.StatusNotFound {
t.Errorf("elevated admin GET /.profile%s = 404; the gate should admit admins", sub)
}
}
}
// dump prints the rec body when t.Logf would help debugging — used in