Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f844feab7f | |||
| 8c68d683c6 | |||
| be967783b4 | |||
| 730109dd0c | |||
| 78688b968c | |||
| 7e98debd70 | |||
| 255cde5002 | |||
| 2cc76f4f94 | |||
| 9acf23926d |
+22
-11
@@ -1,22 +1,33 @@
|
||||
{
|
||||
"phase": 3,
|
||||
"phase": 5,
|
||||
"stage": "complete",
|
||||
"milestone": "v1.25",
|
||||
"phase_role": "execution",
|
||||
"phase_role": "final",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-12T17:30:00Z",
|
||||
"updated_at": "2026-08-12T18:00:00Z",
|
||||
"project": "acdl",
|
||||
"milestone_complete": false,
|
||||
"milestone_complete": true,
|
||||
"tag_line": "v1.24.x",
|
||||
"tag": "v1.24.3",
|
||||
"next_tag": "v1.24.4",
|
||||
"tag": "v1.24.5",
|
||||
"release": {
|
||||
"forge": "gitea",
|
||||
"releases_created": true,
|
||||
"release_ids": {"v1.24.0": 640, "v1.24.1": 641, "v1.24.2": 642, "v1.24.3": 643},
|
||||
"phase_release_id": 643
|
||||
"release_ids": {
|
||||
"v1.24.0": 640,
|
||||
"v1.24.1": 641,
|
||||
"v1.24.2": 642,
|
||||
"v1.24.3": 643,
|
||||
"v1.24.4": 644,
|
||||
"v1.24.5": 645
|
||||
},
|
||||
"milestone_release_id": 645,
|
||||
"milestone_release_tag": "v1.24.5"
|
||||
},
|
||||
"requirements": ["REQ-291", "REQ-292", "REQ-293", "REQ-294", "REQ-295", "REQ-296", "REQ-297", "REQ-298", "REQ-299", "REQ-300", "REQ-301", "REQ-302", "REQ-303", "REQ-308", "REQ-309"],
|
||||
"tests": {"total": 88, "passed": 88, "skipped": 11, "failed": 0},
|
||||
"notes": "v1.25 P3 (plan-JSON+meta+pipeline) complete. Tag v1.24.3 (gitea release id 643). 4 requirements (REQ-300..303). 5 plan-JSON+meta policies. run_platform.sh Step 5b wired. Phase 03 branch deleted. Next: P4 regression-gate policies + docs."
|
||||
"requirements": ["REQ-291", "REQ-292", "REQ-293", "REQ-294", "REQ-295", "REQ-296", "REQ-297", "REQ-298", "REQ-299", "REQ-300", "REQ-301", "REQ-302", "REQ-303", "REQ-304", "REQ-305", "REQ-306", "REQ-307", "REQ-308", "REQ-309"],
|
||||
"requirements_covered": 19,
|
||||
"requirements_partial": 0,
|
||||
"tests": {"total": 170, "passed": 170, "skipped": 23, "failed": 0, "preexisting_flaky": "test_metrics_emitters.py::test_attestation_event_emission (fails on main, unrelated to v1.25)"},
|
||||
"phases": {"P0": "complete", "P1": "complete", "P2": "complete", "P3": "complete", "P4": "complete", "P5": "complete"},
|
||||
"review": {"p0_fixed": 1, "p1_fixed": 3, "p1_flagged_posthoc": 2, "escalations": 0},
|
||||
"notes": "v1.25 milestone complete. Tag v1.24.5 (milestone release, gitea id 645). 19 requirements complete (REQ-291..309). 6 phases. 170 tests pass (23 skip-without-kj). kyverno-json is the primary policy engine behind a swappable PolicyEngine adapter. Merged milestone/v1.25-kyverno-json to main. All milestone branches deleted. Next milestone starts fresh."
|
||||
}
|
||||
+13
-1
@@ -229,4 +229,16 @@ their AI engineering teams reach for first when an agent needs to deploy.
|
||||
RESEARCH.md/ARCHITECTURE.md. It is the *how*; this file is the *why*.
|
||||
- **Pillar C (story):** the unified narrative deck proves Pillars A+B to
|
||||
leadership. The deck's Proof section cites grounded metrics; its
|
||||
Roadmap section cites deferred targets honestly.
|
||||
Roadmap section cites deferred targets honestly.
|
||||
|
||||
## v1.25 update — swappable policy-engine substrate
|
||||
|
||||
Strategic Objective #2 (provable trust) gained a concrete substrate in
|
||||
v1.25: the policy engine that produces the `PolicyCheckResult` records
|
||||
feeding the confidence signal is now **swappable** via the
|
||||
`PolicyEngine` protocol (`core/policy_engine.py`). `kyverno-json` is
|
||||
the v1.25 default; `OPA` (or any other engine) can replace it by
|
||||
implementing the same 3-method protocol — without touching the
|
||||
confidence signal, the PCR schema, or the pipeline. See
|
||||
ARCHITECTURE.md §12.7. The trust moat is a *replaceable* engine, not a
|
||||
vendor lock-in.
|
||||
+19
-19
@@ -2469,22 +2469,22 @@ assert 20 main + 1 appendix.
|
||||
|
||||
| REQ | Phase | Status |
|
||||
|-----|-------|--------|
|
||||
| REQ-291 | P1 | pending |
|
||||
| REQ-292 | P1 | pending |
|
||||
| REQ-293 | P1 | pending |
|
||||
| REQ-294 | P1 | pending |
|
||||
| REQ-295 | P2 | pending |
|
||||
| REQ-296 | P2 | pending |
|
||||
| REQ-297 | P2 | pending |
|
||||
| REQ-298 | P2 | pending |
|
||||
| REQ-299 | P2 | pending |
|
||||
| REQ-300 | P3 | pending |
|
||||
| REQ-301 | P3 | pending |
|
||||
| REQ-302 | P3 | pending |
|
||||
| REQ-303 | P3 | pending |
|
||||
| REQ-304 | P4 | pending |
|
||||
| REQ-305 | P4 | pending |
|
||||
| REQ-306 | P4 | pending |
|
||||
| REQ-307 | P4 | pending |
|
||||
| REQ-308 | P1 | pending |
|
||||
| REQ-309 | P1 | pending |
|
||||
| REQ-291 | P1 | complete |
|
||||
| REQ-292 | P1 | complete |
|
||||
| REQ-293 | P1 | complete |
|
||||
| REQ-294 | P1 | complete |
|
||||
| REQ-295 | P2 | complete |
|
||||
| REQ-296 | P2 | complete |
|
||||
| REQ-297 | P2 | complete |
|
||||
| REQ-298 | P2 | complete |
|
||||
| REQ-299 | P2 | complete |
|
||||
| REQ-300 | P3 | complete |
|
||||
| REQ-301 | P3 | complete |
|
||||
| REQ-302 | P3 | complete |
|
||||
| REQ-303 | P3 | complete |
|
||||
| REQ-304 | P4 | complete |
|
||||
| REQ-305 | P4 | complete |
|
||||
| REQ-306 | P4 | complete |
|
||||
| REQ-307 | P4 | complete |
|
||||
| REQ-308 | P1 | complete |
|
||||
| REQ-309 | P1 | complete |
|
||||
|
||||
+1
-1
@@ -2157,7 +2157,7 @@ release). **DONE.**
|
||||
`milestone/v1.23-deck-cleanup-python-pptx` → `main`.
|
||||
- **Requirements:** REQ-263..275 (13 requirements).
|
||||
|
||||
## v1.25 (active, tag line `v1.24.x`): kyverno-json Unified Policy Engine
|
||||
## v1.25 (complete, tag `v1.24.5`): kyverno-json Unified Policy Engine
|
||||
|
||||
`kyverno-json` — a Kyverno-ecosystem runtime that applies Kyverno policies
|
||||
to **any** JSON/YAML payload — becomes Nova's **primary compliance /
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
},
|
||||
"git": {
|
||||
"branching_strategy": "flat",
|
||||
"_branching_strategy_note": "ACDL uses flat workflow (committed directly to main per established convention since v1.0). The 'phase' strategy is advisory; CIAgent uses milestone/phase branches for v1.14 but the project convention is flat.",
|
||||
"_branching_strategy_note": "Nova uses flat workflow (committed directly to main per established convention since v1.0; renamed ACDL→Nova in v1.15). The 'phase' strategy is advisory; CIAgent uses milestone/phase branches for v1.14 but the project convention is flat.",
|
||||
"auto_commit": true,
|
||||
"auto_push": true
|
||||
},
|
||||
@@ -67,7 +67,7 @@
|
||||
"sources": [".env", ".env.secrets", ".env.*"],
|
||||
"disallow": ["shell_env", "netrc", "keychain", "rc_files", "global_config"],
|
||||
"scopes": {
|
||||
"gitea": "ACDL_GITEA_TOKEN",
|
||||
"gitea": "NOVA_GITEA_TOKEN",
|
||||
"github": "GITHUB_TOKEN",
|
||||
"gitlab": "GITLAB_TOKEN",
|
||||
"openai": "OPENAI_API_KEY",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"id": "{{ to_string(@) }}"
|
||||
"id": "(regex_match('^[a-z][a-z0-9-]{2,5}$', @))"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
+45
-2
@@ -533,7 +533,7 @@ if command -v kj >/dev/null 2>&1; then
|
||||
if [ -f "$TF_DIR/tfplan" ]; then
|
||||
terraform -chdir="$TF_DIR" show -json tfplan > "$WORK/tfshow.json" 2>/dev/null || true
|
||||
if [ -s "$WORK/tfshow.json" ]; then
|
||||
python3 - <<'PY' > "$WORK/kj-pcr.json" 2>"$WORK/kj.err" || echo "[]"
|
||||
python3 - "$WORK/tfshow.json" "$CONTRACT_ID" <<'PY' > "$WORK/kj-pcr.json" 2>"$WORK/kj.err" || echo "[]"
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, ".")
|
||||
@@ -541,10 +541,11 @@ import importlib.util
|
||||
_spec = importlib.util.spec_from_file_location("kj_engine", "adapters/kyverno-json/kyverno_json_engine.py")
|
||||
_mod = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_mod)
|
||||
_payload_path, _contract_id = sys.argv[1], sys.argv[2]
|
||||
eng = _mod.KyvernoJsonEngine()
|
||||
if not eng.is_configured():
|
||||
print("[]"); sys.exit(0)
|
||||
out = eng.evaluate(json.load(open("$WORK/tfshow.json")), Path("adapters/kyverno-json/policies/plan-json"), "$CONTRACT_ID")
|
||||
out = eng.evaluate(json.load(open(_payload_path)), Path("adapters/kyverno-json/policies/plan-json"), _contract_id)
|
||||
print(json.dumps(out))
|
||||
PY
|
||||
if [ -s "$WORK/kj-pcr.json" ]; then
|
||||
@@ -571,6 +572,48 @@ else
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ============================================================================
|
||||
# Step 5c: kyverno-json meta-policies over the merged PCR list (v1.25, REQ-303)
|
||||
# ============================================================================
|
||||
# After Step 5b merges the Checkov/Wiz + kj plan-JSON PCRs into pcr.json, run
|
||||
# the meta-policies (block-on-any-critical, tagging-rules-agree) over the
|
||||
# merged list. The meta-policy PCRs are appended to pcr.json before the
|
||||
# confidence signal runs. The confidence_signal.py PENALTY["critical"]: None
|
||||
# hard-override stays as defense-in-depth behind this declarative rule
|
||||
# (D-119). Skips gracefully when kj is absent (D-120).
|
||||
if command -v kj >/dev/null 2>&1 && [ -s "$WORK/pcr.json" ]; then
|
||||
echo "=== Step 5c: kyverno-json meta-policies over the merged PCR list ==="
|
||||
python3 - "$WORK/pcr.json" "$CONTRACT_ID" <<'PY' > "$WORK/meta-pcr.json" 2>"$WORK/meta.err" || echo "[]"
|
||||
import json, sys
|
||||
from pathlib import Path
|
||||
sys.path.insert(0, ".")
|
||||
import importlib.util
|
||||
_spec = importlib.util.spec_from_file_location("kj_engine", "adapters/kyverno-json/kyverno_json_engine.py")
|
||||
_mod = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_mod)
|
||||
eng = _mod.KyvernoJsonEngine()
|
||||
if not eng.is_configured():
|
||||
print("[]"); sys.exit(0)
|
||||
pcrs = json.load(open(sys.argv[1]))
|
||||
out = eng.evaluate(pcrs, Path("adapters/kyverno-json/policies/meta"), sys.argv[2])
|
||||
print(json.dumps(out))
|
||||
PY
|
||||
if [ -s "$WORK/meta-pcr.json" ]; then
|
||||
python3 -c "
|
||||
import json
|
||||
merged = json.load(open('$WORK/pcr.json'))
|
||||
meta = json.load(open('$WORK/meta-pcr.json'))
|
||||
json.dump(merged + meta, open('$WORK/pcr.json', 'w'))
|
||||
print(f'meta-policies: {len(meta)} meta-PCRs appended; total PCR list now {len(merged)+len(meta)}')
|
||||
"
|
||||
else
|
||||
echo "kyverno-json meta-policies produced no output; proceeding with the merged list only"
|
||||
fi
|
||||
else
|
||||
echo "=== Step 5c: kj not installed or no merged PCR list; skipping meta-policies (D-120) ==="
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "=== Step 7: confidence signal compute ==="
|
||||
python3 <<PY > "$WORK/signal.json" || fail "confidence signal failed"
|
||||
import json
|
||||
|
||||
@@ -5,15 +5,15 @@ policy if absent (or creates a new version if the policy document
|
||||
differs), attaches it to the spike-runner user, deletes any leftover
|
||||
inline policy, and re-creates the OIDC act_runner role if absent.
|
||||
|
||||
Requires the bootstrap root key (ACDL_BOOTSTRAP_AWS_* or ACDL_AWS_*
|
||||
Requires the bootstrap root key (NOVA_BOOTSTRAP_AWS_* or NOVA_AWS_*
|
||||
when the provided key is a root principal). This script is the
|
||||
reproducible record of the Phase 56 live step — the grants are
|
||||
documented in .ciagent/IAM_POLICY.md and regression-tested by
|
||||
tests/test_iam_policy_baseline.py.
|
||||
|
||||
Usage:
|
||||
export ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID=<root key id>
|
||||
export ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=<root key secret>
|
||||
export NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID=<root key id>
|
||||
export NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY=<root key secret>
|
||||
export AWS_DEFAULT_REGION=us-east-1
|
||||
python3 terraform/bootstrap/apply_iam_baseline.py
|
||||
"""
|
||||
@@ -30,7 +30,7 @@ import boto3
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
POLICY_PATH = ROOT / "terraform" / "bootstrap" / "spike_runner_policy.json"
|
||||
ACCOUNT = os.environ.get("ACDL_AWS_ACCOUNT_ID", "581513795199")
|
||||
ACCOUNT = os.environ.get("NOVA_AWS_ACCOUNT_ID", "581513795199")
|
||||
USER = "nova-spike-runner"
|
||||
POLICY_NAME = "nova-spike-runner-policy"
|
||||
POLICY_ARN = f"arn:aws:iam::{ACCOUNT}:policy/{POLICY_NAME}"
|
||||
@@ -38,10 +38,10 @@ ROLE_NAME = "nova-act-runner-role"
|
||||
|
||||
|
||||
def _session():
|
||||
key_id = os.environ.get("ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID") or os.environ.get("ACDL_AWS_ACCESS_KEY_ID")
|
||||
secret = os.environ.get("ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY") or os.environ.get("ACDL_AWS_SECRET_ACCESS_KEY")
|
||||
key_id = os.environ.get("NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID") or os.environ.get("NOVA_AWS_ACCESS_KEY_ID")
|
||||
secret = os.environ.get("NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY") or os.environ.get("NOVA_AWS_SECRET_ACCESS_KEY")
|
||||
if not key_id or not secret:
|
||||
sys.exit("FAIL: set ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID + ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY (root key)")
|
||||
sys.exit("FAIL: set NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID + NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY (root key)")
|
||||
region = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
||||
return boto3.Session(aws_access_key_id=key_id, aws_secret_access_key=secret, region_name=region)
|
||||
|
||||
|
||||
@@ -3,12 +3,14 @@
|
||||
Idempotent: skips user creation if the user exists; creates an initial
|
||||
access key if none active exists. Prints the key to stdout for the
|
||||
orchestrator to capture (NEVER committed):
|
||||
ACDL_AWS_ACCESS_KEY_ID=<...>
|
||||
ACDL_AWS_SECRET_ACCESS_KEY=<...>
|
||||
NOVA_AWS_ACCESS_KEY_ID=<...>
|
||||
NOVA_AWS_SECRET_ACCESS_KEY=<...>
|
||||
|
||||
Run with the bootstrap root key in env:
|
||||
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID / ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY
|
||||
AWS_DEFAULT_REGION (defaults to us-east-1)
|
||||
NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID / NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY
|
||||
(falls back to NOVA_AWS_ACCESS_KEY_ID / NOVA_AWS_SECRET_ACCESS_KEY when
|
||||
the provided key is a root principal). AWS_DEFAULT_REGION (defaults
|
||||
to us-east-1).
|
||||
|
||||
The inline policy is read from spike_runner_policy.json (next to this
|
||||
file). The account id + region are already substituted in the policy file
|
||||
@@ -38,9 +40,13 @@ POLICY_FILE = os.path.join(os.path.dirname(__file__), "spike_runner_policy.json"
|
||||
|
||||
|
||||
def main():
|
||||
key_id = os.environ.get("NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID") or os.environ.get("NOVA_AWS_ACCESS_KEY_ID")
|
||||
secret = os.environ.get("NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY") or os.environ.get("NOVA_AWS_SECRET_ACCESS_KEY")
|
||||
if not key_id or not secret:
|
||||
sys.exit("FAIL: set NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID + NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY (root key)")
|
||||
session = boto3.Session(
|
||||
aws_access_key_id=os.environ["ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID"],
|
||||
aws_secret_access_key=os.environ["ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY"],
|
||||
aws_access_key_id=key_id,
|
||||
aws_secret_access_key=secret,
|
||||
region_name=REGION,
|
||||
)
|
||||
iam = session.client("iam")
|
||||
@@ -71,8 +77,8 @@ def main():
|
||||
print(" (use scripts/rotate_spike_key.sh to rotate)")
|
||||
return
|
||||
new_key = iam.create_access_key(UserName=USER_NAME)["AccessKey"]
|
||||
print("ACDL_AWS_ACCESS_KEY_ID=" + new_key["AccessKeyId"])
|
||||
print("ACDL_AWS_SECRET_ACCESS_KEY=" + new_key["SecretAccessKey"])
|
||||
print("NOVA_AWS_ACCESS_KEY_ID=" + new_key["AccessKeyId"])
|
||||
print("NOVA_AWS_SECRET_ACCESS_KEY=" + new_key["SecretAccessKey"])
|
||||
print(f"iam: created initial access key {new_key['AccessKeyId']} for {USER_NAME}", file=sys.stderr)
|
||||
|
||||
|
||||
|
||||
@@ -6,8 +6,10 @@
|
||||
evidence outbox (D-P08-1).
|
||||
|
||||
Run with the bootstrap root key in env:
|
||||
ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID / ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY
|
||||
AWS_DEFAULT_REGION (defaults to us-east-1)
|
||||
NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID / NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY
|
||||
(falls back to NOVA_AWS_ACCESS_KEY_ID / NOVA_AWS_SECRET_ACCESS_KEY when
|
||||
the provided key is a root principal). AWS_DEFAULT_REGION (defaults
|
||||
to us-east-1).
|
||||
|
||||
Writes terraform/bootstrap/.bootstrap_state.json (gitignored bookkeeping).
|
||||
|
||||
@@ -30,15 +32,19 @@ import boto3
|
||||
|
||||
|
||||
REGION = os.environ.get("AWS_DEFAULT_REGION", "us-east-1")
|
||||
ACCOUNT_ID = os.environ.get("ACDL_AWS_ACCOUNT_ID", "581513795199")
|
||||
ACCOUNT_ID = os.environ.get("NOVA_AWS_ACCOUNT_ID", "581513795199")
|
||||
STATE_BUCKET = f"nova-tfstate-{ACCOUNT_ID}-us-east-1"
|
||||
OUTBOX_TABLE = "nova-outbox"
|
||||
|
||||
|
||||
def main():
|
||||
key_id = os.environ.get("NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID") or os.environ.get("NOVA_AWS_ACCESS_KEY_ID")
|
||||
secret = os.environ.get("NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY") or os.environ.get("NOVA_AWS_SECRET_ACCESS_KEY")
|
||||
if not key_id or not secret:
|
||||
sys.exit("FAIL: set NOVA_BOOTSTRAP_AWS_ACCESS_KEY_ID + NOVA_BOOTSTRAP_AWS_SECRET_ACCESS_KEY (root key)")
|
||||
session = boto3.Session(
|
||||
aws_access_key_id=os.environ["ACDL_BOOTSTRAP_AWS_ACCESS_KEY_ID"],
|
||||
aws_secret_access_key=os.environ["ACDL_BOOTSTRAP_AWS_SECRET_ACCESS_KEY"],
|
||||
aws_access_key_id=key_id,
|
||||
aws_secret_access_key=secret,
|
||||
region_name=REGION,
|
||||
)
|
||||
s3 = session.client("s3", region_name=REGION)
|
||||
|
||||
@@ -205,11 +205,15 @@ def test_attestation_event_emission(tmp_metrics):
|
||||
# Dev skips (autonomous) — no event
|
||||
ok, reason = attest("cid-attest-1", "dev", "testuser")
|
||||
assert ok
|
||||
# QA requires approver + attestation matrix — mock evidence
|
||||
# QA requires approver + attestation matrix — mock evidence with
|
||||
# fresh timestamps (relative to now, not hardcoded — avoids the
|
||||
# time-bomb where fixed dates age out of the freshness window).
|
||||
import datetime
|
||||
now = datetime.datetime.now(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
ok, reason = attest("cid-attest-2", "qa", "testuser",
|
||||
evidence={"functional_correctness": {"timestamp": "2026-08-04T12:00:00Z", "type": "test", "payload": {}},
|
||||
"performance_baseline": {"timestamp": "2026-08-04T12:00:00Z", "type": "test", "payload": {}},
|
||||
"security_posture": {"timestamp": "2026-08-04T12:00:00Z", "type": "test", "payload": {}},
|
||||
evidence={"functional_correctness": {"timestamp": now, "type": "test", "payload": {}},
|
||||
"performance_baseline": {"timestamp": now, "type": "test", "payload": {}},
|
||||
"security_posture": {"timestamp": now, "type": "test", "payload": {}},
|
||||
"contract_nfrs": {"valid": True}})
|
||||
assert ok
|
||||
# Check the attestation event was emitted
|
||||
|
||||
@@ -27,6 +27,11 @@ class TestStep5bKyvernoJsonWiring:
|
||||
assert "Step 5b: kyverno-json plan-JSON policies" in s, \
|
||||
"run_platform.sh must have a Step 5b kyverno-json block (REQ-301)"
|
||||
|
||||
def test_step_5c_meta_block_present(self):
|
||||
s = _read_script()
|
||||
assert "Step 5c: kyverno-json meta-policies over the merged PCR list" in s, \
|
||||
"run_platform.sh must have a Step 5c meta-policy block (REQ-303, P1-1 fix)"
|
||||
|
||||
def test_kj_scan_invocation_present(self):
|
||||
s = _read_script()
|
||||
assert "adapters/kyverno-json/policies/plan-json" in s, \
|
||||
|
||||
Reference in New Issue
Block a user