# Copyright 2026 Guildhouse Dev # SPDX-License-Identifier: Apache-2.0 """Declarative compliance policy schema. A policy defines conditions that managed devices must satisfy. Each condition maps to a PostureConditionKind evaluated by Bastion's compliance engine. Conditions can have platform-specific check implementations (Intune field, script, Keylime attestation). Policies are framework-agnostic. They describe WHAT must be true, not HOW to achieve it. Playbooks/scripts achieve compliance; policies evaluate it. """ from __future__ import annotations import tomllib from pathlib import Path from typing import Any, Optional from pydantic import BaseModel class PlatformCheck(BaseModel): """Platform-specific check implementation.""" intune_field: Optional[str] = None intune_policy: Optional[str] = None expect: Optional[Any] = None script: Optional[str] = None keylime_check: Optional[str] = None class PolicyCondition(BaseModel): """A single compliance condition.""" id: str kind: str description: str = "" framework_ref: Optional[str] = None severity: str = "medium" optional: bool = False linux: Optional[PlatformCheck] = None windows: Optional[PlatformCheck] = None macos: Optional[PlatformCheck] = None class BreachResponseConfig(BaseModel): """What happens when conditions at each severity level fail.""" critical: str = "suspend_access" high: str = "alert_msp" medium: str = "log_only" low: str = "log_only" class EvaluationSchedule(BaseModel): """How often to evaluate compliance.""" interval_seconds: int = 300 full_evaluation_hours: int = 24 class CompliancePolicy(BaseModel): """A complete compliance policy definition.""" name: str description: str = "" version: str = "1.0.0" framework: Optional[str] = None framework_controls: list[str] = [] conditions: list[PolicyCondition] = [] breach_response: BreachResponseConfig = BreachResponseConfig() schedule: EvaluationSchedule = EvaluationSchedule() @classmethod def from_toml(cls, path: str | Path) -> CompliancePolicy: """Parse a policy TOML file.""" with open(path, "rb") as f: data = tomllib.load(f) return cls.model_validate(data) def conditions_for_platform(self, platform: str) -> list[PolicyCondition]: """Return conditions applicable to a specific platform.""" result = [] for c in self.conditions: check = getattr(c, platform, None) if check is not None: result.append(c) return result