Files
leocrm/docs/phase0_error_list.md
T

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 public schema
  • Tables: ALL
  • Reproduction: SELECT tableowner FROM pg_tables WHERE schemaname='public' → all crm_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_isolation policy
  • 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.yml worker environment
  • Reproduction: docker exec leocrm-worker env | grep DATABASE_URLcrm_api
  • Target: Worker uses crm_worker role
  • 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_migration should 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_tokens to crm_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_migration owner
  • 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