Files
breakpilot-compliance/backend-compliance/migrations/156_control_suppressions.sql
T
Benjamin Admin 7aabfbe5b5 feat(controls): Mandanten-Suppression — per-tenant Applicability-Override
Geteilte Schicht für alle Surfaces (Workspace-Anwälte, Cyber-Risiko-Projekt,
Admin): ein Mandant markiert ein Control als "nicht anwendbar" → in seinen
Use-Case-Ansichten (und künftig Repo-Scans) ausgeblendet.

- Migration 156: compliance.control_suppressions (PK tenant_id+control_uuid),
  reversibel (active + reverted_*), auditierbar (actor/reason/created_at).
  [migration-approved]
- Service control_suppression: suppress/revert/list_suppressions +
  suppressed_control_uuids (geteilter Filter).
- Routes: GET/POST /v1/controls/suppressions + POST .../{uuid}/revert (X-Tenant-ID).
- controls_for_use_case: optionaler X-Tenant-ID + include_suppressed; suppressed
  per Default versteckt (nie gelöscht), suppressed_count, suppressed-Flag pro
  Control. Agenten/CRA ohne Tenant unberührt.
- Tests: Request-Validierung + import-safety (E2E-Zyklus gegen macmini bewiesen).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:35:38 +02:00

36 lines
1.4 KiB
SQL

-- Migration 156: control_suppressions — per-tenant Applicability-Override.
-- Ein Mandant markiert ein Control als "unbrauchbar / nicht anwendbar" -> es
-- wird in seinen Use-Case-Ansichten (und künftig Repo-Scans) ausgeblendet.
-- REVERSIBEL (active=false + reverted_*, Zeile bleibt) und AUDIT-tauglich
-- (actor + reason + created_at beantworten "warum nicht geprüft?"). PER-TENANT.
-- Composite PK (tenant_id, control_uuid) = genau eine aktuelle Zeile je
-- Mandant+Control; Re-Suppress reaktiviert. Additiv, idempotent. [migration-approved]
SET search_path TO compliance, public;
DO $$
BEGIN
IF EXISTS (SELECT 1 FROM information_schema.tables
WHERE table_schema = 'compliance'
AND table_name = 'canonical_controls') THEN
CREATE TABLE IF NOT EXISTS control_suppressions (
tenant_id UUID NOT NULL,
control_uuid UUID NOT NULL
REFERENCES canonical_controls(id) ON DELETE CASCADE,
reason TEXT,
actor VARCHAR(120),
active BOOLEAN NOT NULL DEFAULT TRUE,
reverted_at TIMESTAMPTZ,
reverted_by VARCHAR(120),
revert_reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (tenant_id, control_uuid)
);
CREATE INDEX IF NOT EXISTS idx_ctrl_suppr_tenant_active
ON control_suppressions(tenant_id, active);
END IF;
END $$;