feat: add PolicyPackSelectorComponent with tests and integration

- Implemented PolicyPackSelectorComponent for selecting policy packs.
- Added unit tests for component behavior, including API success and error handling.
- Introduced monaco-workers type declarations for editor workers.
- Created acceptance tests for guardrails with stubs for AT1–AT10.
- Established SCA Failure Catalogue Fixtures for regression testing.
- Developed plugin determinism harness with stubs for PL1–PL10.
- Added scripts for evidence upload and verification processes.
This commit is contained in:
StellaOps Bot
2025-12-05 21:24:34 +02:00
parent 347c88342c
commit 18d87c64c5
220 changed files with 7700 additions and 518 deletions

View File

@@ -0,0 +1,49 @@
{
"schemaVersion": "stellaops.pack.offline-bundle.v1",
"pack": {
"name": "demo-pack",
"version": "1.0.0",
"bundle": "packs/demo-pack.tgz",
"digest": "sha256:c0ffee0000000000000000000000000000000000000000000000000000000000",
"registry": "registry.local/demo/demo-pack:1.0.0",
"sbom": "sbom.json"
},
"plan": {
"hashAlgorithm": "sha256",
"hash": "sha256:1111111111111111111111111111111111111111111111111111111111111111",
"canonicalPlanPath": "canonical-plan.json",
"inputsLock": "inputs.lock",
"rngSeed": "seed-1111",
"timestampSource": "utc-iso8601"
},
"evidence": {
"attestation": "attestation.dsse",
"approvalsLedger": "approvals-ledger.dsse"
},
"security": {
"sandbox": {
"mode": "sealed",
"egressAllowlist": [],
"cpuLimitMillicores": 250,
"memoryLimitMiB": 256
},
"revocations": "revocations.json",
"signatures": {
"bundleDsse": "bundle.dsse",
"attestationDsse": "attestation.dsse.sig",
"registryCertChain": "certs.pem"
},
"secretsRedactionPolicy": "redaction-policy.json"
},
"hashes": [],
"slo": {
"runP95Seconds": 300,
"approvalP95Seconds": 900,
"maxQueueDepth": 1000,
"alertRules": "alerts.yaml"
},
"tenant": "demo-tenant",
"environment": "dev",
"created": "2025-12-05T00:00:00Z",
"verifyScriptVersion": "local-fixture"
}

View File

@@ -0,0 +1,13 @@
{
"schemaVersion": "stellaops.pack.approval-ledger.v1",
"runId": "run-1",
"gateId": "security-review",
"planHash": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
"decision": "approved",
"decidedAt": "2025-12-05T00:00:00Z",
"tenantId": "demo-tenant",
"approver": {
"id": "approver@example.com",
"summary": "LGTM"
}
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,104 @@
{
"schemaVersion": "stellaops.pack.offline-bundle.v1",
"pack": {
"name": "demo-pack",
"version": "1.0.0",
"bundle": "packs/demo-pack.tgz",
"digest": "sha256:c0ffee0000000000000000000000000000000000000000000000000000000000",
"registry": "registry.local/demo/demo-pack:1.0.0",
"sbom": "sbom.json"
},
"plan": {
"hashAlgorithm": "sha256",
"hash": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356",
"canonicalPlanPath": "canonical-plan.json",
"inputsLock": "inputs.lock",
"rngSeed": "seed-1111",
"timestampSource": "utc-iso8601"
},
"evidence": {
"attestation": "attestation.dsse",
"approvalsLedger": "approvals-ledger.dsse",
"timeline": "timeline.ndjson"
},
"security": {
"sandbox": {
"mode": "sealed",
"egressAllowlist": [],
"cpuLimitMillicores": 250,
"memoryLimitMiB": 256,
"quotaSeconds": 120
},
"revocations": "revocations.json",
"signatures": {
"bundleDsse": "bundle.dsse",
"attestationDsse": "attestation.dsse.sig",
"registryCertChain": "certs.pem"
},
"secretsRedactionPolicy": "redaction-policy.json"
},
"hashes": [
{
"path": "canonical-plan.json",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "inputs.lock",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "sbom.json",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "attestation.dsse",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "approvals-ledger.dsse",
"algorithm": "sha256",
"digest": "sha256:2018f79642928cedd3b3716637b075d4d8374cc8997f58e00dd4fbf5addcea56"
},
{
"path": "revocations.json",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "bundle.dsse",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "attestation.dsse.sig",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "redaction-policy.json",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
},
{
"path": "packs/demo-pack.tgz",
"algorithm": "sha256",
"digest": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
}
],
"slo": {
"runP95Seconds": 300,
"approvalP95Seconds": 900,
"maxQueueDepth": 1000,
"alertRules": "alerts.yaml"
},
"tenant": "demo-tenant",
"environment": "dev",
"created": "2025-12-05T00:00:00Z",
"expires": "2026-01-05T00:00:00Z",
"verifyScriptVersion": "local-fixture",
"hash": "sha256:ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356"
}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,10 @@
canonical-plan.json
inputs.lock
sbom.json
attestation.dsse
approvals-ledger.dsse
revocations.json
bundle.dsse
attestation.dsse.sig
redaction-policy.json
packs/demo-pack.tgz

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
root_dir=$(cd "$(dirname "$0")/.." && pwd)
verifier="$root_dir/packs/verify_offline_bundle.py"
python3 "$verifier" --bundle "$root_dir/packs/__fixtures__/good" --manifest bundle.json --require-dsse
python3 "$verifier" --bundle "$root_dir/packs/__fixtures__/bad" --manifest bundle-missing-quota.json --require-dsse && exit 1 || true
echo "fixture checks completed"

View File

@@ -0,0 +1,180 @@
#!/usr/bin/env python3
import json
import tempfile
import unittest
from pathlib import Path
import runpy
_VERIFIER_PATH = Path(__file__).parent / "verify_offline_bundle.py"
_mod = runpy.run_path(_VERIFIER_PATH.as_posix(), run_name="verify_offline_bundle")
BundleReader = _mod["BundleReader"]
validate_manifest = _mod["validate_manifest"]
verify_files = _mod["verify_files"]
verify_hashes = _mod["verify_hashes"]
sha256_digest = _mod["sha256_digest"]
def _write(path: Path, content: str) -> str:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8")
return sha256_digest(content.encode("utf-8"))
class VerifyOfflineBundleTests(unittest.TestCase):
def setUp(self) -> None:
self.tmp = tempfile.TemporaryDirectory()
self.root = Path(self.tmp.name)
def tearDown(self) -> None:
self.tmp.cleanup()
def _build_manifest(self) -> Path:
plan_hash = _write(self.root / "canonical-plan.json", '{"steps":[]}')
inputs_lock_hash = _write(self.root / "inputs.lock", '{"inputs":{}}')
sbom_hash = _write(self.root / "sbom.json", '{"bom":"demo"}')
attest_hash = _write(self.root / "attestation.dsse", "attestation")
approvals_ledger = json.dumps(
{
"schemaVersion": "stellaops.pack.approval-ledger.v1",
"runId": "run-1",
"gateId": "security-review",
"planHash": plan_hash,
"decision": "approved",
"decidedAt": "2025-12-05T00:00:00Z",
"tenantId": "demo-tenant",
"approver": {"id": "approver@example.com", "summary": "LGTM"},
}
)
approvals_hash = _write(self.root / "approvals-ledger.dsse", approvals_ledger)
revocations_hash = _write(self.root / "revocations.json", '{"revoked":false}')
bundle_dsse_hash = _write(self.root / "bundle.dsse", "bundle-dsse")
att_dsse_hash = _write(self.root / "attestation.dsse.sig", "att-dsse")
redaction_hash = _write(self.root / "redaction-policy.json", '{"mode":"hash"}')
pack_blob_hash = _write(self.root / "packs/my-pack.tgz", "dummy pack")
manifest = {
"schemaVersion": "stellaops.pack.offline-bundle.v1",
"pack": {
"name": "demo-pack",
"version": "1.0.0",
"bundle": "packs/my-pack.tgz",
"digest": pack_blob_hash,
"registry": "demo.local/pack/demo:1.0.0",
"sbom": "sbom.json",
},
"plan": {
"hashAlgorithm": "sha256",
"hash": plan_hash,
"canonicalPlanPath": "canonical-plan.json",
"inputsLock": "inputs.lock",
"rngSeed": "rng-demo",
"timestampSource": "utc-iso8601",
},
"evidence": {
"attestation": "attestation.dsse",
"approvalsLedger": "approvals-ledger.dsse",
"timeline": "timeline.ndjson",
},
"security": {
"sandbox": {
"mode": "sealed",
"egressAllowlist": [],
"cpuLimitMillicores": 250,
"memoryLimitMiB": 256,
"quotaSeconds": 120,
},
"revocations": "revocations.json",
"signatures": {
"bundleDsse": "bundle.dsse",
"attestationDsse": "attestation.dsse.sig",
"registryCertChain": "certs.pem",
},
"secretsRedactionPolicy": "redaction-policy.json",
},
"hashes": [
{"path": "canonical-plan.json", "algorithm": "sha256", "digest": plan_hash},
{"path": "inputs.lock", "algorithm": "sha256", "digest": inputs_lock_hash},
{"path": "sbom.json", "algorithm": "sha256", "digest": sbom_hash},
{"path": "attestation.dsse", "algorithm": "sha256", "digest": attest_hash},
{"path": "approvals-ledger.dsse", "algorithm": "sha256", "digest": approvals_hash},
{"path": "revocations.json", "algorithm": "sha256", "digest": revocations_hash},
{"path": "bundle.dsse", "algorithm": "sha256", "digest": bundle_dsse_hash},
{"path": "attestation.dsse.sig", "algorithm": "sha256", "digest": att_dsse_hash},
{"path": "redaction-policy.json", "algorithm": "sha256", "digest": redaction_hash},
{"path": "packs/my-pack.tgz", "algorithm": "sha256", "digest": pack_blob_hash},
],
"slo": {
"runP95Seconds": 300,
"approvalP95Seconds": 900,
"maxQueueDepth": 1000,
"alertRules": "alerts.yaml",
},
"tenant": "demo-tenant",
"environment": "dev",
"created": "2025-12-05T00:00:00Z",
"expires": "2026-01-05T00:00:00Z",
"verifyScriptVersion": "local-test",
}
manifest_path = self.root / "bundle.json"
manifest_path.write_text(json.dumps(manifest, indent=2), encoding="utf-8")
return manifest_path
def test_good_bundle_passes(self):
manifest_path = self._build_manifest()
reader = BundleReader(self.root.as_posix())
manifest = json.loads(manifest_path.read_text())
errors = []
errors.extend(validate_manifest(manifest))
errors.extend(verify_files(reader, manifest, require_dsse=True))
errors.extend(verify_hashes(reader, manifest))
self.assertFalse(errors, f"Expected no validation errors, got: {errors}")
def test_missing_hash_fails(self):
manifest_path = self._build_manifest()
manifest = json.loads(manifest_path.read_text())
# Corrupt a hash to force a failure.
manifest["hashes"][0]["digest"] = "sha256:" + "0" * 64
reader = BundleReader(self.root.as_posix())
errors = verify_hashes(reader, manifest)
self.assertTrue(errors, "Expected hash verification to fail when hash entry is missing")
def test_missing_quota_fails(self):
manifest_path = self._build_manifest()
manifest = json.loads(manifest_path.read_text())
del manifest["security"]["sandbox"]["quotaSeconds"]
reader = BundleReader(self.root.as_posix())
errors = []
errors.extend(validate_manifest(manifest))
errors.extend(verify_files(reader, manifest, require_dsse=True))
errors.extend(verify_hashes(reader, manifest))
self.assertTrue(
any(err.path == "security.sandbox.quotaSeconds" for err in errors),
"Expected quotaSeconds validation failure"
)
def test_invalid_approval_ledger_plan_hash_fails(self):
manifest_path = self._build_manifest()
manifest = json.loads(manifest_path.read_text())
ledger_path = self.root / "approvals-ledger.dsse"
ledger = json.loads(ledger_path.read_text())
ledger["planHash"] = "not-a-digest"
ledger_path.write_text(json.dumps(ledger), encoding="utf-8")
reader = BundleReader(self.root.as_posix())
errors = []
errors.extend(validate_manifest(manifest))
errors.extend(verify_files(reader, manifest, require_dsse=True))
errors.extend(verify_hashes(reader, manifest))
self.assertTrue(
any(err.path.startswith("approvalsLedger.planHash") for err in errors),
"Expected approval ledger plan hash validation failure"
)
if __name__ == "__main__":
unittest.main()

View File

@@ -20,6 +20,8 @@ import json
import os
import sys
import tarfile
import re
from pathlib import Path
from dataclasses import dataclass
from typing import Dict, Iterable, List, Optional
@@ -37,7 +39,9 @@ class BundleReader:
def __init__(self, bundle_path: str):
self.bundle_path = bundle_path
self._tar: Optional[tarfile.TarFile] = None
if tarfile.is_tarfile(bundle_path):
if os.path.isdir(bundle_path):
self._tar = None
elif tarfile.is_tarfile(bundle_path):
self._tar = tarfile.open(bundle_path, mode="r:*")
def exists(self, path: str) -> bool:
@@ -73,9 +77,14 @@ def parse_args() -> argparse.Namespace:
)
parser.add_argument(
"--bundle",
required=True,
required=False,
help="Path to bundle directory or tarball containing bundle manifest + artefacts.",
)
parser.add_argument(
"--fixture",
choices=["good", "bad"],
help="If set, uses built-in fixtures under scripts/packs/__fixtures__/ for quick checks.",
)
parser.add_argument(
"--manifest",
default="bundle.json",
@@ -154,6 +163,7 @@ def validate_manifest(manifest: Dict) -> List[ValidationError]:
for quota_field in [
("security.sandbox.cpuLimitMillicores", sandbox.get("cpuLimitMillicores")),
("security.sandbox.memoryLimitMiB", sandbox.get("memoryLimitMiB")),
("security.sandbox.quotaSeconds", sandbox.get("quotaSeconds")),
]:
field, value = quota_field
if value is None or not isinstance(value, (int, float)) or value <= 0:
@@ -241,12 +251,40 @@ def verify_files(reader: BundleReader, manifest: Dict, require_dsse: bool) -> Li
f"hash mismatch (expected {expected_plan_hash}, got {actual_plan_hash})",
)
)
approvals_path = manifest.get("evidence", {}).get("approvalsLedger")
if approvals_path and reader.exists(approvals_path):
try:
approval_doc = json.loads(reader.read_bytes(approvals_path))
errors.extend(validate_approval_ledger(approval_doc, manifest.get("plan", {}).get("hash")))
except Exception as exc: # broad but scoped to ledger parse
errors.append(ValidationError("evidence.approvalsLedger", f"failed to parse ledger JSON: {exc}"))
return errors
def validate_approval_ledger(doc: Dict, expected_plan_hash: Optional[str]) -> List[ValidationError]:
errors: List[ValidationError] = []
if doc.get("schemaVersion") != "stellaops.pack.approval-ledger.v1":
errors.append(ValidationError("approvalsLedger.schemaVersion", "must be stellaops.pack.approval-ledger.v1"))
for field in ["runId", "gateId", "tenantId", "decision", "planHash", "decidedAt"]:
if not doc.get(field):
errors.append(ValidationError(f"approvalsLedger.{field}", "is required"))
plan_hash = doc.get("planHash")
if plan_hash and not re.match(r"^sha256:[0-9a-f]{64}$", plan_hash, re.IGNORECASE):
errors.append(ValidationError("approvalsLedger.planHash", "must be sha256:<64-hex>"))
if expected_plan_hash and plan_hash and plan_hash.lower() != expected_plan_hash.lower():
errors.append(ValidationError("approvalsLedger.planHash", "must match manifest.plan.hash"))
if doc.get("decision") not in {"approved", "rejected", "expired"}:
errors.append(ValidationError("approvalsLedger.decision", "must be approved|rejected|expired"))
return errors
def main() -> int:
args = parse_args()
reader = BundleReader(args.bundle)
bundle_path = args.bundle
if args.fixture:
bundle_path = Path(__file__).parent / "__fixtures__" / args.fixture
reader = BundleReader(bundle_path.as_posix() if isinstance(bundle_path, Path) else bundle_path)
try:
manifest = load_manifest(reader, args.manifest)
errors: List[ValidationError] = []