security: WS1 Gate A - CourtListener off, MFA, invitaciones (W1.3, W1.10, W1.11)
From the PR description
W1.5 + W1.6 + W1.7 - Tenancy, RLS total y epochs (Gate A, items 5-7)
W1.5 - Tenancy foundations
- 6 tablas nuevas (organizations, organization_memberships, workspaces, workspace_memberships, matters, matter_memberships) con roles (org_owner, workspace_admin, matter_owner, editor, viewer, technical_operator), 4 funciones SECURITY DEFINER (search_path fijado), 15 policies RLS, grants solo a authenticated.
- Matriz RLS verificada en PostgreSQL 16 real: miembro ve su org/asunto; viewer bloqueado al insertar; ajeno aislado (0 filas); owner gestiona. Fix post-review: execute de helpers solo a authenticated (anon = 0 grants).
W1.6 - RLS en todas las tablas + invariantes de CI
- 18 tablas del OSS ahora con RLS (migración 04 con guards to_regclass + schema.sql).
backend/scripts/check-security-invariants.shen CI: toda tabla pública debe tener RLS (excepción courtlistener_*) y ningún router usa service_role sin requireAuth. Verificado en PG16: 0 tablas sin relrowsecurity.
W1.7 - Authorization epochs
organizations.authorization_epoch(default 0) + RPC atómicobump_authorization_epoch(security definer, service_role only).backend/src/lib/tenancy.ts: revocación = delete membresía + bump +admin.signOut;assertEpochFreshaborta jobs con snapshot obsoleto. TDD (4 tests), verificado en PG16 (0→1→2 monotónico). Suite backend 517/15.
Evidencia
- Scripts ad-hoc conservados:
hermes-verify-w15.sh(matriz RLS, 15/15),hermes-verify-w16.sh(9/9),hermes-verify-w17.sh(epoch monotónico + 517/15). - Reviews independientes: consistencia schema≡migraciones PASS por hash, overreach PASS, seguridad RLS con hallazgo aplicado (anon grants), security W1.7 en curso.
W1.8 - Eliminado el compartir por correo sin aceptación (Gate A, item 8)
shared_with(projects, tabular_reviews),workflow_sharesy la UI de People eliminados; el acceso queda owner-only hasta que tenancy (W1.5) + invitación nominal (W1.11) tomen la colaboración.- Migración
20260813_06(verificada en PG16: residuales 0|0|0|0) + schema.sql (columnas, índice, tabla y ramas shared de las RPCs overview). - Backend -417 líneas (access.ts, projects/tabular/workflows/downloads/documents/chat/projectChat, userDataExport/Cleanup, contextBuilders). Frontend: PeopleModal fuera, tipos/API de sharedWith fuera.
- Fix de lint del orquestador en
InvitePasswordSetup(effect sin setState síncrono - regla nueva de react-hooks). - Suites: backend 510/15, frontend 237/237, tsc PASS, lint 0.
W1.9 - Descargas autenticadas + cuarentena de upload (Gate A, item 9)
- Descargas ya eran autenticadas (GET /download/:token con requireAuth + ensureDocAccess + fetch server-side; cero signed URLs en backend y frontend).
- Nuevo
fileValidation.ts: magic bytes reales (PDF %PDF-, OOXML PK\x03\x04, OLE2 D0CF11E0) - upload con contenido que no coincide con su extensión recibe 415 antes de crear fila u objeto (bloquea ejecutables disfrazados). - Limpieza de parciales: si el procesamiento falla, se borran el objeto subido y su PDF (deleteFile) - nada queda huérfano.
- TDD: 11 tests nuevos; suite backend 521/15.
W1.13 + W1.14 - Auditoría insert-only y export diario cifrado (Gate A, items 13-14)
W1.13
audit_events(identity pk, actor, org, tipo, detalle) con trigger que aborta UPDATE/DELETE para todo rol (incluido service_role) - verificado en PG16: INSERT ok, UPDATE bloqueado, DELETE bloqueado, fila preservada.recordAuditEvent(): único path de escritura, nunca rompe la operación. Eventos: user.invited, membership.revoked, document.uploaded/deleted/downloaded. RLS + grants solo service_role.
W1.14
- Job
npm run export:audit(cron 03:00 UTC): eventos de ayer → JSONL cifrado AES-256-GCM con rotación diaria determinista (HMAC(master, fecha)) →audit-exports/daily/<fecha>.jsonl.enc(magic AUDIT1) → purga >30 días. uploadFileToBucket/deleteFileFromBucketscoped al bucket de exports. 6 tests (derivación/rotación, round-trip, rechazo de clave de otro día, retención). Suite backend 527/16.
W1.4 - NO VIABLE en este código
No existe superficie de "contactos institucionales" en el fork (el OSS nunca la tuvo; vive en la especificación como parte del Command Center, WS7). No hay nada que depurar en el repositorio.
Reviewers
- W1.13: seguridad insert-only (en curso) · W1.14: seguridad del export (en curso)
Our analysis
Land tenancy and security gate controls — read the full analysis →
Think the analysis missed something the PR description covers?
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-2.md from
inside the repo you want the changes in.