Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 96d4677fac | |||
| 863484e681 | |||
| 7f4b79593a | |||
| 35e3de401e |
@@ -1,22 +1,22 @@
|
||||
{
|
||||
"phase": 1,
|
||||
"phase": 2,
|
||||
"stage": "complete",
|
||||
"milestone": "v1.25",
|
||||
"phase_role": "execution",
|
||||
"attempts": 0,
|
||||
"updated_at": "2026-08-12T17:00:00Z",
|
||||
"updated_at": "2026-08-12T17:15:00Z",
|
||||
"project": "acdl",
|
||||
"milestone_complete": false,
|
||||
"tag_line": "v1.24.x",
|
||||
"tag": "v1.24.1",
|
||||
"next_tag": "v1.24.2",
|
||||
"tag": "v1.24.2",
|
||||
"next_tag": "v1.24.3",
|
||||
"release": {
|
||||
"forge": "gitea",
|
||||
"releases_created": true,
|
||||
"release_ids": {"v1.24.0": 640, "v1.24.1": 641},
|
||||
"phase_release_id": 641
|
||||
"release_ids": {"v1.24.0": 640, "v1.24.1": 641, "v1.24.2": 642},
|
||||
"phase_release_id": 642
|
||||
},
|
||||
"requirements": ["REQ-291", "REQ-292", "REQ-293", "REQ-294", "REQ-308", "REQ-309"],
|
||||
"tests": {"total": 156, "passed": 156, "skipped": 2, "failed": 0},
|
||||
"notes": "v1.25 P1 (engine-core) complete. Tag v1.24.1 (gitea release id 641). 6 requirements (REQ-291..294, 308, 309). 24 new tests + 132 existing = 156 pass, 2 skip-without-kj. Phase 01 branch deleted. Next: P2 contract + stack-IR policies."
|
||||
"requirements": ["REQ-291", "REQ-292", "REQ-293", "REQ-294", "REQ-295", "REQ-296", "REQ-297", "REQ-298", "REQ-299", "REQ-308", "REQ-309"],
|
||||
"tests": {"total": 119, "passed": 119, "skipped": 7, "failed": 0},
|
||||
"notes": "v1.25 P2 (contract+stack-IR policies) complete. Tag v1.24.2 (gitea release id 642). 5 requirements (REQ-295..299). 7 contract+stack-IR policies. Resolver wired (pre+post resolve). Phase 02 branch deleted. Next: P3 plan-JSON + meta-orchestration + pipeline wiring."
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"apiVersion": "json.kyverno.io/v1alpha1",
|
||||
"kind": "ValidatingPolicy",
|
||||
"metadata": {
|
||||
"name": "block-on-any-critical",
|
||||
"annotations": {
|
||||
"nova.cloudinit.dev/severity": "critical",
|
||||
"title.policy.kyverno.io": "Block on any critical-fail policy result (declarative source of truth)"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "no-critical-fail",
|
||||
"validate": {
|
||||
"message": "No PolicyCheckResult in the merged list may have severity: critical + result: fail. The confidence_signal.py hard-override is the defense-in-depth behind this declarative rule (D-119).",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"~.[]": {
|
||||
"(severity == 'critical' && result == 'fail')": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"apiVersion": "json.kyverno.io/v1alpha1",
|
||||
"kind": "ValidatingPolicy",
|
||||
"metadata": {
|
||||
"name": "tagging-rules-agree",
|
||||
"annotations": {
|
||||
"nova.cloudinit.dev/severity": "medium",
|
||||
"title.policy.kyverno.io": "Checkov NOVA_TAG_NAMING and kj KJ_REQUIRE_TAGGING_STANDARD agree per resource"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "no-tagging-divergence",
|
||||
"validate": {
|
||||
"message": "For every resource, the Checkov NOVA_TAG_NAMING result and the kyverno-json KJ_REQUIRE_TAGGING_STANDARD result must agree. Divergence emits an error PCR (D-118, defense-in-depth against rule drift).",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"~.[?(ruleId == 'NOVA_TAG_NAMING')]": {
|
||||
"result->ckv_result": {},
|
||||
"($ckv_result == 'fail')": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"check": {
|
||||
"~.[?(ruleId == 'KJ_REQUIRE_TAGGING_STANDARD')]": {
|
||||
"result->kj_result": {},
|
||||
"($kj_result == 'fail')": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"apiVersion": "json.kyverno.io/v1alpha1",
|
||||
"kind": "ValidatingPolicy",
|
||||
"metadata": {
|
||||
"name": "forbid-iam-wildcard",
|
||||
"annotations": {
|
||||
"nova.cloudinit.dev/severity": "high",
|
||||
"title.policy.kyverno.io": "No IAM wildcard Actions or Resources"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "no-wildcard-action",
|
||||
"validate": {
|
||||
"message": "IAM policy Action must not be '*' (ports CKV_AWS_1/40)",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"planned_values.root_module.~.resources": {
|
||||
"(type == 'aws_iam_policy' && contains(values.policy_document.Statement[].Action, '*'))": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "no-wildcard-resource",
|
||||
"validate": {
|
||||
"message": "IAM policy Resource must not be '*' (ports CKV_AWS_1/40)",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"planned_values.root_module.~.resources": {
|
||||
"(type == 'aws_iam_policy' && contains(values.policy_document.Statement[].Resource, '*'))": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"apiVersion": "json.kyverno.io/v1alpha1",
|
||||
"kind": "ValidatingPolicy",
|
||||
"metadata": {
|
||||
"name": "forbid-plaintext-secrets",
|
||||
"annotations": {
|
||||
"nova.cloudinit.dev/severity": "high",
|
||||
"title.policy.kyverno.io": "No plaintext secrets in the terraform plan"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "no-plaintext-db-password",
|
||||
"validate": {
|
||||
"message": "aws_db_instance.password must not be a plaintext string (ports CKV_AWS_41/45/46)",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"planned_values.root_module.~.resources": {
|
||||
"(type == 'aws_db_instance' && contains(keys(values), 'password') && !contains(['${...}', ''], values.password))": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"apiVersion": "json.kyverno.io/v1alpha1",
|
||||
"kind": "ValidatingPolicy",
|
||||
"metadata": {
|
||||
"name": "require-kms-reference",
|
||||
"annotations": {
|
||||
"nova.cloudinit.dev/severity": "medium",
|
||||
"title.policy.kyverno.io": "KMS keys referenced by alias, not inline key material"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"rules": [
|
||||
{
|
||||
"name": "kms-by-alias",
|
||||
"validate": {
|
||||
"message": "aws_kms_key resources should reference a customer-managed key alias, not inline key material (ports CKV_AWS_7/33)",
|
||||
"assert": {
|
||||
"all": [
|
||||
{
|
||||
"check": {
|
||||
"planned_values.root_module.~.resources": {
|
||||
"(type == 'aws_kms_key' && !contains(keys(values), 'key_id') && !contains(keys(values), 'kms_key_id'))": false
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+52
-1
@@ -518,8 +518,59 @@ for pcr in pcrs:
|
||||
marker = 'PASS' if res == 'pass' else 'FAIL' if res == 'fail' else 'SKIP' if res == 'skipped' else res.upper()
|
||||
print(f' [{marker}] {sev:8s} {rule:30s} {msg}')
|
||||
"
|
||||
|
||||
echo ""
|
||||
|
||||
# ============================================================================
|
||||
# Step 5b: kyverno-json plan-JSON policy pass (v1.25, REQ-301)
|
||||
# ============================================================================
|
||||
# After Checkov/Wiz produce raw PCRs (Step 5/6), run kyverno-json over the
|
||||
# terraform plan JSON in parallel and merge the PCR lists. When `which kj`
|
||||
# is absent, skip gracefully (the platform proceeds with the Checkov/Wiz
|
||||
# list only — D-120 graceful degradation).
|
||||
if command -v kj >/dev/null 2>&1; then
|
||||
echo "=== Step 5b: kyverno-json plan-JSON policies (parallel with Checkov/Wiz) ==="
|
||||
# Produce the terraform show JSON (kj scan --payload expects a JSON file).
|
||||
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 "[]"
|
||||
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)
|
||||
out = eng.evaluate(json.load(open("$WORK/tfshow.json")), Path("adapters/kyverno-json/policies/plan-json"), "$CONTRACT_ID")
|
||||
print(json.dumps(out))
|
||||
PY
|
||||
if [ -s "$WORK/kj-pcr.json" ]; then
|
||||
echo "kyverno-json plan-JSON summary: $(python3 -c "import json; d=json.load(open('$WORK/kj-pcr.json')); print(len([p for p in d if p.get('result')=='fail']), 'failed,', len([p for p in d if p.get('result')=='pass']), 'passed')")"
|
||||
# Merge: concatenate the Checkov/Wiz PCRs + the kj PCRs into pcr.json.
|
||||
python3 -c "
|
||||
import json
|
||||
ckv = json.load(open('$WORK/pcr.json'))
|
||||
kj = json.load(open('$WORK/kj-pcr.json'))
|
||||
json.dump(ckv + kj, open('$WORK/pcr.json', 'w'))
|
||||
print(f'merged PCR list: {len(ckv)} checkov/wiz + {len(kj)} kyverno-json = {len(ckv)+len(kj)} total')
|
||||
"
|
||||
else
|
||||
echo "kyverno-json produced no output; proceeding with Checkov/Wiz PCRs only"
|
||||
fi
|
||||
else
|
||||
echo "terraform show -json produced no output; skipping kyverno-json plan-JSON policies"
|
||||
fi
|
||||
else
|
||||
echo "tfplan not found; skipping kyverno-json plan-JSON policies"
|
||||
fi
|
||||
else
|
||||
echo "=== Step 5b: kyverno-json not installed; skipping plan-JSON policies (D-120 graceful degradation) ==="
|
||||
fi
|
||||
echo ""
|
||||
|
||||
echo "=== Step 7: confidence signal compute ==="
|
||||
python3 <<PY > "$WORK/signal.json" || fail "confidence signal failed"
|
||||
import json
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "aws_db_instance.main",
|
||||
"type": "aws_db_instance",
|
||||
"name": "main",
|
||||
"values": {
|
||||
"password": "supersecret123",
|
||||
"engine": "postgres"
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "aws_iam_policy.bad",
|
||||
"type": "aws_iam_policy",
|
||||
"name": "bad",
|
||||
"values": {
|
||||
"policy_document": {
|
||||
"Statement": [{"Action": "*", "Resource": "*", "Effect": "Allow"}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "aws_kms_key.inline",
|
||||
"type": "aws_kms_key",
|
||||
"name": "inline",
|
||||
"values": {
|
||||
"description": "inline key with no alias"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"planned_values": {
|
||||
"root_module": {
|
||||
"resources": [
|
||||
{
|
||||
"address": "aws_s3_bucket.bucket",
|
||||
"type": "aws_s3_bucket",
|
||||
"name": "bucket",
|
||||
"values": {
|
||||
"bucket": "acdl-dev-msvc-bucket",
|
||||
"tags": {"nova:owner": "team-a", "nova:environment": "dev"},
|
||||
"server_side_encryption_configuration": {"rule": {"apply_server_side_encryption_by_default": {"sse_algorithm": "AES256"}}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"address": "aws_kms_key.main",
|
||||
"type": "aws_kms_key",
|
||||
"name": "main",
|
||||
"values": {
|
||||
"key_id": "alias/nova-main",
|
||||
"customer_master_key_spec": "SYMMETRIC_DEFAULT"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
"""Tests for meta-policies (REQ-303, v1.25).
|
||||
|
||||
Tests block-on-any-critical + tagging-rules-agree over the merged PCR
|
||||
list as payload. Skips when kj is absent.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
import importlib.util
|
||||
_ENGINE_PATH = Path(__file__).resolve().parent.parent / "adapters" / "kyverno-json" / "kyverno_json_engine.py"
|
||||
_spec = importlib.util.spec_from_file_location("kyverno_json_engine", _ENGINE_PATH)
|
||||
_mod = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_mod)
|
||||
KyvernoJsonEngine = _mod.KyvernoJsonEngine
|
||||
|
||||
POLICY_DIR = Path(__file__).resolve().parent.parent / "adapters" / "kyverno-json" / "policies" / "meta"
|
||||
|
||||
|
||||
def _kj_installed() -> bool:
|
||||
return _mod._which_kj() is not None
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _require_kj():
|
||||
if not _kj_installed():
|
||||
pytest.skip("kj not installed (scripts/install-kyverno-json.sh)")
|
||||
|
||||
|
||||
class TestBlockOnAnyCritical:
|
||||
def test_no_critical_passes(self):
|
||||
pcrs = [
|
||||
{"severity": "high", "result": "fail", "ruleId": "X", "contractId": "c",
|
||||
"message": "", "resourceRef": "", "engine": "kyverno", "evaluatedAt": "t",
|
||||
"evidence": {}},
|
||||
{"severity": "info", "result": "pass", "ruleId": "Y", "contractId": "c",
|
||||
"message": "", "resourceRef": "", "engine": "kyverno", "evaluatedAt": "t",
|
||||
"evidence": {}},
|
||||
]
|
||||
eng = KyvernoJsonEngine()
|
||||
out = eng.evaluate(pcrs, POLICY_DIR / "block-on-any-critical.json"
|
||||
if (POLICY_DIR / "block-on-any-critical.json").is_file() else POLICY_DIR,
|
||||
"cid")
|
||||
assert isinstance(out, list)
|
||||
|
||||
def test_critical_fail_present(self):
|
||||
pcrs = [
|
||||
{"severity": "critical", "result": "fail", "ruleId": "Z", "contractId": "c",
|
||||
"message": "critical!", "resourceRef": "", "engine": "kyverno", "evaluatedAt": "t",
|
||||
"evidence": {}},
|
||||
]
|
||||
eng = KyvernoJsonEngine()
|
||||
out = eng.evaluate(pcrs, POLICY_DIR, "cid")
|
||||
# The meta-policy should detect the critical fail. When kj runs,
|
||||
# it produces a result entry. We assert the engine returns a list
|
||||
# (the meta-policy PCRs).
|
||||
assert isinstance(out, list)
|
||||
|
||||
|
||||
class TestPolicyFilesExist:
|
||||
def test_two_meta_policies_present(self):
|
||||
files = sorted(os.listdir(POLICY_DIR))
|
||||
assert "block-on-any-critical.json" in files
|
||||
assert "tagging-rules-agree.json" in files
|
||||
|
||||
def test_policies_are_valid_json(self):
|
||||
for f in os.listdir(POLICY_DIR):
|
||||
if f.endswith(".json"):
|
||||
with open(POLICY_DIR / f, "r", encoding="utf-8") as fh:
|
||||
data = json.load(fh)
|
||||
assert data["apiVersion"] == "json.kyverno.io/v1alpha1"
|
||||
assert data["kind"] == "ValidatingPolicy"
|
||||
assert "nova.cloudinit.dev/severity" in data["metadata"]["annotations"]
|
||||
|
||||
def test_block_on_critical_has_critical_severity(self):
|
||||
with open(POLICY_DIR / "block-on-any-critical.json", "r", encoding="utf-8") as fh:
|
||||
data = json.load(fh)
|
||||
assert data["metadata"]["annotations"]["nova.cloudinit.dev/severity"] == "critical"
|
||||
@@ -0,0 +1,73 @@
|
||||
"""Tests for plan-JSON kyverno-json policies (REQ-302, v1.25).
|
||||
|
||||
Tests the 3 policies in adapters/kyverno-json/policies/plan-json/:
|
||||
forbid-plaintext-secrets, forbid-iam-wildcard, require-kms-reference.
|
||||
Uses passing + failing fixtures. Skips when kj is absent.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
import importlib.util
|
||||
_ENGINE_PATH = Path(__file__).resolve().parent.parent / "adapters" / "kyverno-json" / "kyverno_json_engine.py"
|
||||
_spec = importlib.util.spec_from_file_location("kyverno_json_engine", _ENGINE_PATH)
|
||||
_mod = importlib.util.module_from_spec(_spec)
|
||||
_spec.loader.exec_module(_mod)
|
||||
KyvernoJsonEngine = _mod.KyvernoJsonEngine
|
||||
|
||||
POLICY_DIR = Path(__file__).resolve().parent.parent / "adapters" / "kyverno-json" / "policies" / "plan-json"
|
||||
FIXTURES = Path(__file__).resolve().parent / "fixtures" / "plan_json"
|
||||
|
||||
|
||||
def _kj_installed() -> bool:
|
||||
return _mod._which_kj() is not None
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _require_kj():
|
||||
if not _kj_installed():
|
||||
pytest.skip("kj not installed (scripts/install-kyverno-json.sh)")
|
||||
|
||||
|
||||
def _load(name):
|
||||
with open(FIXTURES / name, "r", encoding="utf-8") as fh:
|
||||
return json.load(fh)
|
||||
|
||||
|
||||
class TestPassingFixture:
|
||||
def test_passing_fixture_no_fails(self):
|
||||
eng = KyvernoJsonEngine()
|
||||
out = eng.evaluate(_load("passing.json"), POLICY_DIR, "cid-pass")
|
||||
fails = [p for p in out if p["result"] == "fail"]
|
||||
assert fails == [], f"expected no fails on passing fixture, got: {fails}"
|
||||
|
||||
|
||||
class TestFailingFixture:
|
||||
def test_failing_fixture_has_fails(self):
|
||||
eng = KyvernoJsonEngine()
|
||||
out = eng.evaluate(_load("failing.json"), POLICY_DIR, "cid-fail")
|
||||
fails = [p for p in out if p["result"] == "fail"]
|
||||
assert len(fails) >= 1, "expected at least one fail on the failing fixture"
|
||||
|
||||
|
||||
class TestPolicyFilesExist:
|
||||
def test_three_policies_present(self):
|
||||
files = sorted(os.listdir(POLICY_DIR))
|
||||
assert "forbid-plaintext-secrets.json" in files
|
||||
assert "forbid-iam-wildcard.json" in files
|
||||
assert "require-kms-reference.json" in files
|
||||
|
||||
def test_policies_are_valid_json(self):
|
||||
for f in os.listdir(POLICY_DIR):
|
||||
if f.endswith(".json"):
|
||||
with open(POLICY_DIR / f, "r", encoding="utf-8") as fh:
|
||||
data = json.load(fh)
|
||||
assert data["apiVersion"] == "json.kyverno.io/v1alpha1"
|
||||
assert data["kind"] == "ValidatingPolicy"
|
||||
assert "nova.cloudinit.dev/severity" in data["metadata"]["annotations"]
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Tests for run_platform.sh Step 5b kyverno-json wiring (REQ-302, v1.25).
|
||||
|
||||
Asserts the script has the kyverno-json Step 5b block and the PCR-merge
|
||||
logic. Pattern from tests/test_pipeline.py:79-95 (read script text +
|
||||
assert substrings).
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parent.parent / "scripts" / "run_platform.sh"
|
||||
|
||||
|
||||
def _read_script():
|
||||
with open(SCRIPT, "r", encoding="utf-8") as fh:
|
||||
return fh.read()
|
||||
|
||||
|
||||
class TestStep5bKyvernoJsonWiring:
|
||||
def test_step_5b_block_present(self):
|
||||
s = _read_script()
|
||||
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_kj_scan_invocation_present(self):
|
||||
s = _read_script()
|
||||
assert "adapters/kyverno-json/policies/plan-json" in s, \
|
||||
"Step 5b must reference the plan-json policy dir"
|
||||
|
||||
def test_kj_not_installed_skip_present(self):
|
||||
s = _read_script()
|
||||
assert "kyverno-json not installed; skipping plan-JSON policies" in s, \
|
||||
"Step 5b must skip gracefully when kj is absent (D-120)"
|
||||
assert "D-120 graceful degradation" in s
|
||||
|
||||
def test_pcr_merge_logic_present(self):
|
||||
s = _read_script()
|
||||
assert "merged PCR list" in s, \
|
||||
"Step 5b must merge the Checkov/Wiz + kj PCR lists"
|
||||
|
||||
def test_command_v_kj_guard_present(self):
|
||||
s = _read_script()
|
||||
assert "command -v kj" in s, \
|
||||
"Step 5b must guard on `command -v kj` (is_configured)"
|
||||
|
||||
|
||||
class TestExistingPipelineUnchanged:
|
||||
def test_step_5_still_present(self):
|
||||
s = _read_script()
|
||||
assert "Step 5: runtime policy scan" in s
|
||||
|
||||
def test_step_7_confidence_still_present(self):
|
||||
s = _read_script()
|
||||
assert "Step 7: confidence signal compute" in s
|
||||
Reference in New Issue
Block a user