audit-labs/control-coverage

Control coverage and blind-spot analysis for audit evidence.

clone: git clone https://gitbay.org/audit-labs/control-coverage.git

653b90a91d22b8290b57626d398ff5cb8efbbf37

verified · cmc

author: Christian Cleberg <hello@cleberg.net> · 2026-08-07T02:13:32Z

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
 .github/workflows/ci.yml                 |  26 ++++++
 MAPPING.md                               |  62 ++++++++++++++
 control_coverage/catalog.py              |   6 +-
 control_coverage/catalogs/iso27001.yaml  |   9 +-
 control_coverage/catalogs/nist80053.yaml |   4 +
 control_coverage/catalogs/soc2.yaml      | 136 ++++++++++++++++---------------
 control_coverage/reporters/html.py       |   8 +-
 control_coverage/reporters/json.py       |   4 +
 control_coverage/reporters/markdown.py   |   7 +-
 tests/test_reporters.py                  |   9 ++
 10 files changed, 203 insertions(+), 68 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..affc03a
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,26 @@
+name: CI
+
+on:
+  push:
+  pull_request:
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: ["3.10", "3.12"]
+    steps:
+      - uses: actions/checkout@v5
+      - name: Set up Python ${{ matrix.python-version }}
+        uses: actions/setup-python@v6
+        with:
+          python-version: ${{ matrix.python-version }}
+      - name: Install
+        run: |
+          python -m pip install --upgrade pip
+          pip install -e ".[dev]"
+      - name: Ruff
+        run: ruff check .
+      - name: Tests
+        run: pytest -q
diff --git a/MAPPING.md b/MAPPING.md
new file mode 100644
index 0000000..814ecdc
--- /dev/null
+++ b/MAPPING.md
@@ -0,0 +1,62 @@
+# Control catalogs — provenance and rationale
+
+`control-coverage` measures how much of a framework an evidence corpus addresses.
+The **denominator** is a framework catalog: the complete list of controls the
+framework defines. This document records where those catalogs come from, how they
+are versioned, and the limits of what they claim.
+
+## What these catalogs are — and are not
+
+- They enumerate control **identifiers** (e.g. `SOC2:CC6.1`, `ISO:A.5.17`,
+  `NIST:AC-2`) plus a short title used as a display label.
+- They are **not** the normative control text. For authoritative wording, consult
+  the source standard.
+- Mapping a control to an evidence signal is the **maintainers' interpretation**.
+  It is not reviewed or endorsed by the AICPA, ISO/IEC, or NIST.
+- Coverage is a measure of **evidence**, not of compliance. A control counted as
+  "addressed" means the corpus contains a signal relevant to it — not that the
+  control operates effectively. That judgment belongs to the organization and its
+  auditor.
+
+## Sources and revisions
+
+| Framework | Catalog file | Revision used | Scope |
+|---|---|---|---|
+| 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 |
+| ISO/IEC 27001 | `catalogs/iso27001.yaml` | 27001:2022 Annex A | All 93 Annex A controls, four themes |
+| NIST SP 800-53 | `catalogs/nist80053.yaml` | Rev. 5 / SP 800-53B **Moderate** baseline | 177 base controls (see below) |
+
+### How the NIST count is 177
+
+The NIST catalog is the base controls selected in the **SP 800-53B Moderate**
+impact baseline, across the 18 baseline-applicable families. Control
+**enhancements** (e.g. `AC-2(1)`) are not enumerated — coverage is measured at the
+base-control level. The Program Management (PM) family is organization-wide and
+not baseline-allocated; the Privacy (PT) family is selected via the separate
+privacy baseline. That selection is 177 base controls.
+
+## Versioning and traceability
+
+- Each catalog carries a `version` field, and every report stamps the catalog
+  `version` **and a SHA-256 of the catalog file** into its output (`tool` and
+  `frameworks[].sha256` in JSON; the header line in Markdown/HTML).
+- This lets an auditor tie any coverage result back to the exact denominator that
+  produced it, and re-perform against it.
+- Change the control set or a title and the SHA-256 changes; bump `version` on any
+  substantive change.
+
+## Authorship and review
+
+- **Author:** the audit-labs maintainer.
+- **Review status:** maintainer self-review. These catalogs have **not** been
+  through independent professional review; treat them accordingly and validate
+  against the source standards before relying on them in an engagement.
+- **Effective date:** 2026-08.
+
+## Copyright
+
+- **SOC 2 / Trust Services Criteria** — copyright AICPA. Only identifiers are
+  reproduced; titles are our own short-form paraphrases, not the criteria text.
+- **ISO/IEC 27001:2022** — copyright ISO/IEC. Only Annex A identifiers and short
+  titles are reproduced; normative text and guidance are not.
+- **NIST SP 800-53** — U.S. Government work in the public domain.
diff --git a/control_coverage/catalog.py b/control_coverage/catalog.py
index f81625c..ac05cf4 100644
--- a/control_coverage/catalog.py
+++ b/control_coverage/catalog.py
@@ -15,6 +15,7 @@ Control codes are written ``FRAMEWORK:ID`` (for example ``SOC2:CC6.1``,
 
 from __future__ import annotations
 
+import hashlib
 from dataclasses import dataclass
 from pathlib import Path
 
@@ -60,6 +61,7 @@ class Catalog:
     coverage: str  # "complete" or "partial"
     source: str
     controls: list[Control]
+    sha256: str = ""  # digest of the catalog file, so coverage ties to a mapping
 
     @property
     def complete(self) -> bool:
@@ -86,7 +88,8 @@ def _resolve(name: str) -> Path:
 def load(name: str) -> Catalog:
     """Load a bundled catalog by framework name, short code, or alias."""
     path = _resolve(name)
-    raw = yaml.safe_load(path.read_text(encoding="utf-8"))
+    text = path.read_text(encoding="utf-8")
+    raw = yaml.safe_load(text)
     framework = raw["framework"]
     controls = [
         Control(
@@ -104,6 +107,7 @@ def load(name: str) -> Catalog:
         coverage=raw.get("coverage", "partial"),
         source=raw.get("source", ""),
         controls=controls,
+        sha256=hashlib.sha256(text.encode("utf-8")).hexdigest(),
     )
 
 
diff --git a/control_coverage/catalogs/iso27001.yaml b/control_coverage/catalogs/iso27001.yaml
index be4daf5..fbf9f83 100644
--- a/control_coverage/catalogs/iso27001.yaml
+++ b/control_coverage/catalogs/iso27001.yaml
@@ -2,11 +2,18 @@
 #
 # This is exactly the list a Statement of Applicability enumerates. A control's
 # full code is "ISO:<id>", matching the codes audit-report rulesets cite.
+#
+# COPYRIGHT: ISO/IEC 27001:2022 is copyright ISO/IEC. Only the Annex A control
+# identifiers (e.g. A.5.17) and their short titles are reproduced here as labels;
+# the normative control text and implementation guidance are not. For the
+# authoritative wording, obtain the standard from ISO. These control-to-signal
+# mappings are the maintainers' interpretation and are not reviewed or endorsed
+# by ISO/IEC. See ../../MAPPING.md.
 framework: ISO
 name: ISO/IEC 27001:2022 Annex A
 version: "2022"
 coverage: complete
-source: ISO/IEC 27001:2022 Annex A
+source: ISO/IEC 27001:2022 Annex A (identifiers and short titles only)
 controls:
   # A.5 — Organizational controls
   - {id: A.5.1, family: Organizational, title: "Policies for information security."}
diff --git a/control_coverage/catalogs/nist80053.yaml b/control_coverage/catalogs/nist80053.yaml
index be26cc5..b35e23f 100644
--- a/control_coverage/catalogs/nist80053.yaml
+++ b/control_coverage/catalogs/nist80053.yaml
@@ -6,6 +6,10 @@
 # management (PM) family is org-wide and not baseline-allocated; the privacy (PT)
 # family is selected via the separate privacy baseline. A control's full code is
 # "NIST:<id>".
+#
+# COPYRIGHT: NIST SP 800-53 is a U.S. Government work in the public domain. The
+# control-to-signal mappings, however, are the maintainers' interpretation and
+# are not reviewed or endorsed by NIST. See ../../MAPPING.md.
 framework: NIST
 name: NIST SP 800-53 Rev. 5 (Moderate baseline)
 version: "Rev. 5"
diff --git a/control_coverage/catalogs/soc2.yaml b/control_coverage/catalogs/soc2.yaml
index f0b2e91..0c890d6 100644
--- a/control_coverage/catalogs/soc2.yaml
+++ b/control_coverage/catalogs/soc2.yaml
@@ -1,85 +1,93 @@
 # SOC 2 — Trust Services Criteria (AICPA, 2017 with 2022 revised points of focus).
 #
 # The full Common Criteria (the "Security" category every SOC 2 report covers)
-# plus the Availability category. A control's full code is "SOC2:<id>", matching
-# the codes audit-report rulesets cite.
+# plus the Availability, Confidentiality, Processing Integrity, and Privacy
+# categories. A control's full code is "SOC2:<id>", matching the codes
+# audit-report rulesets cite.
+#
+# COPYRIGHT: The Trust Services Criteria are copyright AICPA. The `title` fields
+# below are our own short-form paraphrases used as labels — not the normative
+# criteria text. Only the criterion identifiers (e.g. CC6.1) are reproduced. For
+# the authoritative wording and points of focus, consult the AICPA TSC. These
+# control-to-signal mappings are the maintainers' interpretation and are not
+# reviewed or endorsed by the AICPA. See ../../MAPPING.md.
 framework: SOC2
 name: SOC 2 (Trust Services Criteria)
 version: "2017 (rev. 2022)"
 coverage: complete
-source: AICPA Trust Services Criteria
+source: AICPA Trust Services Criteria (identifiers only; titles paraphrased)
 controls:
   # CC1 — Control Environment
-  - {id: CC1.1, family: Control Environment, title: "The entity demonstrates a commitment to integrity and ethical values."}
-  - {id: CC1.2, family: Control Environment, title: "The board of directors demonstrates independence and exercises oversight of internal control."}
-  - {id: CC1.3, family: Control Environment, title: "Management establishes structures, reporting lines, and appropriate authorities and responsibilities."}
-  - {id: CC1.4, family: Control Environment, title: "The entity demonstrates a commitment to attract, develop, and retain competent individuals."}
-  - {id: CC1.5, family: Control Environment, title: "The entity holds individuals accountable for their internal control responsibilities."}
+  - {id: CC1.1, family: Control Environment, title: "Commitment to integrity and ethical values"}
+  - {id: CC1.2, family: Control Environment, title: "Board independence and internal-control oversight"}
+  - {id: CC1.3, family: Control Environment, title: "Structures, reporting lines, and authorities established"}
+  - {id: CC1.4, family: Control Environment, title: "Commitment to attracting and retaining competent people"}
+  - {id: CC1.5, family: Control Environment, title: "Accountability for internal-control responsibilities"}
   # CC2 — Communication and Information
-  - {id: CC2.1, family: Communication and Information, title: "The entity obtains or generates relevant, quality information to support internal control."}
-  - {id: CC2.2, family: Communication and Information, title: "The entity internally communicates information, including objectives and responsibilities for internal control."}
-  - {id: CC2.3, family: Communication and Information, title: "The entity communicates with external parties about matters affecting internal control."}
+  - {id: CC2.1, family: Communication and Information, title: "Relevant, quality information supporting internal control"}
+  - {id: CC2.2, family: Communication and Information, title: "Internal communication of control objectives and duties"}
+  - {id: CC2.3, family: Communication and Information, title: "External communication on internal-control matters"}
   # CC3 — Risk Assessment
-  - {id: CC3.1, family: Risk Assessment, title: "The entity specifies objectives with sufficient clarity to enable identification of risks."}
-  - {id: CC3.2, family: Risk Assessment, title: "The entity identifies and analyzes risks to the achievement of its objectives."}
-  - {id: CC3.3, family: Risk Assessment, title: "The entity considers the potential for fraud in assessing risks."}
-  - {id: CC3.4, family: Risk Assessment, title: "The entity identifies and assesses changes that could significantly affect internal control."}
+  - {id: CC3.1, family: Risk Assessment, title: "Objectives specified clearly enough to identify risk"}
+  - {id: CC3.2, family: Risk Assessment, title: "Identification and analysis of risks to objectives"}
+  - {id: CC3.3, family: Risk Assessment, title: "Fraud potential considered in risk assessment"}
+  - {id: CC3.4, family: Risk Assessment, title: "Assessment of changes affecting internal control"}
   # CC4 — Monitoring Activities
-  - {id: CC4.1, family: Monitoring Activities, title: "The entity selects, develops, and performs ongoing and separate evaluations of internal control."}
-  - {id: CC4.2, family: Monitoring Activities, title: "The entity evaluates and communicates internal control deficiencies in a timely manner."}
+  - {id: CC4.1, family: Monitoring Activities, title: "Ongoing and separate evaluations of internal control"}
+  - {id: CC4.2, family: Monitoring Activities, title: "Timely evaluation and reporting of control deficiencies"}
   # CC5 — Control Activities
-  - {id: CC5.1, family: Control Activities, title: "The entity selects and develops control activities that mitigate risks to acceptable levels."}
-  - {id: CC5.2, family: Control Activities, title: "The entity selects and develops general control activities over technology."}
-  - {id: CC5.3, family: Control Activities, title: "The entity deploys control activities through policies and procedures."}
+  - {id: CC5.1, family: Control Activities, title: "Control activities selected to mitigate risk"}
+  - {id: CC5.2, family: Control Activities, title: "General technology controls developed"}
+  - {id: CC5.3, family: Control Activities, title: "Control activities deployed via policies and procedures"}
   # CC6 — Logical and Physical Access Controls
-  - {id: CC6.1, family: Logical and Physical Access Controls, title: "The entity implements logical access security software, infrastructure, and architectures over protected assets."}
-  - {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."}
-  - {id: CC6.3, family: Logical and Physical Access Controls, title: "The entity authorizes, modifies, or removes access based on roles and least privilege."}
-  - {id: CC6.4, family: Logical and Physical Access Controls, title: "The entity restricts physical access to facilities and protected information assets."}
-  - {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."}
-  - {id: CC6.6, family: Logical and Physical Access Controls, title: "The entity implements logical access security measures against threats from outside its system boundaries."}
-  - {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."}
-  - {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."}
+  - {id: CC6.1, family: Logical and Physical Access Controls, title: "Logical access security over protected assets"}
+  - {id: CC6.2, family: Logical and Physical Access Controls, title: "User registration, authorization, and deprovisioning"}
+  - {id: CC6.3, family: Logical and Physical Access Controls, title: "Role- and least-privilege-based access management"}
+  - {id: CC6.4, family: Logical and Physical Access Controls, title: "Physical access restricted to facilities and assets"}
+  - {id: CC6.5, family: Logical and Physical Access Controls, title: "Protections removed only after data made unreadable"}
+  - {id: CC6.6, family: Logical and Physical Access Controls, title: "Perimeter defenses against external threats"}
+  - {id: CC6.7, family: Logical and Physical Access Controls, title: "Restricted transmission and removal of information"}
+  - {id: CC6.8, family: Logical and Physical Access Controls, title: "Prevention and detection of unauthorized software"}
   # CC7 — System Operations
-  - {id: CC7.1, family: System Operations, title: "The entity uses detection and monitoring procedures to identify configuration changes and new vulnerabilities."}
-  - {id: CC7.2, family: System Operations, title: "The entity monitors system components for anomalies indicative of malicious acts or errors."}
-  - {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."}
-  - {id: CC7.4, family: System Operations, title: "The entity responds to identified security incidents through a defined program."}
-  - {id: CC7.5, family: System Operations, title: "The entity identifies, develops, and implements activities to recover from security incidents."}
+  - {id: CC7.1, family: System Operations, title: "Detection of configuration changes and vulnerabilities"}
+  - {id: CC7.2, family: System Operations, title: "Monitoring for anomalies indicating malicious acts"}
+  - {id: CC7.3, family: System Operations, title: "Evaluation of security events against objectives"}
+  - {id: CC7.4, family: System Operations, title: "Defined security-incident response program"}
+  - {id: CC7.5, family: System Operations, title: "Recovery from security incidents"}
   # CC8 — Change Management
-  - {id: CC8.1, family: Change Management, title: "The entity authorizes, designs, develops, tests, approves, and implements changes to infrastructure, data, and software."}
+  - {id: CC8.1, family: Change Management, title: "Change management across infrastructure, data, and software"}
   # CC9 — Risk Mitigation
-  - {id: CC9.1, family: Risk Mitigation, title: "The entity identifies, selects, and develops risk mitigation activities for disruptions."}
-  - {id: CC9.2, family: Risk Mitigation, title: "The entity assesses and manages risks associated with vendors and business partners."}
+  - {id: CC9.1, family: Risk Mitigation, title: "Risk-mitigation activities for business disruptions"}
+  - {id: CC9.2, family: Risk Mitigation, title: "Vendor and business-partner risk management"}
   # Availability category
-  - {id: A1.1, family: Availability, title: "The entity maintains, monitors, and evaluates current processing capacity to meet demand."}
-  - {id: A1.2, family: Availability, title: "The entity authorizes, designs, and implements environmental protections, backup, and recovery infrastructure."}
-  - {id: A1.3, family: Availability, title: "The entity tests recovery plan procedures supporting system recovery."}
+  - {id: A1.1, family: Availability, title: "Processing-capacity monitoring against demand"}
+  - {id: A1.2, family: Availability, title: "Environmental protections, backup, and recovery infrastructure"}
+  - {id: A1.3, family: Availability, title: "Recovery-plan testing"}
   # Confidentiality category
-  - {id: C1.1, family: Confidentiality, title: "The entity identifies and maintains confidential information to meet its objectives related to confidentiality."}
-  - {id: C1.2, family: Confidentiality, title: "The entity disposes of confidential information to meet its objectives related to confidentiality."}
+  - {id: C1.1, family: Confidentiality, title: "Identification and safeguarding of confidential information"}
+  - {id: C1.2, family: Confidentiality, title: "Disposal of confidential information"}
   # Processing Integrity category
-  - {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."}
-  - {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."}
-  - {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."}
-  - {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."}
-  - {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."}
+  - {id: PI1.1, family: Processing Integrity, title: "Quality information about processing objectives and specs"}
+  - {id: PI1.2, family: Processing Integrity, title: "Input controls for completeness and accuracy"}
+  - {id: PI1.3, family: Processing Integrity, title: "Processing controls producing objective-meeting output"}
+  - {id: PI1.4, family: Processing Integrity, title: "Output delivered completely, accurately, and on time"}
+  - {id: PI1.5, family: Processing Integrity, title: "Storage of inputs, work in process, and outputs"}
   # Privacy category
-  - {id: P1.1, family: Privacy, title: "The entity provides notice to data subjects about its privacy practices to meet its objectives related to privacy."}
-  - {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."}
-  - {id: P3.1, family: Privacy, title: "Personal information is collected consistent with the entity's objectives related to privacy."}
-  - {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."}
-  - {id: P4.1, family: Privacy, title: "The entity limits the use of personal information to the purposes identified in its objectives related to privacy."}
-  - {id: P4.2, family: Privacy, title: "The entity retains personal information consistent with its objectives related to privacy."}
-  - {id: P4.3, family: Privacy, title: "The entity securely disposes of personal information to meet its objectives related to privacy."}
-  - {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."}
-  - {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."}
-  - {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."}
-  - {id: P6.2, family: Privacy, title: "The entity creates and retains a complete, accurate, and timely record of authorized disclosures of personal information."}
-  - {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."}
-  - {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."}
-  - {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."}
-  - {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."}
-  - {id: P6.7, family: Privacy, title: "The entity provides data subjects with an accounting of the personal information held and disclosures made, upon request."}
-  - {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."}
-  - {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."}
+  - {id: P1.1, family: Privacy, title: "Notice of privacy practices to data subjects"}
+  - {id: P2.1, family: Privacy, title: "Choice and consent over personal-information handling"}
+  - {id: P3.1, family: Privacy, title: "Collection consistent with privacy objectives"}
+  - {id: P3.2, family: Privacy, title: "Explicit consent obtained before collection where required"}
+  - {id: P4.1, family: Privacy, title: "Use of personal information limited to stated purposes"}
+  - {id: P4.2, family: Privacy, title: "Retention of personal information per objectives"}
+  - {id: P4.3, family: Privacy, title: "Secure disposal of personal information"}
+  - {id: P5.1, family: Privacy, title: "Data-subject access to their personal information"}
+  - {id: P5.2, family: Privacy, title: "Correction and amendment of personal information"}
+  - {id: P6.1, family: Privacy, title: "Third-party disclosure only with consent"}
+  - {id: P6.2, family: Privacy, title: "Record of authorized disclosures"}
+  - {id: P6.3, family: Privacy, title: "Record of unauthorized disclosures"}
+  - {id: P6.4, family: Privacy, title: "Privacy commitments obtained from vendors and third parties"}
+  - {id: P6.5, family: Privacy, title: "Vendor commitments to notify of unauthorized disclosure"}
+  - {id: P6.6, family: Privacy, title: "Breach notification to affected parties and regulators"}
+  - {id: P6.7, family: Privacy, title: "Accounting of personal information and disclosures on request"}
+  - {id: P7.1, family: Privacy, title: "Accuracy and currency of personal information"}
+  - {id: P8.1, family: Privacy, title: "Handling of privacy inquiries, complaints, and disputes"}
diff --git a/control_coverage/reporters/html.py b/control_coverage/reporters/html.py
index e1798a7..d57a517 100644
--- a/control_coverage/reporters/html.py
+++ b/control_coverage/reporters/html.py
@@ -9,6 +9,7 @@ from __future__ import annotations
 from html import escape
 from typing import TYPE_CHECKING
 
+from .. import __version__
 from ..coverage import (
     ASSERTED,
     FAILING,
@@ -214,7 +215,11 @@ def _blind_spots(report: CoverageReport) -> str:
 
 def render(report: CoverageReport) -> str:
     title = report.subject or "Evidence corpus"
-    frameworks = ", ".join(fc.catalog.framework for fc in report.frameworks)
+    frameworks = ", ".join(
+        f"{fc.catalog.framework} {fc.catalog.version} "
+        f"(sha256:{fc.catalog.sha256[:12]})"
+        for fc in report.frameworks
+    )
     body = [
         "<!doctype html><html lang='en'><head><meta charset='utf-8'>",
         "<meta name='viewport' content='width=device-width, initial-scale=1'>",
@@ -223,6 +228,7 @@ def render(report: CoverageReport) -> str:
         f"<h1>Control Coverage — {escape(title)}</h1>",
         (
             f'<p class="meta">Generated {escape(report.generated_at)} · '
+            f"Tool control-coverage {escape(__version__)} · "
             f"{report.source_count} evidence source(s) · frameworks: {escape(frameworks)}</p>"
         ),
         (
diff --git a/control_coverage/reporters/json.py b/control_coverage/reporters/json.py
index 2b9db1a..32b8f81 100644
--- a/control_coverage/reporters/json.py
+++ b/control_coverage/reporters/json.py
@@ -9,6 +9,8 @@ from __future__ import annotations
 import json as _json
 from typing import TYPE_CHECKING
 
+from .. import __version__
+
 if TYPE_CHECKING:
     from ..coverage import CoverageReport
 
@@ -18,11 +20,13 @@ def to_dict(report: CoverageReport) -> dict:
         "subject": report.subject,
         "generated_at": report.generated_at,
         "source_count": report.source_count,
+        "tool": {"name": "control-coverage", "version": __version__},
         "frameworks": [
             {
                 "framework": fc.catalog.framework,
                 "name": fc.catalog.name,
                 "version": fc.catalog.version,
+                "sha256": fc.catalog.sha256,
                 "catalog_coverage": fc.catalog.coverage,
                 "in_scope": fc.in_scope,
                 "addressed": fc.addressed,
diff --git a/control_coverage/reporters/markdown.py b/control_coverage/reporters/markdown.py
index fe64725..cefe533 100644
--- a/control_coverage/reporters/markdown.py
+++ b/control_coverage/reporters/markdown.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING
 
+from .. import __version__
 from ..coverage import (
     ASSERTED,
     FAILING,
@@ -108,8 +109,12 @@ def render(report: CoverageReport) -> str:
     out.append(f"# Control Coverage — {title}")
     out.append("")
     out.append(f"- **Generated:** {report.generated_at}")
+    out.append(f"- **Tool:** control-coverage {__version__}")
     out.append(f"- **Corpus:** {report.source_count} evidence source(s)")
-    frameworks = ", ".join(fc.catalog.framework for fc in report.frameworks)
+    frameworks = ", ".join(
+        f"{fc.catalog.framework} {fc.catalog.version} (`sha256:{fc.catalog.sha256[:12]}`)"
+        for fc in report.frameworks
+    )
     out.append(f"- **Frameworks:** {frameworks}")
     out.append("")
     out.append(
diff --git a/tests/test_reporters.py b/tests/test_reporters.py
index 39332cb..ec28c99 100644
--- a/tests/test_reporters.py
+++ b/tests/test_reporters.py
@@ -34,6 +34,15 @@ def test_json_is_valid_and_structured():
     assert "unaddressed" in states
 
 
+def test_json_stamps_tool_and_catalog_provenance():
+    from control_coverage import __version__
+
+    doc = _json.loads(reporters.render(_report(), "json"))
+    assert doc["tool"] == {"name": "control-coverage", "version": __version__}
+    soc2 = doc["frameworks"][0]
+    assert len(soc2["sha256"]) == 64  # full SHA-256 hex digest of the catalog file
+
+
 def test_html_is_self_contained():
     html = reporters.render(_report(), "html")
     assert html.startswith("<!doctype html>")