AID-M-005.002: Policy-as-Code Configuration Baselines & Posture Release Gates

Version 1.20261015 · Actionable sub-technique

During build and validation, define security configuration baselines for AI infrastructure and services as policy-as-code, and establish measurable posture SLO/SLI and release gates. Produce versioned, signed baselines and scoring criteria as the single source of truth for deployment admission and later audit.

Pillars: infra

Lifecycle: building, validation

Scope boundary

Owns signed policy-as-code configuration baselines, posture SLO and SLI definitions, scoring criteria, and build or release conformance gates. It does not own continuous runtime CSPM or drift findings.

AID-M-005.001: AID-M-005.002 evaluates release configuration against signed policy and measurable criteria; AID-M-005.001 authors the underlying secure templates and validates their static structure. Gate evidence proves conformance of a measured release population, while template review proves only the design artifact.

Implementation guidance

AID-M-005.002-G001

Author security baselines as policy-as-code and wire them into CI gates.
Scope and input contract

Evaluate Terraform JSON configuration, where resources use the real resource.<type>.<name> structure. This example deliberately does not claim to parse HCL .tf. If HCL is authoritative, first render the complete declared HCL population into Terraform JSON with a renderer pinned by immutable version or digest, compare source and rendered population manifests, and reject a partial or unpinned rendering.

Rego policy
# File: policy/aidefend-baseline/aidefend_baseline.rego
package aidefend.baseline

import rego.v1

resources := object.get(input, "resource", null)
safe_resources := resources if {
  is_object(resources)
} else := {}
security_groups := object.get(safe_resources, "aws_security_group", {})
instances := object.get(safe_resources, "aws_instance", {})
open_cidrs := {"0.0.0.0/0", "::/0"}

invalid_resource_root if {
  not is_object(resources)
}

invalid_resource_root if {
  is_object(resources)
  count(resources) == 0
}

invalid_literal_cidr(cidr) if {
  not is_string(cidr)
}

invalid_literal_cidr(cidr) if {
  is_string(cidr)
  not net.cidr_is_valid(cidr)
}

deny contains "Terraform JSON must contain a non-empty resource object" if {
  invalid_resource_root
}

deny contains "resource.aws_security_group must be an object" if {
  is_object(resources)
  not is_object(security_groups)
}

deny contains "resource.aws_instance must be an object" if {
  is_object(resources)
  not is_object(instances)
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  not is_object(group)
  msg := sprintf("aws_security_group.%s must be an object", [name])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  ingress := object.get(group, "ingress", [])
  not is_array(ingress)
  msg := sprintf("aws_security_group.%s ingress must be a literal array", [name])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  some index, ingress in object.get(group, "ingress", [])
  not is_object(ingress)
  msg := sprintf("aws_security_group.%s ingress[%d] must be an object", [name, index])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  some index, ingress in object.get(group, "ingress", [])
  is_object(ingress)
  cidrs := object.get(ingress, "cidr_blocks", null)
  not is_array(cidrs)
  msg := sprintf("aws_security_group.%s ingress[%d] must declare literal cidr_blocks", [name, index])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  some index, ingress in object.get(group, "ingress", [])
  is_object(ingress)
  cidrs := object.get(ingress, "cidr_blocks", [])
  is_array(cidrs)
  count(cidrs) == 0
  msg := sprintf("aws_security_group.%s ingress[%d] has no CIDR", [name, index])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  some index, ingress in object.get(group, "ingress", [])
  is_object(ingress)
  some cidr in object.get(ingress, "cidr_blocks", [])
  invalid_literal_cidr(cidr)
  msg := sprintf("aws_security_group.%s ingress[%d] has an unresolved or invalid CIDR", [name, index])
}

deny contains msg if {
  is_object(security_groups)
  some name, group in security_groups
  is_object(group)
  some index, ingress in object.get(group, "ingress", [])
  is_object(ingress)
  some cidr in object.get(ingress, "cidr_blocks", [])
  cidr in open_cidrs
  msg := sprintf("aws_security_group.%s ingress[%d] permits a globally open CIDR", [name, index])
}

deny contains msg if {
  is_object(instances)
  some name, instance in instances
  not is_object(instance)
  msg := sprintf("aws_instance.%s must be an object", [name])
}

deny contains msg if {
  is_object(instances)
  some name, instance in instances
  is_object(instance)
  object.get(instance, "associate_public_ip_address", null) != false
  msg := sprintf("aws_instance.%s must set associate_public_ip_address to literal false", [name])
}

This bounded policy requires every inline security-group ingress rule to have a non-empty list of literal, valid IP CIDRs and every declared EC2 instance to set public-IP association explicitly to false. It also rejects missing, empty, or non-object resource roots, malformed resource-type maps, resource instances, and ingress entries instead of treating skipped evaluation as success. Add separately tested rules if the approved architecture uses security-group references or another Terraform resource model.

Terraform JSON fixtures
# File: terraform/rendered/baseline.tf.json
{"resource":{"aws_security_group":{"app":{"ingress":[{"from_port":443,"to_port":443,"protocol":"tcp","cidr_blocks":["10.20.0.0/16"]}]}},"aws_instance":{"app":{"associate_public_ip_address":false}}}}
# File: testdata/clean.tf.json
{"resource":{"aws_security_group":{"app":{"ingress":[{"from_port":443,"to_port":443,"protocol":"tcp","cidr_blocks":["10.20.0.0/16"]}]}},"aws_instance":{"app":{"associate_public_ip_address":false}}}}
# File: testdata/open-cidr.tf.json
{"resource":{"aws_security_group":{"app":{"ingress":[{"cidr_blocks":["0.0.0.0/0"]}]}}}}
# File: testdata/unresolved-cidr.tf.json
{"resource":{"aws_security_group":{"app":{"ingress":[{"cidr_blocks":"${var.allowed_ingress_cidrs}"}]}}}}
# File: testdata/public-ip.tf.json
{"resource":{"aws_instance":{"app":{"associate_public_ip_address":true}}}}
# File: testdata/missing-resource.tf.json
{}
# File: testdata/non-object-resource.tf.json
{"resource":[]}
# File: testdata/malformed-resource-maps.tf.json
{"resource":{"aws_security_group":[],"aws_instance":[]}}
# File: testdata/malformed-resource-rows.tf.json
{"resource":{"aws_security_group":{"bad_group":"not-an-object","bad_ingress":{"ingress":["not-an-object"]}},"aws_instance":{"bad_instance":"not-an-object"}}}

The last four fixtures deliberately cover absent or non-object resource roots and malformed Terraform JSON shapes, proving the policy returns an explicit deny before field-level checks.

Declare the complete production population
# File: policy/terraform-json-population.txt
terraform/rendered/baseline.tf.json
Validate Conftest JSON reports independently
# File: ci/validate_conftest_report.py
#!/usr/bin/env python3
import argparse
import json

parser = argparse.ArgumentParser()
parser.add_argument("--report", required=True)
parser.add_argument("--namespace", required=True)
parser.add_argument("--expect-file", action="append", default=[])
parser.add_argument("--expect-finding", action="append", default=[])
parser.add_argument("--require-pass", action="store_true")
args = parser.parse_args()

with open(args.report, encoding="utf-8") as handle:
    rows = json.load(handle)
if not isinstance(rows, list) or not rows:
    raise SystemExit("Conftest report must be a non-empty JSON list")

expected_files = sorted(args.expect_file)
actual_files = [row.get("filename") for row in rows if isinstance(row, dict)]
if len(actual_files) != len(rows) or sorted(actual_files) != expected_files:
    raise SystemExit(f"Conftest filenames differ: expected={expected_files!r} actual={sorted(actual_files)!r}")

messages = []
for row in rows:
    if row.get("namespace") != args.namespace:
        raise SystemExit(f"Unexpected namespace for {row.get('filename')!r}")
    successes = row.get("successes")
    if not isinstance(successes, int) or isinstance(successes, bool) or successes < 0:
        raise SystemExit(f"Invalid successes count for {row.get('filename')!r}")
    for field in ("warnings", "exceptions"):
        if row.get(field, []):
            raise SystemExit(f"Unexpected {field} for {row.get('filename')!r}")
    failures = row.get("failures", [])
    if not isinstance(failures, list):
        raise SystemExit(f"Invalid failures field for {row.get('filename')!r}")
    messages.extend(str(item.get("msg", "")) for item in failures if isinstance(item, dict))
    if args.require_pass and (successes <= 0 or failures):
        raise SystemExit(f"No positive policy evaluation for {row.get('filename')!r}")

if args.require_pass and messages:
    raise SystemExit("PASS report contains findings")
if not args.require_pass and not messages:
    raise SystemExit("Negative fixture produced no policy finding")
for expected in args.expect_finding:
    if not any(expected in message for message in messages):
        raise SystemExit(f"Expected finding not present: {expected!r}")
GitHub Actions gate
# File: .github/workflows/iac-baseline.yml
name: IaC Baseline Check
on:
  pull_request:
permissions:
  contents: read
jobs:
  conftest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
      - name: Test policy and gate the declared population
        shell: bash
        run: |
          set -euo pipefail
          image='openpolicyagent/conftest:v0.65.0@sha256:00a89cb5755b0cc9898868ffad8f11e3411fedb7074ea0f3e1f1ababa0a4cfe8'
          namespace='aidefend.baseline'
          mapfile -t declared < <(sed -e '/^[[:space:]]*#/d' -e '/^[[:space:]]*$/d' policy/terraform-json-population.txt)
          (( ${#declared[@]} > 0 )) || { echo 'Terraform JSON population is empty' >&2; exit 2; }
          for path in "${declared[@]}"; do
            [[ "$path" == terraform/rendered/*.tf.json && "$path" != *'..'* ]] || { echo "Unsafe population path: $path" >&2; exit 2; }
          done
          mapfile -t declared_sorted < <(printf '%s\n' "${declared[@]}" | LC_ALL=C sort -u)
          (( ${#declared_sorted[@]} == ${#declared[@]} )) || { echo 'Population manifest has duplicates' >&2; exit 2; }
          mapfile -d '' discovered < <(find terraform/rendered -type f -name '*.tf.json' -print0 | LC_ALL=C sort -z)
          diff -u <(printf '%s\n' "${declared_sorted[@]}") <(printf '%s\n' "${discovered[@]}")

          conftest=(docker run --rm --user "$(id -u):$(id -g)" -v "$PWD:/workspace:ro" -w /workspace "$image" test --policy policy/aidefend-baseline --namespace "$namespace" --output json)
          validator=(python3 ci/validate_conftest_report.py --namespace "$namespace")
          mkdir -p fixture-reports
          run_case() {
            local expected="$1" file="$2" stem report rc finding
            shift 2
            stem="$(basename "$file" .tf.json)"
            report="fixture-reports/$stem.json"
            set +e
            "${conftest[@]}" "$file" >"$report" 2>"${report%.json}.stderr.log"
            rc=$?
            set -e
            validate=("${validator[@]}" --report "$report" --expect-file "$file")
            for finding in "$@"; do
              validate+=(--expect-finding "$finding")
            done
            if [[ "$expected" == pass ]]; then
              (( rc == 0 )) || return 1
              "${validate[@]}" --require-pass
            else
              (( rc != 0 )) || return 1
              "${validate[@]}"
            fi
          }
          run_case pass testdata/clean.tf.json
          run_case fail testdata/open-cidr.tf.json 'globally open CIDR'
          run_case fail testdata/unresolved-cidr.tf.json 'literal cidr_blocks'
          run_case fail testdata/public-ip.tf.json 'literal false'
          run_case fail testdata/missing-resource.tf.json 'Terraform JSON must contain a non-empty resource object'
          run_case fail testdata/non-object-resource.tf.json 'Terraform JSON must contain a non-empty resource object'
          run_case fail testdata/malformed-resource-maps.tf.json 'resource.aws_security_group must be an object' 'resource.aws_instance must be an object'
          run_case fail testdata/malformed-resource-rows.tf.json 'aws_security_group.bad_group must be an object' 'ingress[0] must be an object' 'aws_instance.bad_instance must be an object'

          set +e
          "${conftest[@]}" "${declared_sorted[@]}" >conftest-report.json 2>conftest-stderr.log
          gate_rc=$?
          set -e
          (( gate_rc == 0 )) || exit "$gate_rc"
          validate_production=("${validator[@]}" --report conftest-report.json --require-pass)
          for path in "${declared_sorted[@]}"; do
            validate_production+=(--expect-file "$path")
          done
          "${validate_production[@]}"

      - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02
        if: ${{ always() }}
        with:
          name: conftest-report
          path: |
            conftest-report.json
            conftest-stderr.log
            fixture-reports/
          if-no-files-found: error

The custom package is selected explicitly with --namespace aidefend.baseline. The shell rejects an empty, duplicate, unsafe, missing, or extra declared input population and uses set -euo pipefail. Independent report parsing requires a non-empty result, exact filenames and namespace, positive successful evaluations for every passing file, no warnings, exceptions, or findings, and every expected negative finding; a missing policy, zero-test result, parser error, or incomplete population cannot become a pass. Reports upload even when the gate fails.

AID-M-005.002-G002

Define posture SLO/SLI and scoring rules; document release gates.
Concept

Quantify compliance (coverage, violations, exceptions) and make them enforceable gates at pre-merge and pre-release.

SLO spec
# File: governance/baseline_slo.yaml
slo:
  schema_version: aidefend.baseline-slo.v1
  coverage_target_percent: 100
  violations:
    critical: 0
    high: 0
    medium_max: 3
  exceptions:
    approval_authority: PlatformSec
    max_ttl_days: 30
  report_public_key: keys/policy-report.pub
  signature_bundle: out/policy_report.sig
  verification_timeout_seconds: 30
CI check (Python helper)
# File: tools/check_slo.py
from __future__ import annotations

import json
import math
import subprocess
import tempfile
from datetime import datetime, timezone
from pathlib import Path

import yaml

SLO_PATH = Path("governance/baseline_slo.yaml")
REPORT_PATH = Path("out/policy_report.json")


def unique_object(pairs):
    value = {}
    for key, item in pairs:
        if key in value:
            raise ValueError(f"duplicate JSON key: {key}")
        value[key] = item
    return value


def when(value: str) -> datetime:
    parsed = datetime.fromisoformat(value.replace("Z", "+00:00"))
    if parsed.tzinfo is None:
        raise ValueError("exception timestamp lacks timezone")
    return parsed.astimezone(timezone.utc)


def verified_report_bytes(slo: dict) -> bytes:
    payload = REPORT_PATH.read_bytes()
    bundle = Path(slo["signature_bundle"]).read_bytes()
    if not payload or not bundle:
        raise ValueError("policy report or signature bundle is empty")
    with tempfile.TemporaryDirectory(prefix="aidefend-slo-") as stage_name:
        stage = Path(stage_name)
        snapshot = stage / "policy_report.json"
        signature = stage / "policy_report.sig"
        snapshot.write_bytes(payload)
        signature.write_bytes(bundle)
        subprocess.run(
            [
                "cosign", "verify-blob", "--key", slo["report_public_key"],
                "--bundle", str(signature), str(snapshot),
            ],
            check=True, capture_output=True, text=True,
            timeout=slo["verification_timeout_seconds"],
        )
        if snapshot.read_bytes() != payload or signature.read_bytes() != bundle:
            raise ValueError("verified report snapshot changed")
    return payload


def fail(message: str) -> None:
    raise SystemExit("SLO BLOCKED: " + message)


config = yaml.safe_load(SLO_PATH.read_text(encoding="utf-8"))
if not isinstance(config, dict) or set(config) != {"slo"}:
    fail("SLO document schema differs")
slo = config["slo"]
required_slo = {
    "schema_version", "coverage_target_percent", "violations", "exceptions",
    "report_public_key", "signature_bundle", "verification_timeout_seconds",
}
if not isinstance(slo, dict) or set(slo) != required_slo:
    fail("SLO policy schema differs")
if slo["schema_version"] != "aidefend.baseline-slo.v1":
    fail("SLO policy version differs")
try:
    report = json.loads(
        verified_report_bytes(slo).decode("utf-8", errors="strict"),
        object_pairs_hook=unique_object,
        parse_constant=lambda value: (_ for _ in ()).throw(
            ValueError(f"non-finite JSON number: {value}")
        ),
    )
except (OSError, UnicodeError, ValueError, subprocess.SubprocessError) as error:
    fail("report verification error: " + str(error))
if not isinstance(report, dict) or set(report) != {
    "schema_version", "coverage_percent", "violations", "exceptions",
} or report["schema_version"] != "aidefend.policy-report.v1":
    fail("policy report schema differs")
coverage = report["coverage_percent"]
if (
    isinstance(coverage, bool) or not isinstance(coverage, (int, float))
    or not math.isfinite(coverage) or not 0 <= coverage <= 100
    or coverage < float(slo["coverage_target_percent"])
):
    fail("coverage target is not met")

now = datetime.now(timezone.utc)
valid_exception_ids = set()
for exception in report["exceptions"]:
    if not isinstance(exception, dict) or set(exception) != {
        "id", "owner", "approved_by", "approved_at", "expires_at",
    }:
        fail("exception schema differs")
    approved = when(exception["approved_at"])
    expires = when(exception["expires_at"])
    ttl = (expires - approved).total_seconds() / 86400
    if (
        not exception["id"] or exception["id"] in valid_exception_ids
        or not exception["owner"]
        or exception["approved_by"] != slo["exceptions"]["approval_authority"]
        or approved > now or expires <= now
        or ttl <= 0 or ttl > float(slo["exceptions"]["max_ttl_days"])
    ):
        fail("exception is unapproved, expired, duplicated, or over TTL")
    valid_exception_ids.add(exception["id"])

effective = []
violation_ids = set()
for violation in report["violations"]:
    if not isinstance(violation, dict) or set(violation) != {
        "id", "severity", "exception_id",
    }:
        fail("violation schema differs")
    if (
        not violation["id"] or violation["id"] in violation_ids
        or violation["severity"] not in {"critical", "high", "medium", "low"}
    ):
        fail("violation identity or severity is invalid")
    violation_ids.add(violation["id"])
    exception_id = violation["exception_id"]
    if exception_id is not None and exception_id not in valid_exception_ids:
        fail("violation references an invalid exception")
    if exception_id is None:
        effective.append(violation)

counts = {
    severity: sum(item["severity"] == severity for item in effective)
    for severity in ("critical", "high", "medium")
}
limits = slo["violations"]
if counts["critical"] != limits["critical"]:
    fail("critical violation threshold exceeded")
if counts["high"] != limits["high"]:
    fail("high violation threshold exceeded")
if counts["medium"] > limits["medium_max"]:
    fail("medium violation threshold exceeded")
print("SLO OK")

Action: Sign the complete report after generation, then run both python tools/check_slo.py and python -O tools/check_slo.py in CI. Both invocations must produce the same result. Signature failures, malformed reports, incomplete coverage, and invalid exceptions are blocking errors; approved exceptions must carry an owner, approval authority, and bounded expiry.

AID-M-005.002-G003

Produce and sign a Baseline Manifest to ensure versioning and immutability.
Concept

Generate the baseline manifest from verified report bytes; do not hand-type hashes, violation counts, or exception dates. Scanner-specific adapters first normalize their output and the ruleset into one signed release-input document. This guidance owns the aggregate manifest and its binding, not the scanners' internal verification.

Build from the signed release-input population

The release-input schema contains baseline_version, build_digest, control_mappings, one ruleset entry, a policy-owned non-empty required_report_kinds population, exactly one report for each required kind, and approved exceptions. Every file entry has a regular path and lowercase sha256. Each normalized report has schema aidefend.posture-report.v1, the same build digest, and exact critical/high/medium/low violation counts.

# File: governance/build_baseline_manifest.py
from __future__ import annotations

import hashlib
import json
import os
import re
from datetime import datetime, timezone
from pathlib import Path

SHA256 = re.compile(r"^[0-9a-f]{64}$")
SEVERITIES = ("critical", "high", "medium", "low")


def unique_object(pairs):
    value = {}
    for key, item in pairs:
        if key in value:
            raise ValueError(f"duplicate JSON key: {key}")
        value[key] = item
    return value


def regular_bytes(path: Path) -> bytes:
    if path.is_symlink() or not path.is_file():
        raise ValueError(f"baseline input is not a regular file: {path}")
    raw = path.read_bytes()
    if not raw:
        raise ValueError(f"baseline input is empty: {path}")
    return raw


def checked_entry(entry: dict, *, expected_keys: set[str]) -> tuple[dict, bytes]:
    if not isinstance(entry, dict) or set(entry) != expected_keys:
        raise ValueError("baseline file-entry schema differs")
    if not SHA256.fullmatch(str(entry["sha256"])):
        raise ValueError("baseline file-entry digest is invalid")
    raw = regular_bytes(Path(entry["path"]))
    if hashlib.sha256(raw).hexdigest() != entry["sha256"]:
        raise ValueError(f"baseline file-entry digest differs: {entry['path']}")
    return entry, raw


source_path = Path(os.environ["BASELINE_RELEASE_INPUTS"])
source_raw = regular_bytes(source_path)
source = json.loads(
    source_raw.decode("utf-8", errors="strict"),
    object_pairs_hook=unique_object,
)
required = {
    "schema_version", "baseline_version", "build_digest",
    "control_mappings", "ruleset", "required_report_kinds",
    "reports", "exceptions",
}
if not isinstance(source, dict) or set(source) != required:
    raise ValueError("baseline release-input fields differ")
if source["schema_version"] != "aidefend.baseline-release-inputs.v1":
    raise ValueError("baseline release-input schema differs")
if not SHA256.fullmatch(str(source["build_digest"])):
    raise ValueError("build digest is invalid")
if not isinstance(source["control_mappings"], dict) or not source["control_mappings"]:
    raise ValueError("control mapping population is empty")
required_report_kinds = source["required_report_kinds"]
if (
    not isinstance(required_report_kinds, list)
    or not required_report_kinds
    or any(
        not isinstance(kind, str) or not kind.strip()
        for kind in required_report_kinds
    )
    or len(required_report_kinds) != len(set(required_report_kinds))
):
    raise ValueError("required report-kind population is invalid")

ruleset, _ruleset_raw = checked_entry(
    source["ruleset"],
    expected_keys={"kind", "path", "sha256", "policy_version"},
)
if ruleset["kind"] != "ruleset_manifest" or not ruleset["policy_version"]:
    raise ValueError("ruleset binding is invalid")

reports = source["reports"]
if not isinstance(reports, list) or not reports:
    raise ValueError("report population is empty")
totals = {severity: 0 for severity in SEVERITIES}
bound_reports = []
seen_report_kinds = set()
for entry in reports:
    entry, raw = checked_entry(
        entry,
        expected_keys={"kind", "path", "sha256", "adapter_version"},
    )
    if (
        not isinstance(entry["kind"], str)
        or not entry["kind"].strip()
        or entry["kind"] in seen_report_kinds
    ):
        raise ValueError("report kind is empty or duplicated")
    seen_report_kinds.add(entry["kind"])
    report = json.loads(raw.decode("utf-8", errors="strict"), object_pairs_hook=unique_object)
    if set(report) != {"schema_version", "build_digest", "violations"}:
        raise ValueError(f"normalized posture report schema differs: {entry['kind']}")
    if (
        report["schema_version"] != "aidefend.posture-report.v1"
        or report["build_digest"] != source["build_digest"]
        or not isinstance(report["violations"], dict)
        or set(report["violations"]) != set(SEVERITIES)
    ):
        raise ValueError(f"normalized posture report binding differs: {entry['kind']}")
    for severity in SEVERITIES:
        count = report["violations"][severity]
        if isinstance(count, bool) or not isinstance(count, int) or count < 0:
            raise ValueError(f"invalid violation count: {entry['kind']}/{severity}")
        totals[severity] += count
    bound_reports.append(entry)

if seen_report_kinds != set(required_report_kinds):
    missing = sorted(set(required_report_kinds) - seen_report_kinds)
    unexpected = sorted(seen_report_kinds - set(required_report_kinds))
    raise ValueError(
        f"report-kind population differs; missing={missing}, unexpected={unexpected}"
    )
report_population_sha256 = hashlib.sha256(json.dumps(
    sorted(seen_report_kinds),
    separators=(",", ":"),
    ensure_ascii=False,
).encode("utf-8")).hexdigest()

now = datetime.now(timezone.utc)
exceptions = source["exceptions"]
if not isinstance(exceptions, list):
    raise ValueError("exception population is invalid")
seen_exception_ids = set()
for exception in exceptions:
    if not isinstance(exception, dict) or set(exception) != {
        "id", "rule", "owner", "expires_at", "approval_sha256",
    }:
        raise ValueError("exception schema differs")
    if (
        any(not isinstance(exception[key], str) or not exception[key] for key in ("id", "rule", "owner"))
        or exception["id"] in seen_exception_ids
        or not SHA256.fullmatch(str(exception["approval_sha256"]))
    ):
        raise ValueError("exception identity or approval binding is invalid")
    seen_exception_ids.add(exception["id"])
    expiry = datetime.fromisoformat(exception["expires_at"].replace("Z", "+00:00"))
    if expiry.tzinfo is None or expiry <= now:
        raise ValueError(f"exception is expired: {exception['id']}")

manifest = {
    "schema_version": "aidefend.baseline-manifest.v1",
    "baseline_version": source["baseline_version"],
    "build_digest": source["build_digest"],
    "release_inputs_sha256": hashlib.sha256(source_raw).hexdigest(),
    "ruleset": ruleset,
    "control_mappings": source["control_mappings"],
    "required_report_kinds": required_report_kinds,
    "report_population_sha256": report_population_sha256,
    "violations": totals,
    "exceptions": exceptions,
    "reports": bound_reports,
}
output = Path("out/baseline_manifest.json")
output.parent.mkdir(parents=True, exist_ok=True)
output.write_text(json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8")
if json.loads(output.read_text(encoding="utf-8")) != manifest:
    raise RuntimeError("baseline manifest readback differs")
Verify the input snapshot, sign the output, and replay every digest
# File: governance/publish_baseline_manifest.sh
#!/usr/bin/env bash
set -euo pipefail
umask 077

snapshot_root="$(mktemp -d)"
trap 'rm -rf -- "$snapshot_root"' EXIT
install -m 0400 -- "$BASELINE_RELEASE_INPUTS" "$snapshot_root/release-inputs.json"
install -m 0400 -- "$BASELINE_RELEASE_INPUTS_BUNDLE" "$snapshot_root/release-inputs.sigstore.json"
cosign verify-blob --key "$BASELINE_RELEASE_INPUTS_VERIFY_KEY"   --bundle "$snapshot_root/release-inputs.sigstore.json"   "$snapshot_root/release-inputs.json" >/dev/null

export BASELINE_RELEASE_INPUTS="$snapshot_root/release-inputs.json"
python governance/build_baseline_manifest.py
cosign sign-blob --yes --key "$BASELINE_MANIFEST_SIGNING_KEY"   --bundle out/baseline_manifest.sigstore.json out/baseline_manifest.json
cosign verify-blob --key "$BASELINE_MANIFEST_VERIFY_KEY"   --bundle out/baseline_manifest.sigstore.json out/baseline_manifest.json >/dev/null

jq -e '.schema_version == "aidefend.baseline-manifest.v1"'   out/baseline_manifest.json >/dev/null
while IFS=$'\t' read -r path expected; do
  observed="$(sha256sum -- "$path" | awk '{print $1}')"
  [[ "$observed" == "$expected" ]] || exit 1
done < <(jq -r '
  [.ruleset, .reports[]] | .[] | [.path, .sha256] | @tsv
' out/baseline_manifest.json)

Action: Promotion must reconcile report kinds exactly against the policy-owned required population and reject an expired exception, missing, unexpected, or duplicate report, report/build mismatch, stale ruleset, digest mismatch, invalid signature, or incomplete replay. Store the verified manifest and bundle under the release's immutable build digest.

Open-source tools

Commercial / hosted tools

Threat mappings

MITRE ATLAS

MAESTRO

OWASP LLM Top 10 2026

OWASP ML Top 10 2023

OWASP Top 10 for Agentic Applications 2026

NIST Adversarial Machine Learning 2025

Cisco Integrated AI Security and Safety Framework

Google Secure AI Framework 2.0 - Risks

Databricks AI Security Framework 3.0

AID-M-005.002: Policy-as-Code Configuration Baselines & Posture Release Gates | AIDEFEND

Loading interactive view…

A Practical Knowledge Base for AI Security Defenses

Version