From 1b95709d4bddc7f6416821ed519acf9fc50bf2c1 Mon Sep 17 00:00:00 2001 From: shankar0123 Date: Sat, 16 May 2026 20:36:44 +0000 Subject: [PATCH] =?UTF-8?q?docs(rbac):=20DOC-002=20+=20COMP-005=20?= =?UTF-8?q?=E2=80=94=20pin=20auditor=20role=20invariants=20in=20operator?= =?UTF-8?q?=20docs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Acquisition-audit DOC-002 + COMP-005 closure (Sprint 7 ACQ, 2026-05-16). Both findings were UNKNOWN because the auditor couldn't independently verify the auditor-role permission set is locked-down. The set IS locked down in three places (schema, code, tests) — DOC-002 + COMP-005 close by surfacing that pin in docs/operator/rbac.md so a future SOC 2 / FedRAMP / PCI auditor can re-derive the proof without rebuilding the trail. New "Auditor role invariants" subsection in docs/operator/rbac.md under the existing two-person integrity section. Documents: Layer 1 (schema) — migrations/000029_rbac.up.sql:261-262 + migrations/000039_audit_crit1_perms.up.sql:111 (the inline "r-auditor: NOTHING new" comment). Layer 2 (code) — internal/domain/auth/DefaultRoles[RoleIDAuditor]. Layer 3 (the load-bearing one — tests): - TestAuditorRoleHoldsExactlyAuditReadAndExport set-equality on {audit.read, audit.export} - TestAuditorRoleDoesNotHoldMutatingOrReadingNonAuditPerms catches subtle widening even if set-equality is bypassed - TestAuditorRoleSeparateFromViewer pins auditor and viewer permission sets are disjoint except audit.read (which viewer shares by design) Explicitly notes the audit prompt's recommendation against a bash CI guard — the property is already enforced at the Go test layer with stronger semantics (struct-aware set equality) than `grep` could provide. No code changes; documentation-only closure (existing tests + schema already pin the invariant). Verified locally: gofmt clean, go vet clean across internal/domain/auth + internal/service. --- docs/operator/rbac.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/operator/rbac.md b/docs/operator/rbac.md index 7789904..2961181 100644 --- a/docs/operator/rbac.md +++ b/docs/operator/rbac.md @@ -68,6 +68,45 @@ giving them the keys to the kingdom. The `internal/domain/auth/auditor_test.go` invariants pin this set going forward. +### Auditor role invariants (DOC-002 / COMP-005 closure) + +Acquisition-audit DOC-002 + COMP-005 closure (Sprint 7 ACQ, 2026-05-16). +The auditor role's permission set is **pinned at exactly two +permissions** — `audit.read` and `audit.export` — and any drift breaks +the SOC 2 / FedRAMP / PCI separation. The pin is enforced at three +layers and the load-bearing layer is the unit-test set, not a bash CI +guard: + +1. **Schema layer** — `migrations/000029_rbac.up.sql:261-262` seeds + exactly two `role_permissions` rows for `r-auditor` + (`r-auditor / p-audit-read / global / NULL` and + `r-auditor / p-audit-export / global / NULL`). + `migrations/000039_audit_crit1_perms.up.sql:111` adds an inline + comment confirming `r-auditor` was NOT widened by the migration that + shipped the five admin-only fine-grained perms. +2. **Code layer** — `internal/domain/auth/DefaultRoles[RoleIDAuditor]` + matches the schema. A future code change that adds a non-audit + permission to the slice is caught by: +3. **Test layer** (the load-bearing one) — + `internal/domain/auth/auditor_test.go` ships three pinning tests: + - `TestAuditorRoleHoldsExactlyAuditReadAndExport` — set-equality + comparison; fails on any add or remove + - `TestAuditorRoleDoesNotHoldMutatingOrReadingNonAuditPerms` — + enumerates the slice and rejects any permission outside the + `{audit.read, audit.export}` set; catches subtle widening even if + the set-equality test is bypassed + - `TestAuditorRoleSeparateFromViewer` — pins that the auditor and + viewer permission sets are disjoint except for `audit.read` (which + viewer shares by design); catches the "auditor inherits viewer + reads" leg + +A bash CI guard was deliberately **not** added — the property is +already enforced at the Go test layer with stronger semantics +(struct-aware set equality) than `grep` could provide. If a future +contributor proposes widening `r-auditor`, the three tests above +fail at `go test ./internal/domain/auth/...` BEFORE the change can +land in a merge. + The five **admin-only fine-grained perms** seeded by migration 000030 gate the high-blast-radius endpoints: