323 lines
13 KiB
Python
323 lines
13 KiB
Python
#!/usr/bin/env python3
|
|
"""Resolve risk-based semantic and adversarial review requirements."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
from pathlib import Path
|
|
import sys
|
|
from typing import Any
|
|
|
|
|
|
DEFAULT_ROOT = Path(__file__).resolve().parents[2]
|
|
DEFAULT_PROFILES = DEFAULT_ROOT / "harness/source/execution-profiles.json"
|
|
DEFAULT_WORKFLOW_DIR = DEFAULT_ROOT / "harness/source/workflows"
|
|
SCHEMA_VERSION = "execution-profile-result/v1"
|
|
OUTPUT_MODES = {
|
|
"failures-only",
|
|
"decision-risk-summary",
|
|
"detailed-artifact",
|
|
"public-claim-verification",
|
|
}
|
|
WORKFLOW_KINDS = {"orchestrated", "deterministic"}
|
|
|
|
|
|
class ProfileError(ValueError):
|
|
pass
|
|
|
|
|
|
def load_profiles(path: Path) -> dict[str, Any]:
|
|
document = json.loads(path.read_text(encoding="utf-8"))
|
|
if document.get("schema_version") != "execution-profiles/v1":
|
|
raise ProfileError("expected execution-profiles/v1")
|
|
levels = document.get("risk_levels")
|
|
profiles = document.get("profiles")
|
|
checks = document.get("always_checks")
|
|
mandatory = document.get("mandatory_gates")
|
|
if not isinstance(levels, list) or not levels or len(set(levels)) != len(levels):
|
|
raise ProfileError("risk_levels must be a non-empty unique list")
|
|
if not isinstance(profiles, dict) or not profiles:
|
|
raise ProfileError("profiles must be a non-empty object")
|
|
if not isinstance(checks, list) or not checks or not all(isinstance(item, str) for item in checks):
|
|
raise ProfileError("always_checks must be a non-empty string list")
|
|
if mandatory != {
|
|
"typed_contract": "required_for_design_bearing",
|
|
"semantic_coherence": "required_for_design_bearing",
|
|
"proof_manifest": "required_when_claims_present",
|
|
}:
|
|
raise ProfileError("mandatory_gates must declare the v1 typed/semantic/proof policies")
|
|
for profile, config in profiles.items():
|
|
if not isinstance(profile, str) or not isinstance(config, dict):
|
|
raise ProfileError("profile entries must be named objects")
|
|
dispatch = config.get("dispatch_contract")
|
|
if not isinstance(dispatch, dict) or dispatch != {
|
|
"semantic_review": "when-required",
|
|
"adversarial_review": "when-required",
|
|
}:
|
|
raise ProfileError(f"{profile}: invalid dispatch_contract")
|
|
output = config.get("output_contract")
|
|
if not isinstance(output, dict) or output.get("mode") not in OUTPUT_MODES:
|
|
raise ProfileError(f"{profile}: invalid output_contract.mode")
|
|
include = output.get("include")
|
|
if (
|
|
not isinstance(include, list)
|
|
or not include
|
|
or not all(isinstance(item, str) and item for item in include)
|
|
or len(set(include)) != len(include)
|
|
):
|
|
raise ProfileError(f"{profile}: output_contract.include must be unique strings")
|
|
intensity = config.get("review_intensity")
|
|
if (
|
|
not isinstance(intensity, dict)
|
|
or set(intensity) != {"semantic_passes", "adversarial_findings", "impact_scope"}
|
|
or not isinstance(intensity.get("semantic_passes"), int)
|
|
or isinstance(intensity.get("semantic_passes"), bool)
|
|
or intensity["semantic_passes"] < 1
|
|
or not isinstance(intensity.get("adversarial_findings"), bool)
|
|
or intensity.get("impact_scope") not in {"direct", "transitive", "full-hub"}
|
|
):
|
|
raise ProfileError(f"{profile}: invalid review_intensity")
|
|
return document
|
|
|
|
|
|
def load_workflow_contract(workflow: str, workflow_dir: Path = DEFAULT_WORKFLOW_DIR) -> tuple[str, dict[str, Any]]:
|
|
if not workflow or Path(workflow).name != workflow or workflow.endswith(".json"):
|
|
raise ProfileError("workflow must be an id without path separators or extension")
|
|
path = workflow_dir / f"{workflow}.json"
|
|
data = json.loads(path.read_text(encoding="utf-8"))
|
|
if data.get("schema_version") != 2 or data.get("source_kind") != "workflow":
|
|
raise ProfileError(f"{path}: expected workflow schema_version 2")
|
|
if data.get("id") != workflow:
|
|
raise ProfileError(f"{path}: workflow id mismatch")
|
|
if "profile" in data or "default_risk" in data:
|
|
raise ProfileError(f"{path}: profile/default_risk must be nested")
|
|
contract = data.get("execution_contract")
|
|
if not isinstance(contract, dict):
|
|
raise ProfileError(f"{path}: missing execution_contract")
|
|
allowed = {
|
|
"kind",
|
|
"profile",
|
|
"default_risk",
|
|
"entrypoint",
|
|
"dry_run_first",
|
|
"result_schema",
|
|
"design_bearing",
|
|
}
|
|
if set(contract) - allowed:
|
|
raise ProfileError(f"{path}: unsupported execution_contract fields")
|
|
kind = contract.get("kind")
|
|
if kind not in WORKFLOW_KINDS:
|
|
raise ProfileError(f"{path}: unsupported execution kind")
|
|
deterministic = {"entrypoint", "dry_run_first", "result_schema"}
|
|
if kind == "deterministic":
|
|
if not deterministic.issubset(contract):
|
|
raise ProfileError(f"{path}: incomplete deterministic execution contract")
|
|
entrypoint = contract.get("entrypoint")
|
|
result_schema = contract.get("result_schema")
|
|
if (
|
|
not isinstance(entrypoint, str)
|
|
or not entrypoint.endswith(".py")
|
|
or Path(entrypoint).is_absolute()
|
|
or ".." in Path(entrypoint).parts
|
|
or contract.get("dry_run_first") is not True
|
|
or not isinstance(result_schema, str)
|
|
or not result_schema
|
|
):
|
|
raise ProfileError(f"{path}: invalid deterministic execution contract")
|
|
elif deterministic.intersection(contract):
|
|
raise ProfileError(f"{path}: orchestrated workflow declares deterministic fields")
|
|
profile = contract.get("profile")
|
|
risk = contract.get("default_risk")
|
|
design_bearing = contract.get("design_bearing")
|
|
if not isinstance(profile, str) or not isinstance(risk, str) or not isinstance(design_bearing, bool):
|
|
raise ProfileError(f"{path}: execution_contract requires profile/default_risk/design_bearing")
|
|
return workflow, dict(contract)
|
|
|
|
|
|
def _condition_matches(condition: str, context: dict[str, Any], levels: list[str]) -> bool:
|
|
if condition == "public_claims_present":
|
|
return bool(context["public_claims_present"])
|
|
if condition.startswith("finding_count>="):
|
|
try:
|
|
threshold = int(condition.split(">=", 1)[1])
|
|
except ValueError as exc:
|
|
raise ProfileError(f"invalid condition: {condition}") from exc
|
|
return context["finding_count"] >= threshold
|
|
if condition.startswith("risk>="):
|
|
threshold = condition.split(">=", 1)[1]
|
|
if threshold not in levels:
|
|
raise ProfileError(f"unknown risk threshold: {threshold}")
|
|
return levels.index(context["risk"]) >= levels.index(threshold)
|
|
raise ProfileError(f"unsupported condition: {condition}")
|
|
|
|
|
|
def _required(rule: Any, context: dict[str, Any], levels: list[str]) -> tuple[bool, list[str]]:
|
|
if not isinstance(rule, dict):
|
|
raise ProfileError("review rule must be an object")
|
|
mode = rule.get("mode")
|
|
if mode == "not_required":
|
|
return False, []
|
|
if mode == "required":
|
|
return True, ["profile requires review"]
|
|
if mode == "required_at_or_above_risk":
|
|
threshold = rule.get("minimum_risk")
|
|
if threshold not in levels:
|
|
raise ProfileError(f"unknown minimum_risk: {threshold}")
|
|
matched = levels.index(context["risk"]) >= levels.index(threshold)
|
|
return matched, [f"risk>={threshold}"] if matched else []
|
|
if mode == "required_when":
|
|
conditions = rule.get("conditions")
|
|
if not isinstance(conditions, list) or not conditions or not all(
|
|
isinstance(item, str) for item in conditions
|
|
):
|
|
raise ProfileError("required_when.conditions must be a non-empty string list")
|
|
matched = [item for item in conditions if _condition_matches(item, context, levels)]
|
|
return bool(matched), matched
|
|
raise ProfileError(f"unsupported review mode: {mode}")
|
|
|
|
|
|
def resolve_policy(
|
|
document: dict[str, Any],
|
|
profile: str,
|
|
risk: str,
|
|
finding_count: int,
|
|
public_claims_present: bool,
|
|
design_bearing: bool = False,
|
|
claims_present: bool = False,
|
|
) -> dict[str, Any]:
|
|
levels = document["risk_levels"]
|
|
profiles = document["profiles"]
|
|
if profile not in profiles:
|
|
raise ProfileError(f"unknown profile: {profile}")
|
|
if risk not in levels:
|
|
raise ProfileError(f"unknown risk: {risk}")
|
|
if finding_count < 0:
|
|
raise ProfileError("finding_count must be >= 0")
|
|
context = {
|
|
"risk": risk,
|
|
"finding_count": finding_count,
|
|
"public_claims_present": public_claims_present,
|
|
"design_bearing": design_bearing,
|
|
"claims_present": claims_present,
|
|
}
|
|
config = profiles[profile]
|
|
semantic, semantic_reasons = _required(config.get("semantic_review"), context, levels)
|
|
if design_bearing and not semantic:
|
|
semantic = True
|
|
semantic_reasons = ["mandatory_gates.semantic_coherence"]
|
|
adversarial, adversarial_reasons = _required(config.get("adversarial_review"), context, levels)
|
|
output_contract = dict(config["output_contract"])
|
|
base_intensity = dict(config["review_intensity"])
|
|
risk_index = levels.index(risk)
|
|
if risk_index >= levels.index("high"):
|
|
base_intensity["semantic_passes"] += 1
|
|
if base_intensity["impact_scope"] == "direct":
|
|
base_intensity["impact_scope"] = "transitive"
|
|
base_intensity["adversarial_findings"] = bool(
|
|
base_intensity["adversarial_findings"] or adversarial
|
|
)
|
|
mandatory_gates = {
|
|
"typed_contract": "required" if design_bearing else "skip",
|
|
"semantic_coherence": "required" if design_bearing else "skip",
|
|
"proof_manifest": "required" if claims_present or public_claims_present else "skip",
|
|
}
|
|
return {
|
|
"schema_version": SCHEMA_VERSION,
|
|
"status": "RESOLVED",
|
|
"profile": profile,
|
|
"context": context,
|
|
"always_checks": list(document["always_checks"]),
|
|
"mandatory_gates": mandatory_gates,
|
|
"review_intensity": base_intensity,
|
|
"semantic_review": {"required": semantic, "matched_conditions": semantic_reasons},
|
|
"adversarial_review": {"required": adversarial, "matched_conditions": adversarial_reasons},
|
|
"dispatch": {
|
|
"semantic_review": "dispatch" if semantic else "skip",
|
|
"adversarial_review": "dispatch" if adversarial else "skip",
|
|
},
|
|
"output_contract": output_contract,
|
|
"output_mode": output_contract["mode"],
|
|
}
|
|
|
|
|
|
def resolve_workflow_policy(
|
|
document: dict[str, Any],
|
|
workflow: str,
|
|
workflow_dir: Path,
|
|
risk: str | None,
|
|
finding_count: int,
|
|
public_claims_present: bool,
|
|
claims_present: bool = False,
|
|
) -> dict[str, Any]:
|
|
workflow_id, contract = load_workflow_contract(workflow, workflow_dir)
|
|
selected_risk = risk or contract["default_risk"]
|
|
result = resolve_policy(
|
|
document,
|
|
contract["profile"],
|
|
selected_risk,
|
|
finding_count,
|
|
public_claims_present,
|
|
bool(contract["design_bearing"]),
|
|
claims_present,
|
|
)
|
|
result["workflow"] = workflow_id
|
|
result["execution_contract"] = contract
|
|
return result
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("profile", nargs="?")
|
|
parser.add_argument("--workflow", help="resolve profile/default risk from neutral workflow metadata")
|
|
parser.add_argument("--risk", help="override workflow default risk; required with positional profile")
|
|
parser.add_argument("--finding-count", type=int, default=0)
|
|
parser.add_argument("--public-claims-present", action="store_true")
|
|
parser.add_argument("--claims-present", action="store_true")
|
|
parser.add_argument("--profiles", type=Path, default=DEFAULT_PROFILES)
|
|
parser.add_argument("--workflow-dir", type=Path, default=DEFAULT_WORKFLOW_DIR)
|
|
args = parser.parse_args(argv)
|
|
try:
|
|
document = load_profiles(args.profiles.resolve(strict=True))
|
|
if bool(args.profile) == bool(args.workflow):
|
|
raise ProfileError("provide exactly one positional profile or --workflow")
|
|
if args.workflow:
|
|
result = resolve_workflow_policy(
|
|
document,
|
|
args.workflow,
|
|
args.workflow_dir.resolve(strict=True),
|
|
args.risk,
|
|
args.finding_count,
|
|
args.public_claims_present,
|
|
args.claims_present,
|
|
)
|
|
else:
|
|
if args.risk is None:
|
|
raise ProfileError("--risk is required with positional profile")
|
|
result = resolve_policy(
|
|
document,
|
|
args.profile,
|
|
args.risk,
|
|
args.finding_count,
|
|
args.public_claims_present,
|
|
False,
|
|
args.claims_present,
|
|
)
|
|
json.dump(result, sys.stdout, ensure_ascii=False, indent=2, sort_keys=True)
|
|
sys.stdout.write("\n")
|
|
return 0
|
|
except (OSError, UnicodeError, json.JSONDecodeError, ProfileError) as exc:
|
|
json.dump(
|
|
{"schema_version": SCHEMA_VERSION, "status": "FAIL", "error": str(exc)},
|
|
sys.stdout,
|
|
ensure_ascii=False,
|
|
indent=2,
|
|
sort_keys=True,
|
|
)
|
|
sys.stdout.write("\n")
|
|
return 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|