audit-labs/control-coverage
Control coverage and blind-spot analysis for audit evidence.
clone: git clone https://gitbay.org/audit-labs/control-coverage.git
v1.0.0: control_coverage/reporters/soa.py · raw
1"""Statement of Applicability renderer.
2
3ISO 27001 requires a Statement of Applicability (SoA): for every Annex A control,
4whether it applies, why, and its implementation status. This renders exactly that
5from the coverage result — applicability comes from the scope file, and the
6implementation status is derived from the evidence corpus rather than asserted by
7hand, so the SoA stays honest to what the evidence actually shows.
8"""
9
10from __future__ import annotations
11
12from typing import TYPE_CHECKING
13
14from ..coverage import ASSERTED, FAILING, OUT_OF_SCOPE, SUPPORTED, UNADDRESSED
15
16if TYPE_CHECKING:
17 from ..coverage import CoverageReport
18
19# How each assurance state reads as an implementation status in an SoA.
20_IMPL_STATUS = {
21 SUPPORTED: "Implemented — supporting evidence collected",
22 FAILING: "Deficient — evidence shows a non-supporting state",
23 ASSERTED: "Claimed — mapped, but evidence not yet obtained",
24 UNADDRESSED: "Not evidenced — no evidence collected yet",
25 OUT_OF_SCOPE: "Excluded",
26}
27
28
29def _justification(result) -> str:
30 if result.state == OUT_OF_SCOPE:
31 return result.exclusion_reason
32 rules = sorted({o.rule_id for o in result.observations if o.rule_id})
33 sources = sorted({o.source for o in result.observations})
34 if rules:
35 return f"Evidenced by {', '.join(rules)} in {', '.join(sources)}."
36 return "No control in the evidence corpus addresses this yet."
37
38
39def render(report: CoverageReport) -> str:
40 out: list[str] = []
41 subject = report.subject or "the organization"
42 out.append(f"# Statement of Applicability — {report.subject or 'Untitled'}")
43 out.append("")
44 out.append(f"- **Generated:** {report.generated_at}")
45 out.append(f"- **Derived from:** {report.source_count} evidence source(s)")
46 out.append("")
47 out.append(
48 f"This Statement of Applicability records, for each control in scope for "
49 f"{subject}, whether it applies and its implementation status. Applicability "
50 "decisions come from the documented scope; implementation status is derived "
51 "from collected evidence, not asserted."
52 )
53 out.append("")
54
55 for fc in report.frameworks:
56 out.append(f"## {fc.catalog.name}")
57 out.append("")
58 out.append("| Control | Description | Applicable | Status | Justification | Owner |")
59 out.append("| --- | --- | --- | --- | --- | --- |")
60 for r in fc.results:
61 applicable = "No" if r.state == OUT_OF_SCOPE else "Yes"
62 out.append(
63 f"| **{r.control.id}** | {r.control.title} | {applicable} "
64 f"| {_IMPL_STATUS[r.state]} | {_justification(r)} | {r.owner} |"
65 )
66 out.append("")
67
68 return "\n".join(out).rstrip() + "\n"