All checks were successful
CI/CD / go-lint (push) Has been skipped
CI/CD / python-lint (push) Has been skipped
CI/CD / nodejs-lint (push) Has been skipped
CI/CD / test-go-ai-compliance (push) Successful in 40s
CI/CD / test-python-backend-compliance (push) Successful in 41s
CI/CD / test-python-document-crawler (push) Successful in 26s
CI/CD / test-python-dsms-gateway (push) Successful in 23s
CI/CD / validate-canonical-controls (push) Successful in 18s
CI/CD / deploy-hetzner (push) Successful in 2m26s
Eigenstaendig formulierte Security Controls mit unabhaengiger Taxonomie und Open-Source-Verankerung (OWASP, NIST, ENISA). Keine BSI-Nomenklatur. - Migration 044: 5 DB-Tabellen (frameworks, controls, sources, licenses, mappings) - 10 Seed Controls mit 39 Open-Source-Referenzen - License Gate: Quellen-Berechtigungspruefung (analysis/excerpt/embeddings/product) - Too-Close-Detektor: 5 Metriken (exact-phrase, token-overlap, ngram, embedding, LCS) - REST API: 8 Endpoints unter /v1/canonical/ - Go Loader mit Multi-Index (ID, domain, severity, framework) - Frontend: Control Library Browser + Provenance Wiki - CI/CD: validate-controls.py Job (schema, no-leak, open-anchors) - 67 Tests (8 Go + 59 Python), alle PASS - MkDocs Dokumentation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
143 lines
5.7 KiB
Python
143 lines
5.7 KiB
Python
"""Tests for the CI/CD control validator script."""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent.parent
|
|
VALIDATOR = REPO_ROOT / "scripts" / "validate-controls.py"
|
|
CONTROLS_FILE = REPO_ROOT / "ai-compliance-sdk" / "policies" / "canonical_controls_v1.json"
|
|
|
|
|
|
class TestValidatorScript:
|
|
"""Integration tests for validate-controls.py."""
|
|
|
|
def test_validator_passes_on_valid_controls(self):
|
|
result = subprocess.run(
|
|
[sys.executable, str(VALIDATOR)],
|
|
capture_output=True, text=True, cwd=str(REPO_ROOT),
|
|
)
|
|
assert result.returncode == 0, f"Validator failed:\n{result.stdout}\n{result.stderr}"
|
|
assert "ALL CHECKS PASSED" in result.stdout
|
|
|
|
def test_validator_reports_control_count(self):
|
|
result = subprocess.run(
|
|
[sys.executable, str(VALIDATOR)],
|
|
capture_output=True, text=True, cwd=str(REPO_ROOT),
|
|
)
|
|
assert "Controls: 10" in result.stdout
|
|
assert "Open Anchors:" in result.stdout
|
|
|
|
|
|
class TestControlsJsonStructure:
|
|
"""Direct validation of the JSON file structure."""
|
|
|
|
@pytest.fixture
|
|
def controls_data(self):
|
|
with open(CONTROLS_FILE) as f:
|
|
return json.load(f)
|
|
|
|
def test_top_level_keys(self, controls_data):
|
|
assert "version" in controls_data
|
|
assert "schema" in controls_data
|
|
assert "framework" in controls_data
|
|
assert "domains" in controls_data
|
|
assert "controls" in controls_data
|
|
|
|
def test_framework_metadata(self, controls_data):
|
|
fw = controls_data["framework"]
|
|
assert fw["id"] == "bp_security_v1"
|
|
assert fw["version"] == "1.0"
|
|
|
|
def test_all_controls_have_open_anchors(self, controls_data):
|
|
for ctrl in controls_data["controls"]:
|
|
anchors = ctrl.get("open_anchors", [])
|
|
assert len(anchors) >= 1, (
|
|
f"Control {ctrl['control_id']} has no open anchors"
|
|
)
|
|
|
|
def test_no_bsi_nomenclature_in_controls(self, controls_data):
|
|
"""Ensure no BSI-proprietary IDs leak into product-facing fields."""
|
|
import re
|
|
bsi_pattern = re.compile(r"O\.[A-Za-z]+_[0-9]+")
|
|
for ctrl in controls_data["controls"]:
|
|
for field in ["objective", "rationale", "title"]:
|
|
text = ctrl.get(field, "")
|
|
match = bsi_pattern.search(text)
|
|
assert match is None, (
|
|
f"Control {ctrl['control_id']}.{field} contains BSI pattern: {match.group()}"
|
|
)
|
|
|
|
def test_control_id_format(self, controls_data):
|
|
import re
|
|
pattern = re.compile(r"^[A-Z]{2,6}-[0-9]{3}$")
|
|
for ctrl in controls_data["controls"]:
|
|
assert pattern.match(ctrl["control_id"]), (
|
|
f"Invalid control_id format: {ctrl['control_id']}"
|
|
)
|
|
|
|
def test_valid_severities(self, controls_data):
|
|
valid = {"low", "medium", "high", "critical"}
|
|
for ctrl in controls_data["controls"]:
|
|
assert ctrl["severity"] in valid, (
|
|
f"Control {ctrl['control_id']} has invalid severity: {ctrl['severity']}"
|
|
)
|
|
|
|
def test_domains_referenced_by_controls(self, controls_data):
|
|
domain_ids = {d["id"] for d in controls_data["domains"]}
|
|
for ctrl in controls_data["controls"]:
|
|
assert ctrl["domain"] in domain_ids, (
|
|
f"Control {ctrl['control_id']} references unknown domain: {ctrl['domain']}"
|
|
)
|
|
|
|
def test_open_anchor_structure(self, controls_data):
|
|
for ctrl in controls_data["controls"]:
|
|
for i, anchor in enumerate(ctrl.get("open_anchors", [])):
|
|
assert "framework" in anchor, (
|
|
f"Control {ctrl['control_id']}: anchor[{i}] missing 'framework'"
|
|
)
|
|
assert "ref" in anchor, (
|
|
f"Control {ctrl['control_id']}: anchor[{i}] missing 'ref'"
|
|
)
|
|
assert "url" in anchor, (
|
|
f"Control {ctrl['control_id']}: anchor[{i}] missing 'url'"
|
|
)
|
|
assert anchor["url"].startswith("https://"), (
|
|
f"Control {ctrl['control_id']}: anchor[{i}] URL not HTTPS"
|
|
)
|
|
|
|
def test_evidence_structure(self, controls_data):
|
|
for ctrl in controls_data["controls"]:
|
|
for i, ev in enumerate(ctrl.get("evidence", [])):
|
|
assert "type" in ev, (
|
|
f"Control {ctrl['control_id']}: evidence[{i}] missing 'type'"
|
|
)
|
|
assert "description" in ev, (
|
|
f"Control {ctrl['control_id']}: evidence[{i}] missing 'description'"
|
|
)
|
|
|
|
def test_risk_scores_in_range(self, controls_data):
|
|
for ctrl in controls_data["controls"]:
|
|
if ctrl.get("risk_score") is not None:
|
|
assert 0 <= ctrl["risk_score"] <= 10, (
|
|
f"Control {ctrl['control_id']}: risk_score {ctrl['risk_score']} out of range"
|
|
)
|
|
|
|
def test_total_controls_matches(self, controls_data):
|
|
assert controls_data["total_controls"] == len(controls_data["controls"])
|
|
|
|
def test_independent_taxonomy_no_tr_reference(self, controls_data):
|
|
"""Verify controls don't reference BSI TR documents in product text."""
|
|
import re
|
|
tr_pattern = re.compile(r"TR-03161|BSI-TR-")
|
|
for ctrl in controls_data["controls"]:
|
|
for field in ["objective", "rationale", "title"]:
|
|
text = ctrl.get(field, "")
|
|
match = tr_pattern.search(text)
|
|
assert match is None, (
|
|
f"Control {ctrl['control_id']}.{field} references BSI TR: {match.group()}"
|
|
)
|