6.7 KiB
6.7 KiB
Phase 0 — Frozen Error List (P0/P1)
Date: 2026-07-31
Baseline commit: 11d6faa (tag: v-phase0-baseline)
Phase 0 commit: 032a7e8
P0 — Critical Security Issues
P0-01: All tables owned by SUPERUSER role
- Severity: P0
- Files: All 123 tables in
publicschema - Tables: ALL
- Reproduction:
SELECT tableowner FROM pg_tables WHERE schemaname='public'→ allcrm_user - Target: Owner =
crm_migration(NOSUPERUSER, NOBYPASSRLS) - Status: Open — Phase 1
P0-02: crm_migration has BYPASSRLS
- Severity: P0
- Files: DB role
crm_migration - Reproduction:
SELECT rolbypassrls FROM pg_roles WHERE rolname='crm_migration'→true - Target:
ALTER ROLE crm_migration NOBYPASSRLS - Status: Open — Phase 1
P0-03: RLS disabled on ~70+ tenant tables
- Severity: P0
- Tables: contacts, addresses, attachments, ai_, calendar_, comm_, mail_, workflows, etc.
- Reproduction:
SELECT relname FROM pg_class WHERE relrowsecurity=false AND relforcerowsecurity=true - Target: ENABLE ROW LEVEL SECURITY on all tenant tables
- Status: Open — Phase 1
P0-04: Old RLS policies scoped to {public} — potential cross-transaction leak
- Severity: P0
- Tables: ~70+ tables with old
tenant_isolationpolicy - Reproduction:
SELECT policyname, roles FROM pg_policies WHERE roles='{public}' - Target: Drop old policies, create new ones scoped to
{crm_api, crm_worker} - Status: Open — Phase 1
P0-05: No separate database connections for auth/api/worker/migration
- Severity: P0
- Files:
app/config.py,app/core/db/__init__.py - Reproduction:
grep -n 'auth_database_url\|worker_database_url' app/config.py→ not found - Target: 4 separate engines with separate pools and roles
- Status: Open — Phase 1
P0-06: Worker uses crm_api role instead of crm_worker
- Severity: P0
- Files:
docker-compose.ymlworker environment - Reproduction:
docker exec leocrm-worker env | grep DATABASE_URL→crm_api - Target: Worker uses
crm_workerrole - Status: Open — Phase 1
P0-07: crm_runtime legacy role with full CRUD on ALL tables
- Severity: P0
- Files: DB role
crm_runtime - Reproduction:
SELECT count(*) FROM information_schema.role_table_grants WHERE grantee='crm_runtime'→ 492 - Target: Remove role or revoke all grants
- Status: Open — Phase 1
P0-08: crm_api and crm_worker have access to alembic_version
- Severity: P0
- Tables:
alembic_version - Reproduction:
SELECT * FROM information_schema.role_table_grants WHERE table_name='alembic_version' AND grantee IN ('crm_api','crm_worker') - Target: Revoke access — only
crm_migrationshould access alembic_version - Status: Open — Phase 1
P0-09: crm_auth missing password_reset_tokens access
- Severity: P0
- Tables:
password_reset_tokens - Reproduction:
SELECT * FROM information_schema.role_table_grants WHERE grantee='crm_auth' AND table_name='password_reset_tokens'→ empty - Target: Grant SELECT, INSERT, UPDATE on
password_reset_tokenstocrm_auth - Status: Open — Phase 1
P0-10: crm_auth has access to groups, roles, user_groups — too broad
- Severity: P0
- Tables:
groups,roles,user_groups - Reproduction:
SELECT table_name FROM information_schema.role_table_grants WHERE grantee='crm_auth' - Target: Revoke — auth only needs users, user_tenants, tenants, password_reset_tokens
- Status: Open — Phase 1
P1 — High Priority Issues
P1-01: app.tenant_id legacy variable still set
- Severity: P1
- Files:
app/core/db/__init__.py:128(now fixed) - Reproduction:
grep -rn 'app.tenant_id' app/ --include='*.py'(was setting both vars) - Target: Only
app.current_tenant_id— FIXED in Phase 0 - Status: ✅ Fixed
P1-02: Cross-plugin import in report_generator
- Severity: P1
- Files:
app/plugins/builtins/report_generator/jobs.py:79 - Reproduction:
grep 'from app.plugins.builtins.dms' app/plugins/builtins/report_generator/jobs.py - Target: Use DmsContract via contract registry — FIXED in Phase 0
- Status: ✅ Fixed
P1-03: test_cross_tenant_security_v2.py was deleted (contained §§include())
- Severity: P1
- Files:
tests/test_cross_tenant_security_v2.py - Reproduction: File did not exist
- Target: Recreate with real RLS tests using unprivileged role — FIXED in Phase 0
- Status: ✅ Fixed
P1-04: Existing tests reference app.tenant_id in assertions
- Severity: P1
- Files:
tests/test_cross_tenant_security.py,tests/test_cross_tenant_standalone.py - Reproduction:
grep 'app.tenant_id' tests/test_cross_tenant*.py - Target: Only test
app.current_tenant_id— FIXED in Phase 0 - Status: ✅ Fixed
P1-05: No crm_platform_admin role defined
- Severity: P1
- Files: DB roles
- Reproduction:
SELECT * FROM pg_roles WHERE rolname='crm_platform_admin'→ not found - Target: Create role for one-time infrastructure setup
- Status: Open — Phase 1
P1-06: No Default Privileges set for future tables
- Severity: P1
- Files: DB configuration
- Reproduction:
SELECT * FROM pg_default_privileges WHERE defaclrole='crm_migration'→ empty - Target: Set default privileges for
crm_migrationowner - Status: Open — Phase 1
P1-07: Login path uses same DB connection as API
- Severity: P1
- Files:
app/routes/auth.py,app/core/db/__init__.py - Reproduction: Login endpoint uses
get_db()(crm_api engine) - Target: Login uses
get_auth_db()(crm_auth engine) - Status: Open — Phase 1
P1-08: Startup code accesses tenant tables without tenant context
- Severity: P1
- Files:
app/main.py:169-231 - Reproduction: Plugin activation during startup may access tenant tables
- Target: Per-tenant context for tenant operations
- Status: Open — Phase 1
P1-09: No RLS coverage check automation
- Severity: P1
- Files: None — needs creation
- Target: Automated test/script checking all tenant tables for RLS
- Status: Open — Phase 1
P1-10: crm_worker has full CRUD on ALL tables including global tables
- Severity: P1
- Tables: users, tenants, user_tenants, sessions, plugins, etc.
- Reproduction:
SELECT count(*) FROM information_schema.role_table_grants WHERE grantee='crm_worker'→ 492 - Target: Narrow to only necessary job/outbox/tenant tables
- Status: Open — Phase 1
Summary
| Status | Count |
|---|---|
| Open (P0) | 10 |
| Open (P1) | 7 |
| Fixed (P1) | 4 |
| Total | 21 |