Files
breakpilot-compliance/backend-compliance/tests/test_validate_controls.py
Benjamin Admin 050f353192
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
feat(canonical-controls): Canonical Control Library — rechtssichere Security Controls
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>
2026-03-12 19:55:06 +01:00

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()}"
)