20260802_460_security_governance.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. """Add cross-domain security governance control plane."""
  2. from alembic import op
  3. revision = "20260802_460"
  4. down_revision = "20260802_450"
  5. branch_labels = None
  6. depends_on = None
  7. def upgrade() -> None:
  8. op.execute(
  9. """
  10. CREATE TABLE public.security_classification_profiles (
  11. uid UUID PRIMARY KEY,
  12. code VARCHAR(120) NOT NULL UNIQUE,
  13. business_domain_uid UUID NOT NULL,
  14. status VARCHAR(20) NOT NULL,
  15. record JSONB NOT NULL,
  16. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  17. CHECK (jsonb_typeof(record) = 'object')
  18. );
  19. CREATE INDEX idx_security_profile_domain_status
  20. ON public.security_classification_profiles(business_domain_uid, status);
  21. CREATE TABLE public.security_classification_scans (
  22. uid UUID PRIMARY KEY,
  23. profile_uid UUID NOT NULL REFERENCES public.security_classification_profiles(uid),
  24. business_domain_uid UUID NOT NULL,
  25. resource_type VARCHAR(80) NOT NULL,
  26. resource_uid VARCHAR(200) NOT NULL,
  27. record JSONB NOT NULL,
  28. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  29. CHECK (jsonb_typeof(record) = 'object')
  30. );
  31. CREATE INDEX idx_security_scan_domain_created
  32. ON public.security_classification_scans(business_domain_uid, created_at DESC);
  33. CREATE TABLE public.security_classification_findings (
  34. uid UUID PRIMARY KEY,
  35. scan_uid UUID NOT NULL REFERENCES public.security_classification_scans(uid),
  36. status VARCHAR(30) NOT NULL,
  37. current_version INTEGER NOT NULL DEFAULT 1 CHECK (current_version > 0),
  38. record JSONB NOT NULL,
  39. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  40. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  41. CHECK (jsonb_typeof(record) = 'object')
  42. );
  43. CREATE INDEX idx_security_finding_status_created
  44. ON public.security_classification_findings(status, created_at DESC);
  45. CREATE TABLE public.security_access_policies (
  46. uid UUID PRIMARY KEY,
  47. code VARCHAR(120) NOT NULL UNIQUE,
  48. business_domain_uid UUID NOT NULL,
  49. status VARCHAR(20) NOT NULL,
  50. expires_at TIMESTAMPTZ NOT NULL,
  51. review_due_at TIMESTAMPTZ NOT NULL,
  52. record JSONB NOT NULL,
  53. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  54. CHECK (expires_at > review_due_at),
  55. CHECK (jsonb_typeof(record) = 'object')
  56. );
  57. CREATE INDEX idx_security_policy_domain_status
  58. ON public.security_access_policies(business_domain_uid, status, expires_at);
  59. CREATE TABLE public.security_access_decisions (
  60. uid UUID PRIMARY KEY,
  61. user_uid UUID NOT NULL REFERENCES public.users(id),
  62. business_domain_uid UUID NOT NULL,
  63. resource_uid VARCHAR(200) NOT NULL,
  64. decision VARCHAR(20) NOT NULL CHECK (decision IN ('authorized','denied')),
  65. reason_code VARCHAR(80) NOT NULL,
  66. record JSONB NOT NULL,
  67. decided_at TIMESTAMPTZ NOT NULL,
  68. CHECK (jsonb_typeof(record) = 'object')
  69. );
  70. CREATE INDEX idx_security_decision_user_time
  71. ON public.security_access_decisions(user_uid, decided_at DESC);
  72. CREATE INDEX idx_security_decision_denied
  73. ON public.security_access_decisions(decided_at DESC) WHERE decision = 'denied';
  74. CREATE TABLE public.security_egress_requests (
  75. uid UUID PRIMARY KEY,
  76. business_domain_uid UUID NOT NULL,
  77. classification VARCHAR(30) NOT NULL,
  78. status VARCHAR(40) NOT NULL,
  79. approval_task_uid UUID REFERENCES public.governance_tasks(uid),
  80. expires_at TIMESTAMPTZ NOT NULL,
  81. current_version INTEGER NOT NULL DEFAULT 1 CHECK (current_version > 0),
  82. record JSONB NOT NULL,
  83. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  84. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  85. CHECK (jsonb_typeof(record) = 'object')
  86. );
  87. CREATE INDEX idx_security_egress_status_expiry
  88. ON public.security_egress_requests(status, expires_at);
  89. CREATE TABLE public.security_retention_policies (
  90. uid UUID PRIMARY KEY,
  91. code VARCHAR(120) NOT NULL UNIQUE,
  92. evidence_type VARCHAR(60) NOT NULL,
  93. status VARCHAR(20) NOT NULL,
  94. record JSONB NOT NULL,
  95. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  96. CHECK (jsonb_typeof(record) = 'object')
  97. );
  98. CREATE TABLE public.security_siem_sinks (
  99. uid UUID PRIMARY KEY,
  100. name VARCHAR(300) NOT NULL,
  101. status VARCHAR(20) NOT NULL,
  102. record JSONB NOT NULL,
  103. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  104. CHECK (jsonb_typeof(record) = 'object')
  105. );
  106. CREATE TABLE public.security_siem_deliveries (
  107. uid UUID PRIMARY KEY,
  108. sink_uid UUID NOT NULL REFERENCES public.security_siem_sinks(uid),
  109. status VARCHAR(20) NOT NULL,
  110. payload_digest CHAR(64) NOT NULL,
  111. event_count INTEGER NOT NULL CHECK (event_count >= 0),
  112. record JSONB NOT NULL,
  113. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  114. CHECK (jsonb_typeof(record) = 'object')
  115. );
  116. CREATE INDEX idx_security_siem_delivery_created
  117. ON public.security_siem_deliveries(sink_uid, created_at DESC);
  118. CREATE TABLE public.security_sboms (
  119. uid UUID PRIMARY KEY,
  120. artifact_name VARCHAR(300) NOT NULL,
  121. artifact_version VARCHAR(120) NOT NULL,
  122. document_digest CHAR(64) NOT NULL,
  123. record JSONB NOT NULL,
  124. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  125. CHECK (jsonb_typeof(record) = 'object')
  126. );
  127. CREATE INDEX idx_security_sbom_artifact
  128. ON public.security_sboms(artifact_name, created_at DESC);
  129. CREATE TABLE public.security_vulnerabilities (
  130. uid UUID PRIMARY KEY,
  131. sbom_uid UUID NOT NULL REFERENCES public.security_sboms(uid),
  132. scanner VARCHAR(100) NOT NULL,
  133. external_id VARCHAR(120) NOT NULL,
  134. severity VARCHAR(20) NOT NULL CHECK (severity IN ('critical','high','medium','low','unknown')),
  135. status VARCHAR(30) NOT NULL,
  136. assignee_uid UUID REFERENCES public.users(id),
  137. current_version INTEGER NOT NULL DEFAULT 1 CHECK (current_version > 0),
  138. record JSONB NOT NULL,
  139. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  140. updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  141. UNIQUE (sbom_uid, scanner, external_id),
  142. CHECK (jsonb_typeof(record) = 'object')
  143. );
  144. CREATE INDEX idx_security_vulnerability_queue
  145. ON public.security_vulnerabilities(status, severity, updated_at DESC);
  146. CREATE TABLE public.security_governance_events (
  147. uid UUID PRIMARY KEY,
  148. resource_type VARCHAR(80) NOT NULL,
  149. resource_uid VARCHAR(200) NOT NULL,
  150. action VARCHAR(80) NOT NULL,
  151. actor_uid UUID NOT NULL REFERENCES public.users(id),
  152. safe_detail JSONB NOT NULL DEFAULT '{}'::jsonb,
  153. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  154. CHECK (jsonb_typeof(safe_detail) = 'object')
  155. );
  156. CREATE INDEX idx_security_governance_event_time
  157. ON public.security_governance_events(created_at DESC, uid DESC);
  158. CREATE TABLE public.access_control_audit_events (
  159. uid UUID PRIMARY KEY,
  160. action VARCHAR(80) NOT NULL,
  161. actor_uid UUID NOT NULL REFERENCES public.users(id),
  162. resource_type VARCHAR(80) NOT NULL,
  163. resource_uid VARCHAR(200) NOT NULL,
  164. status VARCHAR(30) NOT NULL,
  165. safe_detail JSONB NOT NULL DEFAULT '{}'::jsonb,
  166. created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
  167. CHECK (jsonb_typeof(safe_detail) = 'object')
  168. );
  169. CREATE INDEX idx_access_control_audit_time
  170. ON public.access_control_audit_events(created_at DESC, uid DESC);
  171. """
  172. )
  173. def downgrade() -> None:
  174. raise RuntimeError(
  175. "Forward-only migration: security governance evidence cannot be destructively removed"
  176. )