Audit and compliance¶
CI audit gate¶
ci_gate() is the recommended
entry point for CI pipelines. It scans a directory, evaluates a policy,
optionally writes SARIF/JSON output, and returns an exit code:
import sys
from quantum_safe.audit import Auditor, AuditPolicy
exit_code = Auditor.ci_gate(
"./src",
policy=AuditPolicy(allow_classical_only=False, hybrid_required=True),
output_sarif="audit.sarif", # GitHub Code Scanning
output_json="audit.json",
)
sys.exit(exit_code)
Audit policies¶
AuditPolicy controls which findings
block the CI gate. Four presets are available:
Preset |
Behaviour |
|---|---|
|
Never blocks; reports only. |
|
Blocks on CRITICAL only. Allows hybrid during migration. |
|
Blocks on HIGH+. Classical-only is a warning. Default. |
|
Blocks on MEDIUM+. Requires hybrid for all new key usage. |
from quantum_safe.audit import AuditPolicy
# Use a preset
policy = AuditPolicy.from_preset("strict")
# Or configure manually
policy = AuditPolicy(
allow_classical_only=False,
hybrid_required=True,
block_on_severity="HIGH",
)
Auditor¶
Auditor exposes the full audit
pipeline for programmatic use:
from quantum_safe.audit import Auditor, AuditPolicy
from quantum_safe.migrate import Scanner
report = Auditor.audit_source("./src", policy=AuditPolicy.from_preset("standard"))
print(report.summary())
# Individual finding fields
for f in report.critical:
print(f.file, f.line, f.rule_id, f.message, f.fix_hint)
NIST compliance report¶
NISTComplianceChecker maps scanner
findings to specific NIST/CISA controls:
from quantum_safe.audit import NISTComplianceChecker
from quantum_safe.migrate import Scanner
scan = Scanner.scan_directory("./src")
report = NISTComplianceChecker.check(scan, target="./src")
print(report.to_json())
Each finding is annotated with the relevant standards:
FIPS 203 (ML-KEM)
FIPS 204 (ML-DSA)
FIPS 205 (SLH-DSA)
NIST SP 800-208
CISA Post-Quantum Cryptography Checklist
CycloneDX SBOM enrichment¶
SBOMEnricher annotates a CycloneDX SBOM
with PQC-readiness assessments for each component:
import json
from quantum_safe.audit import SBOMEnricher
with open("sbom.json") as f:
sbom = json.load(f)
enriched, assessments = SBOMEnricher.enrich(sbom)
not_ready = [a for a in assessments if a.readiness.value == "NOT_READY"]
for a in not_ready:
print(f"NOT READY: {a.name} {a.version}")
print(f" Action: {a.action}")
with open("sbom-pqc.json", "w") as f:
json.dump(enriched, f, indent=2)
Readiness values:
READY— uses hybrid or pure PQC algorithmsPARTIAL— partially migratedNOT_READY— classical-onlyUNKNOWN— insufficient information
From a requirements.txt:
enriched, assessments = SBOMEnricher.from_requirements("requirements.txt")
CLI¶
# Scan with text output (default)
qs-audit scan ./src
# SARIF for GitHub Code Scanning
qs-audit scan ./src --format sarif --output audit.sarif
# JSON report
qs-audit scan ./src --format json --output audit.json
# Use a policy preset
qs-audit scan ./src --preset-policy strict
# Fail CI on HIGH or above findings
qs-audit scan ./src --fail-on high
# Enrich a CycloneDX SBOM
qs-audit sbom sbom.json --output sbom-pqc.json
# Quick requirements.txt check
qs-audit requirements requirements.txt
# NIST compliance report
qs-audit compliance ./src --format json --output compliance.json