Hermetic whole-app authorization gate#162
Open
pratikbodkhe wants to merge 1 commit into
Open
Conversation
Adds a deterministic, hermetic authorization gate that executes the REAL middleware, the admin server-layout role guard, and API route handlers headlessly in Node against a fixture Supabase backend (a single global fetch seam, no dev server, no database, no network). The whole-app authz matrix runs in ~0.4s. - Probes every page route (middleware reachability), the admin role guard, and a representative handler per guard family (requireAdmin, requireCurrentOrgAdmin, getUser, public) across six identities (logged-out / member / org-admin / global-admin / cross-tenant / pro), asserting the observed matrix equals a hand-audited default-deny policy.json. - Cross-tenant isolation: the two service-role list handlers are asserted to return only the caller's organization rows; a real-code mutation dropping the org filter is proven to turn the gate red. - Coverage gate: every route.ts / page.tsx on disk must be either executed in the matrix or explicitly deferred with a reason, so a new unguarded route fails the gate instead of slipping through. - Hermeticity is enforced by asserting the unmatched-request log is empty on every probe (the Supabase client swallows thrown fetch into null-data results, so a rejection cannot be relied on); undeclared table reads and any write/RPC throw. - negcontrol.mjs mutates a real guard and a real tenant filter, proves the gate goes red with named violations, then reverts. Wired into CI (pnpm test:authz). The seam is purely the external fetch swap; no test branches are added to src/. Covers server-side authorization only; client rendering, real RLS SQL, and write/RPC business logic remain with the Playwright + real-DB suite.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A deterministic, hermetic authorization gate: it executes minutia's real middleware, the admin server-layout role guard, and API route handlers headlessly in plain Node against a fixture Supabase backend — no dev server, no database, no network. The whole-app authorization matrix (every route × six identities) runs in ~0.4s.
The seam is a single
globalThis.fetchswap; zero test branches are added tosrc/(respects the repo's no-stubs-in-prod rule).What it verifies
(app)/admin/layout.tsx) — only a global admin renders it; everyone else redirects.requireAdmin,requireCurrentOrgAdmin,getUser→401, public) via executed representatives, over six identities: logged-out / member / org-admin / global-admin / cross-tenant / pro.workspace/members,admin/invitations) are asserted to return only the caller's organization's rows.policy.json(an explicit expected-outcome per node × identity).Why it's trustworthy (it can't silently lie)
route.ts/page.tsxon disk must be either executed in the matrix or explicitly deferred with a reason — a new unguarded route fails the gate instead of slipping through.negcontrol.mjs): mutating a real guard (neuter the admin role check) and a real tenant filter (drop.eq("organization_id", …)) both turn the gate RED with named violations (reachability-leak,tenant-leak), then revert. A gate that never goes red is a lie; this proves it does.checkPolicyisn't vacuous.Scope (honest boundary)
Covered: server-side authorization (middleware, server-component guards, handler authz status, cross-tenant isolation). Not covered here (stays with the Playwright + real-DB E2E suite): client-component rendering, real RLS SQL enforcement (the fixture applies URL filters literally, no RLS emulation), and write/PUT/PATCH/DELETE + RPC business logic.
Run
pnpm test:authz→ 23/23, deterministic (flake-stable 3×). Wired into the CIlint-and-typecheckjob.node scripts/authz/negcontrol.mjs→ both real-code controls go red then revert.Emits an AKG-shaped artifact (shared graph schema with the RN adapter) under
scripts/authz/artifacts/(gitignored, regenerated per run).