Two-user privacy test for the database, and run the SQL behaviour tests in CI
User description
Requested by Josh · project thread
Summary
Before: nothing signed in as two different users and checked the database's privacy rules by actually querying. The four SQL behaviour tests in tests/sql/ were never run by CI, and one of them (enrichment-repair.sql) had gone stale and failed.
After: tests/sql/two-user-privacy.sql signs in as two synthetic doctors and proves, by querying, that:
- The browser roles (
anon,authenticated) hold no privilege on anypublictable, view or sequence, and every read, insert and delete each role attempts on every table is refused. Work mode data (roster, on call, CPD, teaching, admin) is reachable only through server routes. - Only five reviewed functions are callable by a signed-in user, none by
anon, and the four administrator ones refuse an ordinary doctor. - Every owner policy written for the browser roles (24 tables plus storage objects) isolates the two doctors when a grant is simulated: each sees only their own rows, cannot change or delete the other's, and cannot move a row into the other's name. A new browser-facing policy without coverage fails the test.
All five tests/sql files now run in the Migration replay CI job against the full migration chain, and locally with npm run test:sql (bare-image replay of schema.sql, Docker only, no provider). tests/sql/** now selects the database CI scope.
No migration and no live database change. No policy or grant changed; no gap was found that needed one.
Areas touched: Data platform and access (test, runner), Delivery (CI step, scope pattern, npm script), Source intake (fixture repair only).
Verification
npm run test:sql:All 5 SQL behaviour tests passed.includingTWO_USER_PRIVACY_SQL_PASS.- Same CI loop run locally against a Supabase CLI 2.108.0 emulator after
supabase migration up --local(309 migrations): all five files printed their pass line. - Planted-leak checks (each run then rolled back): granting a Work table to
authenticated, granting a function toanonorauthenticated, opening the documents, chunks or storage read policy, and adding an uncovered policy were each caught with aPRIVACY LEAKor coverage error. npm run test:Test Files 1882 passed (1882),Tests 29901 passed | 5 skipped.typecheck,check:migration-role,check:gate-manifest,check:function-grants,check:owner-scope,check:diff-integrity,check:verification-planpassed; eslint and prettier clean on changed files.- The new CI step only runs on non-draft PRs, so its first hosted run happens when this is marked ready.
Risk and rollout
- Risk: CI only. If the emulator differs from the local replay the new step fails red rather than passing silently.
- Rollback: revert the commit.
- Provider or production effects: None.
Notes
- The real line between doctors is the server routes, which use the service-role key with app-side owner filters. That layer is covered by
check:owner-scopeand the nightly staging cross-tenant harness, not by this test. src/lib/supabase/admin.tsstill lacks aserver-onlyguard. Not added here because the ingestion worker and scripts import it in plain Node, whereserver-onlythrows; it needs its own small change.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KhogQUCHgdtFBHFtpjnd1n
Generated by Claude Code
Summary by cubic
Adds a two-user database privacy test that signs in as two synthetic doctors and proves the privacy rules by querying, and starts running the SQL behaviour tests in CI.
Previously the four SQL behaviour tests in tests/sql/ never ran in CI, and enrichment-repair.sql had gone stale and failed. Now all five run in the Migration replay CI job and locally via npm run test:sql (a disposable Docker replay of schema.sql, never the live database), and tests/sql/** selects the database CI scope. The test proves browser roles hold no privilege on any public relation, that only five reviewed functions are callable by a signed-in user (four administrator ones refuse an ordinary doctor), and that every owner policy, including PUBLIC (no TO clause) ones, isolates the two doctors when a grant is simulated. A new browser-facing policy without coverage fails the test.
Hardening
- Repairs
enrichment-repair.sqlfixtures for the newer lease-tuple checks, and makesroster-behaviour.sqlroll back its fixtures even on a directpsql -frun. - Cross-owner inserts are probed on every covered table by cloning the actor's own row and pointing it at the other doctor; storage inserts are probed once per bucket, so a permissive policy scoped to one bucket is caught.
- Function signatures are checked exactly so a new overload is caught, and the administrator-refusal helper cannot mask a leak.
- Records a known gap: three policies check only
owner_id, not parent document ownership, so the probe skips those; the test fails if the gap list drifts in either direction.
No migration or live database change; no policy or grant changed.
CodeAnt-AI Description
Run two-user database privacy checks in CI and locally
What Changed
- CI now runs every SQL behavior test against the replayed migration chain.
- A new two-user test checks that browser roles cannot access public tables directly, administrator functions reject ordinary doctors, and owner policies keep each doctor’s rows private.
- A local command runs the SQL tests against a disposable database using synthetic data; it does not contact the live database.
- Updated repair-test fixtures to include the lease details required by current database rules.
Impact
✅ Cross-user privacy regressions caught in CI
✅ SQL behavior failures block migration replay checks
✅ Local database checks avoid live data
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
@codeant-ai ask: Your question here
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
@codeant-ai ask: Can you suggest a safer alternative to storing this secret?
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
@codeant-ai: Your feedback here
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
@codeant-ai: Do not flag unused imports.
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
@codeant-ai: review
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.
Summary by CodeRabbit
- Tests
- Added automated database behavior checks that run against a locally prepared database and report failures.
- Added privacy checks covering access permissions and whether users can view or change one another’s records.
- Database test changes are now included in change-scope detection, and the checks run during migration replay in CI.
- Updated repair test data to include lease details required for processing rows.