Files

107 lines
3.7 KiB
Python

"""
S64 Security Invariants Registry.
This module defines the canonical security invariants that must be true
for the application to be considered secure. These invariants are
enforced locally by the Security Gate (S41) and in CI by policy checks.
Artifact Ownership:
- Public Posture: unreachable surfaces (admin-plane leaks)
- Admin Plane: capabilities restricted to admin token
- Fail-Closed: missing security controls block startup
"""
import enum
from dataclasses import dataclass
from typing import Dict, List, Optional
class InvariantScope(enum.Enum):
STARTUP = "startup"
RUNTIME = "runtime"
CI = "ci"
class InvariantSeverity(enum.Enum):
CRITICAL = "critical" # Must block startup/CI
HIGH = "high" # Should block, overrideable in DEV
WARNING = "warning" # Audit only
@dataclass
class SecurityInvariant:
id: str
scope: InvariantScope
severity: InvariantSeverity
description: str
remediation: str
# Canonical Registry of Security Invariants
REGISTRY: Dict[str, SecurityInvariant] = {
# Public Posture / Network Invariants
"S64.INV.001": SecurityInvariant(
id="S64.INV.001",
scope=InvariantScope.STARTUP,
severity=InvariantSeverity.CRITICAL,
description="Admin-plane routes must not be exposed on public interfaces without explicit auth override.",
remediation="Configure OPENCLAW_ADMIN_TOKEN or bind to localhost only.",
),
"S64.INV.002": SecurityInvariant(
id="S64.INV.002",
scope=InvariantScope.STARTUP,
severity=InvariantSeverity.CRITICAL,
description="Public ingress must not bypass MAE route segmentation (no Admin/Internal on User plane).",
remediation="Check route configuration and deployment profile (OPENCLAW_DEPLOYMENT_PROFILE).",
),
# Fail-Closed Invariants
"S64.INV.003": SecurityInvariant(
id="S64.INV.003",
scope=InvariantScope.STARTUP,
severity=InvariantSeverity.CRITICAL,
description="Missing critical security secrets (Tokens/Keys) must block startup in Hardened/Public modes.",
remediation="Provide required secrets (OPENCLAW_ADMIN_TOKEN, keys) or switch to Local profile.",
),
"S64.INV.004": SecurityInvariant(
id="S64.INV.004",
scope=InvariantScope.STARTUP,
severity=InvariantSeverity.CRITICAL,
description="Failed module adapters must not degrade into 'open' state.",
remediation="Check module initialization logs. Ensure fail-closed logic is active.",
),
# Metadata / Governance Invariants (R116)
"S64.INV.005": SecurityInvariant(
id="S64.INV.005",
scope=InvariantScope.CI,
severity=InvariantSeverity.CRITICAL,
description="All managed routes must have explicit Route Plane classification.",
remediation="Decorate route handler with @endpoint_metadata(plane=...).",
),
"S64.INV.006": SecurityInvariant(
id="S64.INV.006",
scope=InvariantScope.CI,
severity=InvariantSeverity.CRITICAL,
description="All managed routes must have explicit Auth Tier classification.",
remediation="Decorate route handler with @endpoint_metadata(auth=...).",
),
}
@dataclass
class InvariantViolation:
invariant_id: str
context: str
evidence: str
def to_dict(self):
inv = REGISTRY.get(self.invariant_id)
return {
"code": self.invariant_id,
"severity": inv.severity.value if inv else "unknown",
"scope": inv.scope.value if inv else "unknown",
"description": inv.description if inv else "Unknown Invariant",
"context": self.context,
"evidence": self.evidence,
"remediation": inv.remediation if inv else "",
}