"""Canonical GRC catalog — frameworks, requirements, controls, crosswalk. This module is the SINGLE SOURCE OF TRUTH for the compliance spine's seed data. Both the alembic seed (migration 250) and the runtime reconcile (``sync_compliance_catalog`` in ``compliance_service``) read from here, so the two never drift. Pure data — imports ONLY stdlib so a migration can import it safely. Design notes ------------ * A **control** is computed from a backing signal. The 8 controls below map 1:1 to the existing detective scheduled jobs (see ``router.CONTROL_HANDLER_KEYS``). Access-certification and SoD controls are seeded by their own PRs once their engines exist — until then the requirements they'd satisfy honestly show as ``not_covered``. * **Framework requirements** are the IT-relevant subset of each standard that Helix can actually evidence (access control, identity & auth, cryptography/key management, logging & monitoring, vulnerability / exposure management). We do NOT enumerate clauses Helix has no signal for (physical security, HR policy, BCP) — claiming coverage we can't back would be the opposite of audit-ready. PCI DSS + 等保 catalogs land in a follow-up PR via the same crosswalk model (data only, no code). """ from __future__ import annotations from typing import Any # ── Control categories (for grouping the catalog page) ────────────── CATEGORY_IDENTITY = "identity" CATEGORY_ACCESS = "access" CATEGORY_POSTURE = "posture" CATEGORY_CRYPTO = "crypto" CATEGORY_LOGGING = "logging" CATEGORY_DATA = "data_protection" CATEGORY_VULN = "vuln_mgmt" # ── Control statuses ──────────────────────────────────────────────── STATUS_PASS = "pass" STATUS_FAIL = "fail" STATUS_STALE = "stale" STATUS_NOT_CONFIGURED = "not_configured" STATUS_UNKNOWN = "unknown" # ── Requirement coverage states (computed, not stored) ────────────── COVERAGE_PASS = "covered_pass" COVERAGE_PARTIAL = "covered_partial" COVERAGE_FAIL = "covered_fail" COVERAGE_NONE = "not_covered" # ════════════════════════════════════════════════════════════════════ # Frameworks # ════════════════════════════════════════════════════════════════════ FRAMEWORKS: list[dict[str, Any]] = [ { "key": "soc2", "name": "SOC 2", "version": "2017 TSC (rev. 2022)", "description": ( "AICPA Trust Services Criteria. Helix evidences the Common " "Criteria (CC) clauses related to logical access and system " "operations." ), "sort_order": 10, }, { "key": "iso27001", "name": "ISO/IEC 27001", "version": "2022", "description": ( "Information security management. Helix evidences the Annex A " "organizational (A.5) and technological (A.8) controls within " "its governance scope." ), "sort_order": 20, }, ] # ════════════════════════════════════════════════════════════════════ # Requirements (IT-relevant subset per framework) # ════════════════════════════════════════════════════════════════════ _SOC2_CC2 = "Common Criteria — Communication & Information (CC2)" _SOC2_CC6 = "Common Criteria — Logical & Physical Access (CC6)" _SOC2_CC7 = "Common Criteria — System Operations (CC7)" _ISO_A5 = "Annex A.5 — Organizational controls" _ISO_A6 = "Annex A.6 — People controls" _ISO_A8 = "Annex A.8 — Technological controls" REQUIREMENTS: dict[str, list[dict[str, Any]]] = { "soc2": [ { # Awareness / internal communication. Added 2026-09-03 with # the security-awareness control: the requirement existed in # the standard all along, but nothing in Helix could evidence # it, so it was honestly absent rather than listed as # ``not_covered`` against a control that did not exist. "ref": "CC2.2", "title": "Internal communication of information", "description": ( "The entity internally communicates information, including " "objectives and responsibilities for internal control, " "necessary to support the functioning of internal control — " "including security awareness and expectations of personnel." ), "category": _SOC2_CC2, "sort_order": 5, }, { "ref": "CC6.1", "title": ( "Logical access security software, infrastructure, and " "architectures" ), "description": ( "The entity implements logical access security measures to " "protect against threats from sources outside its system " "boundaries (authentication, least privilege, encryption of " "secrets)." ), "category": _SOC2_CC6, "sort_order": 10, }, { "ref": "CC6.2", "title": "User registration and authorization", "description": ( "Prior to issuing system credentials, the entity registers " "and authorizes new internal and external users." ), "category": _SOC2_CC6, "sort_order": 20, }, { "ref": "CC6.3", "title": "Role-based access — grant, modify, remove", "description": ( "The entity authorizes, modifies, or removes access to data " "and system components based on roles and least privilege, " "and reviews access periodically." ), "category": _SOC2_CC6, "sort_order": 30, }, { "ref": "CC6.6", "title": "Protection against threats from outside the boundary", "description": ( "The entity implements controls to prevent or detect " "unauthorized access from outside its system boundaries " "(public exposure of resources)." ), "category": _SOC2_CC6, "sort_order": 40, }, { "ref": "CC6.7", "title": "Restrict transmission, movement, and removal of data", "description": ( "The entity restricts the transmission, movement, and " "removal of information and protects it using encryption and " "key/secret lifecycle management." ), "category": _SOC2_CC6, "sort_order": 50, }, { "ref": "CC7.1", "title": "Detection of vulnerabilities and configuration changes", "description": ( "The entity uses detection and monitoring to identify " "vulnerabilities and changes to configurations that could " "introduce new risks." ), "category": _SOC2_CC7, "sort_order": 60, }, { "ref": "CC7.2", "title": "Monitoring of anomalies and security events", "description": ( "The entity monitors system components for anomalies " "indicative of malicious acts, natural disasters, and errors." ), "category": _SOC2_CC7, "sort_order": 70, }, ], "iso27001": [ { # 安全意识、教育与培训. Added 2026-09-03 alongside the # security-awareness control — see the note on CC2.2. "ref": "A.6.3", "title": ( "Information security awareness, education and training" ), "description": ( "Personnel of the organization shall receive appropriate " "information security awareness, education and training and " "regular updates of the organization's policies and " "procedures, as relevant for their job function." ), "category": _ISO_A6, "sort_order": 5, }, { "ref": "A.5.15", "title": "Access control", "description": ( "Rules to control physical and logical access to " "information and assets shall be established and reviewed " "based on business and security requirements." ), "category": _ISO_A5, "sort_order": 10, }, { "ref": "A.5.17", "title": "Authentication information", "description": ( "Allocation and management of authentication information " "(secrets, keys, tokens) shall be controlled." ), "category": _ISO_A5, "sort_order": 20, }, { "ref": "A.5.18", "title": "Access rights", "description": ( "Access rights shall be provisioned, reviewed, modified, and " "removed in accordance with the access control policy." ), "category": _ISO_A5, "sort_order": 30, }, { "ref": "A.8.2", "title": "Privileged access rights", "description": ( "The allocation and use of privileged access rights shall " "be restricted and managed." ), "category": _ISO_A8, "sort_order": 40, }, { "ref": "A.8.5", "title": "Secure authentication", "description": ( "Secure authentication technologies and procedures (e.g. " "multi-factor authentication) shall be implemented." ), "category": _ISO_A8, "sort_order": 50, }, { "ref": "A.8.8", "title": "Management of technical vulnerabilities", "description": ( "Information about technical vulnerabilities and exposure of " "systems shall be obtained and appropriate measures taken." ), "category": _ISO_A8, "sort_order": 60, }, { "ref": "A.8.15", "title": "Logging", "description": ( "Logs recording activities, exceptions, faults, and other " "relevant events shall be produced, stored, and protected." ), "category": _ISO_A8, "sort_order": 70, }, { "ref": "A.8.16", "title": "Monitoring activities", "description": ( "Networks, systems, and applications shall be monitored for " "anomalous behaviour and appropriate actions taken to " "evaluate potential security incidents." ), "category": _ISO_A8, "sort_order": 80, }, { "ref": "A.8.24", "title": "Use of cryptography", "description": ( "Rules for the effective use of cryptography, including key " "and certificate management, shall be defined and " "implemented." ), "category": _ISO_A8, "sort_order": 90, }, ], } # ════════════════════════════════════════════════════════════════════ # Controls (1:1 with the existing detective scheduled jobs) # ════════════════════════════════════════════════════════════════════ CONTROLS: list[dict[str, Any]] = [ { "key": "mfa_coverage", "name": "MFA enrollment coverage", "category": CATEGORY_IDENTITY, "description": ( "Every workforce account on every identity provider has " "multi-factor authentication enrolled." ), "signal_type": "scheduled_job", "signal_ref": "auditor.mfa_sync_daily", "config": {"min_coverage_pct": 95}, "sort_order": 10, }, { "key": "privileged_access_review", "name": "Privileged access review", "category": CATEGORY_ACCESS, "description": ( "Holders of privileged roles are reviewed on a recurring cycle " "and unneeded access is revoked." ), "signal_type": "scheduled_job", "signal_ref": "auditor.privileged_review_auto_open_quarterly", "config": {}, "sort_order": 20, }, { "key": "pat_audit", "name": "Personal access token audit", "category": CATEGORY_ACCESS, "description": ( "Long-lived personal access / API tokens are inventoried and " "risky tokens (dormant, broad-scope, no-expiry) are flagged." ), "signal_type": "scheduled_job", "signal_ref": "auditor.pat_audit_daily", "config": {}, "sort_order": 30, }, { "key": "public_exposure_scan", "name": "Public exposure scanning", "category": CATEGORY_POSTURE, "description": ( "Cloud resources and collaboration spaces are scanned for " "unintended public exposure." ), "signal_type": "scheduled_job", "signal_ref": "auditor.public_exposure_scan_daily", "config": {}, "sort_order": 40, }, { "key": "endpoint_risk_scoring", "name": "Composite risk scoring", "category": CATEGORY_POSTURE, "description": ( "Per-person composite risk score derived from MFA, secret, " "sign-in, token, and certificate posture." ), "signal_type": "scheduled_job", "signal_ref": "auditor.risk_score_recompute_hourly", "config": {}, "sort_order": 50, }, { "key": "secret_rotation", "name": "Secret rotation", "category": CATEGORY_CRYPTO, "description": ( "Long-lived secrets (API keys, IAM keys) are rotated within " "policy and overdue secrets are surfaced." ), "signal_type": "scheduled_job", "signal_ref": "auditor.secret_rotation_check_daily", "config": {}, "sort_order": 60, }, { "key": "cert_expiry_monitoring", "name": "Certificate expiry monitoring", "category": CATEGORY_CRYPTO, "description": ( "TLS / domain / ICP certificates are monitored for impending " "expiry at 90 / 60 / 30 day thresholds." ), "signal_type": "scheduled_job", "signal_ref": "auditor.cert_expiry_check_daily", "config": {}, "sort_order": 70, }, { "key": "signin_anomaly_monitoring", "name": "Sign-in anomaly monitoring", "category": CATEGORY_LOGGING, "description": ( "Sign-in events are pulled from identity providers and scored " "for anomalies; high-risk events are surfaced for review." ), "signal_type": "scheduled_job", "signal_ref": "auditor.signin_anomaly_score_hourly", "config": {}, "sort_order": 80, }, { "key": "access_certification", "name": "Access certification (periodic review)", "category": CATEGORY_ACCESS, "description": ( "User access is reviewed on a recurring cycle by designated " "reviewers (the holder's manager), who attest that each " "entitlement is still appropriate; unneeded access is revoked." ), # No scheduled job feeds this — its status derives from the latest # access-certification campaign's completion / due state. "signal_type": "cert_campaign", "signal_ref": None, "config": {"stale_after_days": 100}, "sort_order": 90, }, { "key": "access_self_attestation", "name": "Access self-attestation (半年度全员访问自查)", "category": CATEGORY_ACCESS, "description": ( "Every employee re-attests their OWN full access footprint " "(seats / cloud grants / permissions / SSO / API keys) twice a " "year; unneeded access is cleared via each type's self-service " "path. Holder-driven, complementing the manager-led access " "certification. Status derives from the latest self_attestation " "campaign's completion / due state." ), "signal_type": "cert_campaign", "signal_ref": None, "config": {"stale_after_days": 200}, "sort_order": 91, }, { "key": "security_awareness_training", "name": "Security awareness training (全员安全宣贯)", "category": CATEGORY_POSTURE, "description": ( "Employees are told, on a recurring basis, what the company " "expects of them on security — and each person's acknowledgement " "is recorded. Status derives from the most recent REQUIRED " "awareness campaign that asked for an acknowledgement: its " "coverage (confirmed / targeted) against the threshold, and its " "age against the staleness window. Voluntary notices are " "deliberately excluded — they ask nothing of anybody and " "counting them would let a comic nobody had to read carry the " "control." ), # Derives from campaign state, like ``access_certification`` and # ``access_self_attestation`` — no scheduled job feeds it, so # ``signal_ref`` stays None. "signal_type": "awareness_campaign", "signal_ref": None, "config": {"min_coverage_pct": 90, "stale_after_days": 200}, "sort_order": 105, }, { "key": "periodic_self_check", "name": "Periodic self-inspection (周期性自我检查)", "category": CATEGORY_POSTURE, "description": ( "Recurring operational security self-checks (bastion-host log " "audit, anti-virus sweep, full-disk-encryption, network config, " "…) are performed on their cadence and evidenced. Status derives " "from on-time completion of the self-check台账; the opener job " "below keeps current-period records flowing." ), # Status derives from the self-check records (overdue → fail), not a # job freshness gate, so signal_ref stays None (like access_cert). "signal_type": "self_check", "signal_ref": None, "config": {"backing_job": "auditor.self_check_opener_monthly"}, "sort_order": 100, }, ] # ════════════════════════════════════════════════════════════════════ # Crosswalk — control_key -> {framework_key: [requirement refs]} # ════════════════════════════════════════════════════════════════════ MAPPINGS: dict[str, dict[str, list[str]]] = { "mfa_coverage": { "soc2": ["CC6.1", "CC6.2"], "iso27001": ["A.8.5", "A.5.17"], }, "privileged_access_review": { "soc2": ["CC6.1", "CC6.3"], "iso27001": ["A.8.2", "A.5.18"], }, "pat_audit": { "soc2": ["CC6.3"], "iso27001": ["A.8.2", "A.5.17"], }, "public_exposure_scan": { "soc2": ["CC6.6", "CC7.1"], "iso27001": ["A.8.8"], }, "endpoint_risk_scoring": { "soc2": ["CC7.1"], "iso27001": ["A.8.8"], }, "secret_rotation": { "soc2": ["CC6.1", "CC6.7"], "iso27001": ["A.8.24", "A.5.17"], }, "cert_expiry_monitoring": { "soc2": ["CC6.7"], "iso27001": ["A.8.24"], }, "signin_anomaly_monitoring": { "soc2": ["CC7.2"], "iso27001": ["A.8.16", "A.8.15"], }, "access_certification": { "soc2": ["CC6.3"], "iso27001": ["A.5.15", "A.5.18"], }, "access_self_attestation": { "soc2": ["CC6.3"], "iso27001": ["A.5.15", "A.5.18"], }, "periodic_self_check": { "soc2": ["CC7.1", "CC7.2"], "iso27001": ["A.8.16", "A.8.8"], }, "security_awareness_training": { "soc2": ["CC2.2"], "iso27001": ["A.6.3"], }, } def all_control_keys() -> set[str]: return {c["key"] for c in CONTROLS}