Commit 653b90a91d

653b90a91d22b8290b57626d398ff5cb8efbbf37

parent: 4187ceff35

Verified · cmc

cmc <hello@cleberg.net> · 2026-08-07 02:13 UTC

Stamp catalog provenance and remove copyrighted control text

- Catalog carries a SHA-256 of its file; reports record the tool version
  and per-framework catalog version + hash
- Paraphrase the AICPA Trust Services Criteria titles into own-words labels
  (identifiers kept) and add copyright disclaimers to all catalogs
- Add MAPPING.md documenting sources, the NIST 177-control derivation, and
  review status
- Add ruff + pytest CI

Layout: unified · split

.github/workflows/ci.yml added +26
@@ -0,0 +1,26 @@
1name: CI
2
3on:
4 push:
5 pull_request:
6
7jobs:
8 test:
9 runs-on: ubuntu-latest
10 strategy:
11 matrix:
12 python-version: ["3.10", "3.12"]
13 steps:
14 - uses: actions/checkout@v5
15 - name: Set up Python ${{ matrix.python-version }}
16 uses: actions/setup-python@v6
17 with:
18 python-version: ${{ matrix.python-version }}
19 - name: Install
20 run: |
21 python -m pip install --upgrade pip
22 pip install -e ".[dev]"
23 - name: Ruff
24 run: ruff check .
25 - name: Tests
26 run: pytest -q
MAPPING.md added +62
@@ -0,0 +1,62 @@
1# Control catalogs — provenance and rationale
2
3`control-coverage` measures how much of a framework an evidence corpus addresses.
4The **denominator** is a framework catalog: the complete list of controls the
5framework defines. This document records where those catalogs come from, how they
6are versioned, and the limits of what they claim.
7
8## What these catalogs are — and are not
9
10- They enumerate control **identifiers** (e.g. `SOC2:CC6.1`, `ISO:A.5.17`,
11 `NIST:AC-2`) plus a short title used as a display label.
12- They are **not** the normative control text. For authoritative wording, consult
13 the source standard.
14- Mapping a control to an evidence signal is the **maintainers' interpretation**.
15 It is not reviewed or endorsed by the AICPA, ISO/IEC, or NIST.
16- Coverage is a measure of **evidence**, not of compliance. A control counted as
17 "addressed" means the corpus contains a signal relevant to it — not that the
18 control operates effectively. That judgment belongs to the organization and its
19 auditor.
20
21## Sources and revisions
22
23| Framework | Catalog file | Revision used | Scope |
24|---|---|---|---|
25| SOC 2 | `catalogs/soc2.yaml` | Trust Services Criteria 2017 (2022 revised points of focus) | All five categories: Security (Common Criteria), Availability, Confidentiality, Processing Integrity, Privacy |
26| ISO/IEC 27001 | `catalogs/iso27001.yaml` | 27001:2022 Annex A | All 93 Annex A controls, four themes |
27| NIST SP 800-53 | `catalogs/nist80053.yaml` | Rev. 5 / SP 800-53B **Moderate** baseline | 177 base controls (see below) |
28
29### How the NIST count is 177
30
31The NIST catalog is the base controls selected in the **SP 800-53B Moderate**
32impact baseline, across the 18 baseline-applicable families. Control
33**enhancements** (e.g. `AC-2(1)`) are not enumerated — coverage is measured at the
34base-control level. The Program Management (PM) family is organization-wide and
35not baseline-allocated; the Privacy (PT) family is selected via the separate
36privacy baseline. That selection is 177 base controls.
37
38## Versioning and traceability
39
40- Each catalog carries a `version` field, and every report stamps the catalog
41 `version` **and a SHA-256 of the catalog file** into its output (`tool` and
42 `frameworks[].sha256` in JSON; the header line in Markdown/HTML).
43- This lets an auditor tie any coverage result back to the exact denominator that
44 produced it, and re-perform against it.
45- Change the control set or a title and the SHA-256 changes; bump `version` on any
46 substantive change.
47
48## Authorship and review
49
50- **Author:** the audit-labs maintainer.
51- **Review status:** maintainer self-review. These catalogs have **not** been
52 through independent professional review; treat them accordingly and validate
53 against the source standards before relying on them in an engagement.
54- **Effective date:** 2026-08.
55
56## Copyright
57
58- **SOC 2 / Trust Services Criteria** — copyright AICPA. Only identifiers are
59 reproduced; titles are our own short-form paraphrases, not the criteria text.
60- **ISO/IEC 27001:2022** — copyright ISO/IEC. Only Annex A identifiers and short
61 titles are reproduced; normative text and guidance are not.
62- **NIST SP 800-53** — U.S. Government work in the public domain.
control_coverage/catalog.py +5 −1
@@ -15,6 +15,7 @@ Control codes are written ``FRAMEWORK:ID`` (for example ``SOC2:CC6.1``,
1515
1616from __future__ import annotations
1717
18import hashlib
1819from dataclasses import dataclass
1920from pathlib import Path
2021
@@ -60,6 +61,7 @@ class Catalog:
6061 coverage: str # "complete" or "partial"
6162 source: str
6263 controls: list[Control]
64 sha256: str = "" # digest of the catalog file, so coverage ties to a mapping
6365
6466 @property
6567 def complete(self) -> bool:
@@ -86,7 +88,8 @@ def _resolve(name: str) -> Path:
8688def load(name: str) -> Catalog:
8789 """Load a bundled catalog by framework name, short code, or alias."""
8890 path = _resolve(name)
89 raw = yaml.safe_load(path.read_text(encoding="utf-8"))
91 text = path.read_text(encoding="utf-8")
92 raw = yaml.safe_load(text)
9093 framework = raw["framework"]
9194 controls = [
9295 Control(
@@ -104,6 +107,7 @@ def load(name: str) -> Catalog:
104107 coverage=raw.get("coverage", "partial"),
105108 source=raw.get("source", ""),
106109 controls=controls,
110 sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
107111 )
108112
109113
control_coverage/catalogs/iso27001.yaml +8 −1
@@ -2,11 +2,18 @@
22#
33# This is exactly the list a Statement of Applicability enumerates. A control's
44# full code is "ISO:<id>", matching the codes audit-report rulesets cite.
5#
6# COPYRIGHT: ISO/IEC 27001:2022 is copyright ISO/IEC. Only the Annex A control
7# identifiers (e.g. A.5.17) and their short titles are reproduced here as labels;
8# the normative control text and implementation guidance are not. For the
9# authoritative wording, obtain the standard from ISO. These control-to-signal
10# mappings are the maintainers' interpretation and are not reviewed or endorsed
11# by ISO/IEC. See ../../MAPPING.md.
512framework: ISO
613name: ISO/IEC 27001:2022 Annex A
714version: "2022"
815coverage: complete
9source: ISO/IEC 27001:2022 Annex A
16source: ISO/IEC 27001:2022 Annex A (identifiers and short titles only)
1017controls:
1118 # A.5 — Organizational controls
1219 - {id: A.5.1, family: Organizational, title: "Policies for information security."}
control_coverage/catalogs/nist80053.yaml +4
@@ -6,6 +6,10 @@
66# management (PM) family is org-wide and not baseline-allocated; the privacy (PT)
77# family is selected via the separate privacy baseline. A control's full code is
88# "NIST:<id>".
9#
10# COPYRIGHT: NIST SP 800-53 is a U.S. Government work in the public domain. The
11# control-to-signal mappings, however, are the maintainers' interpretation and
12# are not reviewed or endorsed by NIST. See ../../MAPPING.md.
913framework: NIST
1014name: NIST SP 800-53 Rev. 5 (Moderate baseline)
1115version: "Rev. 5"
control_coverage/catalogs/soc2.yaml +72 −64
@@ -1,85 +1,93 @@
11# SOC 2 — Trust Services Criteria (AICPA, 2017 with 2022 revised points of focus).
22#
33# The full Common Criteria (the "Security" category every SOC 2 report covers)
4# plus the Availability category. A control's full code is "SOC2:<id>", matching
5# the codes audit-report rulesets cite.
4# plus the Availability, Confidentiality, Processing Integrity, and Privacy
5# categories. A control's full code is "SOC2:<id>", matching the codes
6# audit-report rulesets cite.
7#
8# COPYRIGHT: The Trust Services Criteria are copyright AICPA. The `title` fields
9# below are our own short-form paraphrases used as labels — not the normative
10# criteria text. Only the criterion identifiers (e.g. CC6.1) are reproduced. For
11# the authoritative wording and points of focus, consult the AICPA TSC. These
12# control-to-signal mappings are the maintainers' interpretation and are not
13# reviewed or endorsed by the AICPA. See ../../MAPPING.md.
614framework: SOC2
715name: SOC 2 (Trust Services Criteria)
816version: "2017 (rev. 2022)"
917coverage: complete
10source: AICPA Trust Services Criteria
18source: AICPA Trust Services Criteria (identifiers only; titles paraphrased)
1119controls:
1220 # CC1 — Control Environment
13 - {id: CC1.1, family: Control Environment, title: "The entity demonstrates a commitment to integrity and ethical values."}
14 - {id: CC1.2, family: Control Environment, title: "The board of directors demonstrates independence and exercises oversight of internal control."}
15 - {id: CC1.3, family: Control Environment, title: "Management establishes structures, reporting lines, and appropriate authorities and responsibilities."}
16 - {id: CC1.4, family: Control Environment, title: "The entity demonstrates a commitment to attract, develop, and retain competent individuals."}
17 - {id: CC1.5, family: Control Environment, title: "The entity holds individuals accountable for their internal control responsibilities."}
21 - {id: CC1.1, family: Control Environment, title: "Commitment to integrity and ethical values"}
22 - {id: CC1.2, family: Control Environment, title: "Board independence and internal-control oversight"}
23 - {id: CC1.3, family: Control Environment, title: "Structures, reporting lines, and authorities established"}
24 - {id: CC1.4, family: Control Environment, title: "Commitment to attracting and retaining competent people"}
25 - {id: CC1.5, family: Control Environment, title: "Accountability for internal-control responsibilities"}
1826 # CC2 — Communication and Information
19 - {id: CC2.1, family: Communication and Information, title: "The entity obtains or generates relevant, quality information to support internal control."}
20 - {id: CC2.2, family: Communication and Information, title: "The entity internally communicates information, including objectives and responsibilities for internal control."}
21 - {id: CC2.3, family: Communication and Information, title: "The entity communicates with external parties about matters affecting internal control."}
27 - {id: CC2.1, family: Communication and Information, title: "Relevant, quality information supporting internal control"}
28 - {id: CC2.2, family: Communication and Information, title: "Internal communication of control objectives and duties"}
29 - {id: CC2.3, family: Communication and Information, title: "External communication on internal-control matters"}
2230 # CC3 — Risk Assessment
23 - {id: CC3.1, family: Risk Assessment, title: "The entity specifies objectives with sufficient clarity to enable identification of risks."}
24 - {id: CC3.2, family: Risk Assessment, title: "The entity identifies and analyzes risks to the achievement of its objectives."}
25 - {id: CC3.3, family: Risk Assessment, title: "The entity considers the potential for fraud in assessing risks."}
26 - {id: CC3.4, family: Risk Assessment, title: "The entity identifies and assesses changes that could significantly affect internal control."}
31 - {id: CC3.1, family: Risk Assessment, title: "Objectives specified clearly enough to identify risk"}
32 - {id: CC3.2, family: Risk Assessment, title: "Identification and analysis of risks to objectives"}
33 - {id: CC3.3, family: Risk Assessment, title: "Fraud potential considered in risk assessment"}
34 - {id: CC3.4, family: Risk Assessment, title: "Assessment of changes affecting internal control"}
2735 # CC4 — Monitoring Activities
28 - {id: CC4.1, family: Monitoring Activities, title: "The entity selects, develops, and performs ongoing and separate evaluations of internal control."}
29 - {id: CC4.2, family: Monitoring Activities, title: "The entity evaluates and communicates internal control deficiencies in a timely manner."}
36 - {id: CC4.1, family: Monitoring Activities, title: "Ongoing and separate evaluations of internal control"}
37 - {id: CC4.2, family: Monitoring Activities, title: "Timely evaluation and reporting of control deficiencies"}
3038 # CC5 — Control Activities
31 - {id: CC5.1, family: Control Activities, title: "The entity selects and develops control activities that mitigate risks to acceptable levels."}
32 - {id: CC5.2, family: Control Activities, title: "The entity selects and develops general control activities over technology."}
33 - {id: CC5.3, family: Control Activities, title: "The entity deploys control activities through policies and procedures."}
39 - {id: CC5.1, family: Control Activities, title: "Control activities selected to mitigate risk"}
40 - {id: CC5.2, family: Control Activities, title: "General technology controls developed"}
41 - {id: CC5.3, family: Control Activities, title: "Control activities deployed via policies and procedures"}
3442 # CC6 — Logical and Physical Access Controls
35 - {id: CC6.1, family: Logical and Physical Access Controls, title: "The entity implements logical access security software, infrastructure, and architectures over protected assets."}
36 - {id: CC6.2, family: Logical and Physical Access Controls, title: "The entity registers and authorizes new users before granting access, and removes access when appropriate."}
37 - {id: CC6.3, family: Logical and Physical Access Controls, title: "The entity authorizes, modifies, or removes access based on roles and least privilege."}
38 - {id: CC6.4, family: Logical and Physical Access Controls, title: "The entity restricts physical access to facilities and protected information assets."}
39 - {id: CC6.5, family: Logical and Physical Access Controls, title: "The entity discontinues logical and physical protections over assets only after the ability to read data has been removed."}
40 - {id: CC6.6, family: Logical and Physical Access Controls, title: "The entity implements logical access security measures against threats from outside its system boundaries."}
41 - {id: CC6.7, family: Logical and Physical Access Controls, title: "The entity restricts the transmission, movement, and removal of information to authorized users and processes."}
42 - {id: CC6.8, family: Logical and Physical Access Controls, title: "The entity implements controls to prevent or detect and act upon unauthorized or malicious software."}
43 - {id: CC6.1, family: Logical and Physical Access Controls, title: "Logical access security over protected assets"}
44 - {id: CC6.2, family: Logical and Physical Access Controls, title: "User registration, authorization, and deprovisioning"}
45 - {id: CC6.3, family: Logical and Physical Access Controls, title: "Role- and least-privilege-based access management"}
46 - {id: CC6.4, family: Logical and Physical Access Controls, title: "Physical access restricted to facilities and assets"}
47 - {id: CC6.5, family: Logical and Physical Access Controls, title: "Protections removed only after data made unreadable"}
48 - {id: CC6.6, family: Logical and Physical Access Controls, title: "Perimeter defenses against external threats"}
49 - {id: CC6.7, family: Logical and Physical Access Controls, title: "Restricted transmission and removal of information"}
50 - {id: CC6.8, family: Logical and Physical Access Controls, title: "Prevention and detection of unauthorized software"}
4351 # CC7 — System Operations
44 - {id: CC7.1, family: System Operations, title: "The entity uses detection and monitoring procedures to identify configuration changes and new vulnerabilities."}
45 - {id: CC7.2, family: System Operations, title: "The entity monitors system components for anomalies indicative of malicious acts or errors."}
46 - {id: CC7.3, family: System Operations, title: "The entity evaluates security events to determine whether they could or did result in a failure to meet objectives."}
47 - {id: CC7.4, family: System Operations, title: "The entity responds to identified security incidents through a defined program."}
48 - {id: CC7.5, family: System Operations, title: "The entity identifies, develops, and implements activities to recover from security incidents."}
52 - {id: CC7.1, family: System Operations, title: "Detection of configuration changes and vulnerabilities"}
53 - {id: CC7.2, family: System Operations, title: "Monitoring for anomalies indicating malicious acts"}
54 - {id: CC7.3, family: System Operations, title: "Evaluation of security events against objectives"}
55 - {id: CC7.4, family: System Operations, title: "Defined security-incident response program"}
56 - {id: CC7.5, family: System Operations, title: "Recovery from security incidents"}
4957 # CC8 — Change Management
50 - {id: CC8.1, family: Change Management, title: "The entity authorizes, designs, develops, tests, approves, and implements changes to infrastructure, data, and software."}
58 - {id: CC8.1, family: Change Management, title: "Change management across infrastructure, data, and software"}
5159 # CC9 — Risk Mitigation
52 - {id: CC9.1, family: Risk Mitigation, title: "The entity identifies, selects, and develops risk mitigation activities for disruptions."}
53 - {id: CC9.2, family: Risk Mitigation, title: "The entity assesses and manages risks associated with vendors and business partners."}
60 - {id: CC9.1, family: Risk Mitigation, title: "Risk-mitigation activities for business disruptions"}
61 - {id: CC9.2, family: Risk Mitigation, title: "Vendor and business-partner risk management"}
5462 # Availability category
55 - {id: A1.1, family: Availability, title: "The entity maintains, monitors, and evaluates current processing capacity to meet demand."}
56 - {id: A1.2, family: Availability, title: "The entity authorizes, designs, and implements environmental protections, backup, and recovery infrastructure."}
57 - {id: A1.3, family: Availability, title: "The entity tests recovery plan procedures supporting system recovery."}
63 - {id: A1.1, family: Availability, title: "Processing-capacity monitoring against demand"}
64 - {id: A1.2, family: Availability, title: "Environmental protections, backup, and recovery infrastructure"}
65 - {id: A1.3, family: Availability, title: "Recovery-plan testing"}
5866 # Confidentiality category
59 - {id: C1.1, family: Confidentiality, title: "The entity identifies and maintains confidential information to meet its objectives related to confidentiality."}
60 - {id: C1.2, family: Confidentiality, title: "The entity disposes of confidential information to meet its objectives related to confidentiality."}
67 - {id: C1.1, family: Confidentiality, title: "Identification and safeguarding of confidential information"}
68 - {id: C1.2, family: Confidentiality, title: "Disposal of confidential information"}
6169 # Processing Integrity category
62 - {id: PI1.1, family: Processing Integrity, title: "The entity obtains or generates, uses, and communicates relevant, quality information about processing objectives, including product and service specifications."}
63 - {id: PI1.2, family: Processing Integrity, title: "The entity implements policies and procedures over system inputs, including controls over completeness and accuracy, to meet its objectives."}
64 - {id: PI1.3, family: Processing Integrity, title: "The entity implements policies and procedures over system processing to result in products, services, and reporting that meet its objectives."}
65 - {id: PI1.4, family: Processing Integrity, title: "The entity implements policies and procedures to make available or deliver output completely, accurately, and in a timely manner to meet its objectives."}
66 - {id: PI1.5, family: Processing Integrity, title: "The entity implements policies and procedures to store inputs, items in processing, and outputs completely, accurately, and in a timely manner to meet its objectives."}
70 - {id: PI1.1, family: Processing Integrity, title: "Quality information about processing objectives and specs"}
71 - {id: PI1.2, family: Processing Integrity, title: "Input controls for completeness and accuracy"}
72 - {id: PI1.3, family: Processing Integrity, title: "Processing controls producing objective-meeting output"}
73 - {id: PI1.4, family: Processing Integrity, title: "Output delivered completely, accurately, and on time"}
74 - {id: PI1.5, family: Processing Integrity, title: "Storage of inputs, work in process, and outputs"}
6775 # Privacy category
68 - {id: P1.1, family: Privacy, title: "The entity provides notice to data subjects about its privacy practices to meet its objectives related to privacy."}
69 - {id: P2.1, family: Privacy, title: "The entity communicates choices about the collection, use, retention, disclosure, and disposal of personal information, and obtains consent, to meet its privacy objectives."}
70 - {id: P3.1, family: Privacy, title: "Personal information is collected consistent with the entity's objectives related to privacy."}
71 - {id: P3.2, family: Privacy, title: "For information requiring explicit consent, the entity communicates the need for and obtains consent prior to collection of personal information."}
72 - {id: P4.1, family: Privacy, title: "The entity limits the use of personal information to the purposes identified in its objectives related to privacy."}
73 - {id: P4.2, family: Privacy, title: "The entity retains personal information consistent with its objectives related to privacy."}
74 - {id: P4.3, family: Privacy, title: "The entity securely disposes of personal information to meet its objectives related to privacy."}
75 - {id: P5.1, family: Privacy, title: "The entity grants data subjects the ability to access their stored personal information for review and, upon request, provides copies, to meet its privacy objectives."}
76 - {id: P5.2, family: Privacy, title: "The entity corrects, amends, or appends personal information based on data subject input and communicates it to third parties, to meet its privacy objectives."}
77 - {id: P6.1, family: Privacy, title: "The entity discloses personal information to third parties only with the explicit consent of data subjects and consistent with its privacy objectives."}
78 - {id: P6.2, family: Privacy, title: "The entity creates and retains a complete, accurate, and timely record of authorized disclosures of personal information."}
79 - {id: P6.3, family: Privacy, title: "The entity creates and retains a complete, accurate, and timely record of detected or reported unauthorized disclosures of personal information."}
80 - {id: P6.4, family: Privacy, title: "The entity obtains privacy commitments from vendors and other third parties who have access to personal information, to meet its privacy objectives."}
81 - {id: P6.5, family: Privacy, title: "The entity obtains commitments from vendors and third parties to notify it of actual or suspected unauthorized disclosures of personal information."}
82 - {id: P6.6, family: Privacy, title: "The entity provides notification of breaches and incidents of unauthorized disclosure of personal information to affected data subjects, regulators, and others."}
83 - {id: P6.7, family: Privacy, title: "The entity provides data subjects with an accounting of the personal information held and disclosures made, upon request."}
84 - {id: P7.1, family: Privacy, title: "The entity collects and maintains accurate, up-to-date, complete, and relevant personal information to meet its privacy objectives."}
85 - {id: P8.1, family: Privacy, title: "The entity implements a process for receiving, addressing, resolving, and communicating the resolution of privacy inquiries, complaints, and disputes."}
76 - {id: P1.1, family: Privacy, title: "Notice of privacy practices to data subjects"}
77 - {id: P2.1, family: Privacy, title: "Choice and consent over personal-information handling"}
78 - {id: P3.1, family: Privacy, title: "Collection consistent with privacy objectives"}
79 - {id: P3.2, family: Privacy, title: "Explicit consent obtained before collection where required"}
80 - {id: P4.1, family: Privacy, title: "Use of personal information limited to stated purposes"}
81 - {id: P4.2, family: Privacy, title: "Retention of personal information per objectives"}
82 - {id: P4.3, family: Privacy, title: "Secure disposal of personal information"}
83 - {id: P5.1, family: Privacy, title: "Data-subject access to their personal information"}
84 - {id: P5.2, family: Privacy, title: "Correction and amendment of personal information"}
85 - {id: P6.1, family: Privacy, title: "Third-party disclosure only with consent"}
86 - {id: P6.2, family: Privacy, title: "Record of authorized disclosures"}
87 - {id: P6.3, family: Privacy, title: "Record of unauthorized disclosures"}
88 - {id: P6.4, family: Privacy, title: "Privacy commitments obtained from vendors and third parties"}
89 - {id: P6.5, family: Privacy, title: "Vendor commitments to notify of unauthorized disclosure"}
90 - {id: P6.6, family: Privacy, title: "Breach notification to affected parties and regulators"}
91 - {id: P6.7, family: Privacy, title: "Accounting of personal information and disclosures on request"}
92 - {id: P7.1, family: Privacy, title: "Accuracy and currency of personal information"}
93 - {id: P8.1, family: Privacy, title: "Handling of privacy inquiries, complaints, and disputes"}
control_coverage/reporters/html.py +7 −1
@@ -9,6 +9,7 @@ from __future__ import annotations
99from html import escape
1010from typing import TYPE_CHECKING
1111
12from .. import __version__
1213from ..coverage import (
1314 ASSERTED,
1415 FAILING,
@@ -214,7 +215,11 @@ def _blind_spots(report: CoverageReport) -> str:
214215
215216def render(report: CoverageReport) -> str:
216217 title = report.subject or "Evidence corpus"
217 frameworks = ", ".join(fc.catalog.framework for fc in report.frameworks)
218 frameworks = ", ".join(
219 f"{fc.catalog.framework} {fc.catalog.version} "
220 f"(sha256:{fc.catalog.sha256[:12]})"
221 for fc in report.frameworks
222 )
218223 body = [
219224 "<!doctype html><html lang='en'><head><meta charset='utf-8'>",
220225 "<meta name='viewport' content='width=device-width, initial-scale=1'>",
@@ -223,6 +228,7 @@ def render(report: CoverageReport) -> str:
223228 f"<h1>Control Coverage — {escape(title)}</h1>",
224229 (
225230 f'<p class="meta">Generated {escape(report.generated_at)} · '
231 f"Tool control-coverage {escape(__version__)} · "
226232 f"{report.source_count} evidence source(s) · frameworks: {escape(frameworks)}</p>"
227233 ),
228234 (
control_coverage/reporters/json.py +4
@@ -9,6 +9,8 @@ from __future__ import annotations
99import json as _json
1010from typing import TYPE_CHECKING
1111
12from .. import __version__
13
1214if TYPE_CHECKING:
1315 from ..coverage import CoverageReport
1416
@@ -18,11 +20,13 @@ def to_dict(report: CoverageReport) -> dict:
1820 "subject": report.subject,
1921 "generated_at": report.generated_at,
2022 "source_count": report.source_count,
23 "tool": {"name": "control-coverage", "version": __version__},
2124 "frameworks": [
2225 {
2326 "framework": fc.catalog.framework,
2427 "name": fc.catalog.name,
2528 "version": fc.catalog.version,
29 "sha256": fc.catalog.sha256,
2630 "catalog_coverage": fc.catalog.coverage,
2731 "in_scope": fc.in_scope,
2832 "addressed": fc.addressed,
control_coverage/reporters/markdown.py +6 −1
@@ -4,6 +4,7 @@ from __future__ import annotations
44
55from typing import TYPE_CHECKING
66
7from .. import __version__
78from ..coverage import (
89 ASSERTED,
910 FAILING,
@@ -108,8 +109,12 @@ def render(report: CoverageReport) -> str:
108109 out.append(f"# Control Coverage — {title}")
109110 out.append("")
110111 out.append(f"- **Generated:** {report.generated_at}")
112 out.append(f"- **Tool:** control-coverage {__version__}")
111113 out.append(f"- **Corpus:** {report.source_count} evidence source(s)")
112 frameworks = ", ".join(fc.catalog.framework for fc in report.frameworks)
114 frameworks = ", ".join(
115 f"{fc.catalog.framework} {fc.catalog.version} (`sha256:{fc.catalog.sha256[:12]}`)"
116 for fc in report.frameworks
117 )
113118 out.append(f"- **Frameworks:** {frameworks}")
114119 out.append("")
115120 out.append(
tests/test_reporters.py +9
@@ -34,6 +34,15 @@ def test_json_is_valid_and_structured():
3434 assert "unaddressed" in states
3535
3636
37def test_json_stamps_tool_and_catalog_provenance():
38 from control_coverage import __version__
39
40 doc = _json.loads(reporters.render(_report(), "json"))
41 assert doc["tool"] == {"name": "control-coverage", "version": __version__}
42 soc2 = doc["frameworks"][0]
43 assert len(soc2["sha256"]) == 64 # full SHA-256 hex digest of the catalog file
44
45
3746def test_html_is_self_contained():
3847 html = reporters.render(_report(), "html")
3948 assert html.startswith("<!doctype html>")