1.7 KiB
1.7 KiB
AwoooP RLS Manual Script Review
Manual scripts are not API runtime, but they can still break after fail-closed
RLS if they connect directly with DATABASE_URL and do not set
app.project_id.
Run:
python3 scripts/ops/awooop-rls-manual-script-audit.py --show-pass
Use strict mode for CI or pre-apply review:
python3 scripts/ops/awooop-rls-manual-script-audit.py --strict-review
2026-05-12 Result
AwoooP RLS manual script audit: BLOCKED=0 REVIEW=5 PASS=13
BLOCKED=0 means no hardcoded PostgreSQL URL with inline credentials was found
in the scanned manual scripts.
REVIEW=5 are intentional operator paths:
apps/api/scripts/awooop_phase1_batch1_backfill.py: RLS/project_id bootstrap backfill; use migration/operator role.apps/api/scripts/run_migration.py: DDL migration script; use migration/operator role.scripts/ops/awooop-rls-role-bootstrap.sql: role bootstrap; requires postgres/CREATEROLE operator.scripts/ops/awooop_rls_preflight.py: read-only preflight inside API pod.scripts/sync_dev_db.py: dev schema sync; requiresDEV_DATABASE_URLand must not target production.
The common tenant-data manual scripts now set app.project_id or use
get_db_context().
Operator Rule
- Direct tenant table access must set
app.project_idon every connection, or use an approved migration/operator role with BYPASSRLS. - Direct
asyncpgscripts should use session-level context:
await conn.execute("SELECT set_config('app.project_id', $1, FALSE)", project_id)
- SQLAlchemy runtime/service code should use
get_db_context()orget_db(). - Do not add PostgreSQL URLs with inline credentials to scripts, comments, or examples.