compliance-coverage-scoring
Scores existing tests and evidence against a named compliance framework's criteria list (GDPR, CCPA/CPRA, SOC 2 Trust Services Criteria, HIPAA Security Rule, PCI DSS, ISO/IEC 27001), marking every criterion met, partial, not met, or not applicable with a stated evidence requirement per state, and recording each scope exclusion with its criterion reference, reason, named approver, and re-review date. Includes an adversarial readiness-review mode with hard refusal rules (never "ready" with an unjustified gap), and the ISO/IEC 27001:2022 Annex A per-control test-pattern catalog in references/iso27001.md. Produces a readiness self-assessment only: not certification, not an audit opinion, not legal advice. Use when a framework version has been named and an evidence set already exists, and someone needs a per-criterion readiness score before an observation period opens, before a qualified assessor arrives, or in response to a regulator inquiry.
Install with skills.sh (any agent)
npx skills add testland/qa --skill compliance-coverage-scoringCompliance coverage scoring
Resolve a named framework to its criteria list, score each criterion against the evidence that actually exists, and record every excluded criterion with the four fields that make an exclusion survivable under challenge.
This produces a readiness self-assessment only
The result is a readiness self-assessment: not certification, not an attestation, not an audit opinion, and not legal advice. Nothing produced here demonstrates compliance to a regulator, a customer, or a court. It is an internal gap list that tells a team what to fix before a qualified party looks, and only a qualified party can attest - none of them is you.
Write that framing into the artifact itself, at the top, every time. A readiness matrix that circulates without it gets mistaken for evidence of compliance. For who can attest per framework and for precise result wording, see references/frameworks.md.
What this owns, and what it does not
Owns: resolving a framework plus version to a criteria list, assigning one of four states per criterion, defining what evidence each state requires, recording scope exclusions, and emitting a coverage matrix with a readiness verdict.
Does not own: implementing controls, writing the tests, running scanners, collecting or storing evidence artifacts, and certifying anything. Those are separate jobs with separate outputs. This one reads what exists and scores it.
Step 1: resolve the framework to a versioned criteria list
A criteria list without a version is not scorable. Name the edition in the output header, and take the criterion identifiers from the source of record rather than from memory.
| Framework | Name it as | Source of record |
|---|---|---|
| GDPR | Regulation (EU) 2016/679 | eur-lex.europa.eu/eli/reg/2016/679/oj (opens in new window); article-by-article structure at gdpr-info.eu (opens in new window) |
| CCPA as amended by CPRA | California Civil Code Title 1.81.5, beginning at §1798.100 | Statute at leginfo.legislature.ca.gov (opens in new window); regulator overview at oag.ca.gov/privacy/ccpa (opens in new window) |
| SOC 2 | AICPA 2017 Trust Services Criteria (with revised points of focus, 2022) | aicpa-cima.com (opens in new window) |
| HIPAA Security Rule | 45 CFR Part 164 Subpart C, §§164.302 to 164.318 | ecfr.gov (opens in new window) |
| PCI DSS | The exact version, for example PCI DSS v4.0.1 | pcisecuritystandards.org/document_library (opens in new window) |
| ISO/IEC 27001 | ISO/IEC 27001:2022 (Edition 3, published 2022-10) | iso.org/standard/27001 (opens in new window) |
Per-framework notes that change how the list is built - GDPR chapters by controller/processor role, the CPRA amendments to score against, why SOC 2 criterion IDs must be pasted from the AICPA document rather than invented, keeping the HIPAA Security/Privacy/Breach subparts unmixed, the PCI DSS v4.0.1 version pin, and the ISO/IEC 27001:2022 full-reference rule - are in references/frameworks.md. Read the note for whichever framework is in play before building its list.
For ISO/IEC 27001:2022 engagements, the Annex A per-control test-pattern catalog - the 93-control index, testable technical controls (A.8.x), Stage 1 / Stage 2 evidence shapes, and Statement of Applicability scoping - is in references/iso27001.md; it defines what a met artifact looks like per Annex A control before you score it.
Required versus addressable (HIPAA specifically)
45 CFR §164.306(d) states that implementation specifications are either required or addressable, with the word "Required" or "Addressable" printed in parentheses after the title, and that for an addressable specification the entity must assess whether it is reasonable and appropriate, document the decision, and implement an equivalent alternative where reasonable and appropriate (§164.306 (opens in new window)). So "Encryption and decryption (Addressable)" at §164.312(a)(2)(iv) (opens in new window) with a documented alternative is a documented deviation, not a gap, and is scored met with the deviation record as its evidence. "Unique user identification (Required)" at §164.312(a)(2)(i) has no such route.
Step 2: score each criterion
Four states. Each has an evidence bar, and a criterion that does not clear its bar drops to the state below.
| State | Evidence required | Not sufficient |
|---|---|---|
met | A named artifact (test file, log export, signed record, deviation memo) mapped to that one criterion ID, dated inside the assessment window, and carrying its result | A control that exists in a policy but produced no artifact in the window |
partial | An artifact exists but covers only part of the criterion's scope, or appears intermittently across the window, or verifies design without verifying operation | Treating "we mostly do this" as met |
not met | No artifact maps to the criterion, or the only mapped artifact failed and was not remediated in the window | Leaving it blank, or folding it into a neighbouring criterion |
not applicable | A scope exclusion carrying all four fields from Step 3, still inside its re-review date | An assertion that it does not apply, with no record behind it |
Four scoring rules that decide most disputed cells:
Record, per criterion, the artifact path or identifier and its date. A matrix whose cells say met with nothing beside them cannot be re-checked by anyone, including you next quarter.
Step 3: record scope exclusions (four mandatory fields)
A criterion becomes not applicable only through a written exclusion carrying all four of these. Three out of four is not an exclusion.
| Field | What makes it valid | What voids it |
|---|---|---|
| Criterion | An exact reference to a single criterion, for example 164.504(e) or Art. 37 | A section range, a whole domain, or the word "general" |
| Reason | The specific facts about this entity or this scope that put the criterion out of reach, stated so a third party can test them | "Not applicable to us", or a reason that restates the exclusion |
| Approver | A named accountable individual with role and date, typically the DPO, CISO, or compliance officer | A team name, a shared alias, an unattributed "management approved" |
| Re-review date | A calendar date by which the exclusion must be re-justified | An open-ended exclusion, or a date already past |
Enforcement rules:
Exclusion record shape:
- criterion: "164.504(e)"
framework: "HIPAA Security Rule (45 CFR Part 164 Subpart C)"
scope: "Business associate, ePHI processing for clinical-trial sponsor X"
reason: >
Single-purpose service under one executed BAA with no subcontractor
handling ePHI; no downstream BAA chain exists to verify.
approver: "J. Okonkwo, Compliance Officer"
approved: "2026-04-15"
re_review: "2026-10-15"Step 4: emit the coverage matrix
## Compliance readiness self-assessment: <framework + version> (<commit or date>)
> Readiness self-assessment only. Not certification, not an audit opinion,
> not legal advice. Only a qualified assessor can attest to this framework.
**Framework:** HIPAA Security Rule, 45 CFR Part 164 Subpart C
**Scope:** Business associate handling ePHI for a clinical-trial sponsor
**Assessment window:** 2026-01-01 to 2026-06-30
**Criteria in scope:** 24 | **Artifacts mapped:** 47 | **Exclusions:** 1
### Per-criterion coverage
| Criterion | Name | State | Evidence | Action |
|---|---|---|---|---|
| §164.308(a)(1)(ii)(A) | Risk analysis (Required) | met | risk-analysis-2026-02.pdf, 2026-02-11 | none |
| §164.308(a)(3)(ii)(C) | Termination procedures (Addressable) | partial | tests/test_offboarding.py, runs since 2026-04 only | Extend evidence to cover Jan to Mar or restate the window |
| §164.310(d)(2)(i) | Disposal (Required) | partial | tests/test_device_wipe.py asserts wipe occurred, not the method | Assert the sanitization method against a named standard |
| §164.312(a)(2)(i) | Unique user identification (Required) | met | tests/test_unique_user_id.py, green all 6 months | none |
| §164.312(a)(2)(iv) | Encryption and decryption (Addressable) | met | deviation memo 2026-01-08 plus compensating-control record | Re-review at window close |
| §164.312(b) | Audit controls | not met | none mapped | Emit and retain access records for ePHI reads and writes |
| §164.312(e)(1) | Transmission security | partial | tests/test_https_required.py covers redirect only | Add a cipher-strength assertion |
| §164.504(e) | Organizational requirements (BAA) | n/a | Exclusion approved 2026-04-15 by J. Okonkwo, re-review 2026-10-15 | none |
### Summary
met 4 | partial 3 | not met 1 | n/a 1 (1 valid exclusion, 0 invalid)
### Verdict
NOT READY. One in-scope criterion is `not met` (§164.312(b) audit controls) and
three are `partial`.
### Action items, highest severity first
1. §164.312(b): no access-record evidence exists for the window. Closing this
after the window ends does not retroactively cover the window.
2. §164.310(d)(2)(i): the disposal test asserts that a wipe happened but not
which method was used. Assert against a named sanitization standard such as
NIST SP 800-88 Rev. 1 (https://csrc.nist.gov/pubs/sp/800/88/r1/final).
3. §164.312(e)(1): extend the TLS test past redirect behaviour to negotiated
cipher strength.Verdict bands. These are a practitioner convention for internal triage, not a threshold defined by any of the frameworks, and no band is a compliance claim:
| Band | Condition |
|---|---|
| NOT READY | Any in-scope criterion is not met, or any exclusion is invalid or expired |
| NEEDS WORK | Nothing not met, at least one partial |
| READY FOR ASSESSMENT | Every in-scope criterion met, every exclusion complete and current. This says the evidence set looks complete to you. It says nothing about what a qualified assessor will conclude |
Step 5: the pre-audit dry run
For any framework assessed over a period rather than at a point in time, one scoring pass at the end is too late: evidence that was never collected in month two cannot be created in month six.
Adversarial readiness review
Before a compliance audit (Type II observation period start, QSA dry-run, DPA assessment), run the scoring pass in adversarial mode: assume the evidence set is incomplete and make it prove otherwise. Discover what actually exists rather than trusting the team's claimed coverage:
# Search test directories for compliance-relevant tests
grep -rE "(test_gdpr|test_ccpa|test_soc2|test_hipaa|test_pci)" tests/
# Search for compliance-tag annotations
grep -rE "@compliance\(" tests/ src/
# Discover audit-evidence collection scripts
find evidence/ -name "*.json" -o -name "*.py" -newer evidence/.last-collectedMap each in-scope criterion to the discovered tests and evidence, score per Step 2, then apply the refusal rules. The reviewer refuses to:
A review that trips any rule emits NOT READY with the tripped rule as the first action item - there is no override short of fixing the gap or recording a valid exclusion.
Anti-patterns
| Anti-pattern | Why it fails | Instead |
|---|---|---|
| Matrix with no framework version in the header | Nobody can tell which criteria list was scored, so the result cannot be reproduced or re-used | Name the edition in the header (Step 1) |
| One artifact marked as covering several criteria | Assessors sample per criterion; the bundle reads as a gap on whichever criterion is sampled | Per-criterion verification, else partial (Step 2, rule 1) |
| Scope exclusion used to shrink the workload | The exclusion gets challenged, the work comes back, and the schedule is gone | Four mandatory fields with a named approver (Step 3) |
| Exclusions with no re-review date | The scope drifts and stale exclusions silently persist across cycles | Expiry voids the exclusion automatically (Step 3) |
| Reusing last cycle's matrix after a version change | Criterion numbering and content both moved | Rebuild from the source of record (Step 5, item 5) |
| Circulating the matrix as proof of compliance, or calling it "certified" / "compliant" | It is a self-assessment; no self-assessment produces certification or a compliance claim | Keep the disclaimer in the artifact and use "readiness" with the version and window (Step 4) |
Naming precision in the output
Precise result wording (never "SOC 2 certified", the ISO/IEC 27001:2022 full-reference rule, stating PCI DSS results with the version and QSA, and why "GDPR compliant" / "CCPA compliant" are not attestable states) is in references/frameworks.md.
Limitations
Annex A control index (ISO/IEC 27001:2022)
View source (opens in new window)Annex A control index (ISO/IEC 27001:2022)
Deep reference behind iso27001.md (opens in new window). The full 93-control enumeration across the four Annex A themes. Consult when scoping a Statement of Applicability or checking which theme a control belongs to; iso27001.md keeps only the four-theme summary and the testable-control shortlist.
ISO/IEC 27001:2022 restructured Annex A from 114 controls (2013 edition) to 93 controls across four themes, adding 11 new controls for cloud, threat intelligence, secure coding, and monitoring. All control IDs, names, and counts below are sourced from isms.online/iso-27001/annex-a (opens in new window) (fetched 2026-06-04); the canonical standard text is paywalled at iso.org and is cited by stable ID "ISO/IEC 27001:2022".
A.5 Organizational controls (37 controls)
Most A.5 controls are verified by document review, policy attestation, or access-control test. The exceptions with automated test patterns are A.5.3 (segregation of duties) and A.5.34 (PII protection) - both in technical-control-test-patterns.md (opens in new window).
| ID | Control name |
|---|---|
| A.5.1 | Policies for Information Security |
| A.5.2 | Information Security Roles and Responsibilities |
| A.5.3 | Segregation of Duties |
| A.5.4 | Management Responsibilities |
| A.5.5 | Contact With Authorities |
| A.5.6 | Contact With Special Interest Groups |
| A.5.7 | Threat Intelligence (NEW 2022) |
| A.5.8 | Information Security in Project Management |
| A.5.9 | Inventory of Information and Other Associated Assets |
| A.5.10 | Acceptable Use of Information and Other Associated Assets |
| A.5.11 | Return of Assets |
| A.5.12 | Classification of Information |
| A.5.13 | Labelling of Information |
| A.5.14 | Information Transfer |
| A.5.15 | Access Control |
| A.5.16 | Identity Management |
| A.5.17 | Authentication Information |
| A.5.18 | Access Rights |
| A.5.19 | Information Security in Supplier Relationships |
| A.5.20 | Addressing Information Security Within Supplier Agreements |
| A.5.21 | Managing Information Security in the ICT Supply Chain |
| A.5.22 | Monitoring, Review and Change Management of Supplier Services |
| A.5.23 | Information Security for Use of Cloud Services (NEW 2022) |
| A.5.24 | Information Security Incident Management Planning and Preparation |
| A.5.25 | Assessment and Decision on Information Security Events |
| A.5.26 | Response to Information Security Incidents |
| A.5.27 | Learning From Information Security Incidents |
| A.5.28 | Collection of Evidence |
| A.5.29 | Information Security During Disruption |
| A.5.30 | ICT Readiness for Business Continuity (NEW 2022) |
| A.5.31 | Legal, Statutory, Regulatory and Contractual Requirements |
| A.5.32 | Intellectual Property Rights |
| A.5.33 | Protection of Records |
| A.5.34 | Privacy and Protection of PII |
| A.5.35 | Independent Review of Information Security |
| A.5.36 | Compliance With Policies, Rules and Standards for Information Security |
| A.5.37 | Documented Operating Procedures |
A.6 People controls (8 controls)
A.6 controls are verified by HR records, contract review, training completion records, and offboarding audits. A.6.8 (Information Security Event Reporting) has an automated test pattern - see technical-control-test-patterns.md (opens in new window).
| ID | Control name |
|---|---|
| A.6.1 | Screening |
| A.6.2 | Terms and Conditions of Employment |
| A.6.3 | Information Security Awareness, Education and Training |
| A.6.4 | Disciplinary Process |
| A.6.5 | Responsibilities After Termination or Change of Employment |
| A.6.6 | Confidentiality or Non-Disclosure Agreements |
| A.6.7 | Remote Working |
| A.6.8 | Information Security Event Reporting |
A.7 Physical controls (14 controls)
A.7 controls are verified by site inspection, physical access-log review, and equipment maintenance records. No automated code-level test patterns exist for A.7; evidence is operational.
| ID | Control name |
|---|---|
| A.7.1 | Physical Security Perimeters |
| A.7.2 | Physical Entry |
| A.7.3 | Securing Offices, Rooms and Facilities |
| A.7.4 | Physical Security Monitoring (NEW 2022) |
| A.7.5 | Protecting Against Physical and Environmental Threats |
| A.7.6 | Working In Secure Areas |
| A.7.7 | Clear Desk and Clear Screen |
| A.7.8 | Equipment Siting and Protection |
| A.7.9 | Security of Assets Off-Premises |
| A.7.10 | Storage Media |
| A.7.11 | Supporting Utilities |
| A.7.12 | Cabling Security |
| A.7.13 | Equipment Maintenance |
| A.7.14 | Secure Disposal or Re-Use of Equipment |
A.8 Technological controls (34 controls)
NEW = added in the 2022 revision. The A.8.x controls verifiable through automated tests have code patterns in technical-control-test-patterns.md (opens in new window).
A.8.1 User Endpoint Devices / A.8.2 Privileged Access Rights / A.8.3 Information Access Restriction / A.8.4 Access to Source Code / A.8.5 Secure Authentication / A.8.6 Capacity Management / A.8.7 Protection Against Malware / A.8.8 Management of Technical Vulnerabilities / A.8.9 Configuration Management (NEW) / A.8.10 Information Deletion (NEW) / A.8.11 Data Masking (NEW) / A.8.12 Data Leakage Prevention (NEW) / A.8.13 Information Backup / A.8.14 Redundancy of Information Processing Facilities / A.8.15 Logging / A.8.16 Monitoring Activities (NEW) / A.8.17 Clock Synchronization / A.8.18 Use of Privileged Utility Programs / A.8.19 Installation of Software on Operational Systems / A.8.20 Networks Security / A.8.21 Security of Network Services / A.8.22 Segregation of Networks / A.8.23 Web Filtering (NEW) / A.8.24 Use of Cryptography / A.8.25 Secure Development Life Cycle / A.8.26 Application Security Requirements / A.8.27 Secure System Architecture and Engineering Principles / A.8.28 Secure Coding (NEW) / A.8.29 Security Testing in Development and Acceptance / A.8.30 Outsourced Development / A.8.31 Separation of Development, Test and Production Environments / A.8.32 Change Management / A.8.33 Test Information / A.8.34 Protection of Information Systems During Audit Testing
Source
Framework reference: who attests, per-framework notes, naming precision
View source (opens in new window)Framework reference: who attests, per-framework notes, naming precision
Companion to compliance-coverage-scoring/SKILL.md. Detail a scoring pass needs occasionally, kept out of the spine so the body stays a lean overview.
Who attests (and none of them is you)
The scoring pass produces a readiness self-assessment. Only the parties below can attest:
| Framework | Who attests | What the attestation is |
|---|---|---|
| SOC 2 | A CPA firm performing an examination under AICPA attestation standards (the illustrative type 2 report is written to meet SSAE-21 reporting requirements (opens in new window)) | A report on controls over a defined scope and period. Not a pass/fail certificate, and there is no such thing as being "SOC 2 certified" |
| ISO/IEC 27001 | A certification body whose competence an accreditation body has independently confirmed (iso.org (opens in new window)) | A certificate against a stated edition |
| PCI DSS | A Qualified Security Assessor: "independent security organizations that have been qualified and trained by PCI SSC to perform PCI DSS assessments" (pcisecuritystandards.org (opens in new window)) | An assessment against a stated version |
| GDPR | A supervisory authority, on enforcement | No routine attestation exists |
| CCPA/CPRA | The California Privacy Protection Agency and the Attorney General; consumers have been able to file CCPA complaints with the agency since July 1, 2023 (oag.ca.gov (opens in new window)) | No routine attestation exists |
Per-framework notes that change how the list is built
Naming precision in the output
Compliance wording carries legal weight, so the matrix should say only what is true:
ISO/IEC 27001:2022 test patterns
View source (opens in new window)ISO/IEC 27001:2022 test patterns
Companion reference for compliance-coverage-scoring. When the framework being scored is ISO/IEC 27001:2022, this catalog supplies the per-control Annex A test patterns, Stage 1 / Stage 2 evidence shapes, and Statement of Applicability scoping rules the score consumes.
Overview
Per isms.online/iso-27001/annex-a (opens in new window) (community reference; canonical standard text at iso.org/standard/27001 - paywalled; cite by stable ID "ISO/IEC 27001:2022"):
ISO/IEC 27001:2022 restructured Annex A from 114 controls (2013 edition) to 93 controls across four themes, adding 11 new controls for cloud, threat intelligence, secure coding, and monitoring:
| Theme | Controls | Count |
|---|---|---|
| A.5 Organizational | A.5.1 - A.5.37 | 37 |
| A.6 People | A.6.1 - A.6.8 | 8 |
| A.7 Physical | A.7.1 - A.7.14 | 14 |
| A.8 Technological | A.8.1 - A.8.34 | 34 |
The full 93-control enumeration is in annex-a-control-index.md (opens in new window). All control IDs, names, and counts are sourced from isms.online/iso-27001/annex-a/ (fetched 2026-06-04).
This is a pure-reference skill - it defines the test-pattern catalog by control. Tests use the team's existing framework; this skill is the per-control test recipe. A.5, A.6, and A.7 controls are largely verified by document review and site inspection; automated tests exist only where a control has a runtime behavior, and those patterns are catalogued in technical-control-test-patterns.md (opens in new window).
When to use
How to use
Testable controls (summary)
Each row maps a control to what its test asserts; the full multi-assertion code is in technical-control-test-patterns.md (opens in new window).
| Control | Test asserts | Code |
|---|---|---|
| A.8.2 Privileged Access Rights | Admin identities are separate from user identities; access reviewed within 90 days | reference |
| A.8.3 Information Access Restriction | Access enforced per data classification; role change revokes prior grants | reference |
| A.8.4 Access to Source Code | Only the dev group holds write access | reference |
| A.8.5 Secure Authentication | MFA required (no session without a second factor); weak passwords rejected | Worked example below |
| A.8.15 Logging | Auth events logged with required fields; audit log is append-only | reference |
| A.8.16 Monitoring Activities (NEW) | Sustained failures raise an alert; privileged actions appear in the monitoring stream | reference |
| A.8.17 Clock Synchronization | Log timestamps stay within NTP drift tolerance | reference |
| A.8.24 Use of Cryptography | Approved at-rest algorithm; TLS floor enforced; key rotation on schedule | reference |
| A.8.25 - A.8.31 Secure development | Security-test gate on release; env credential isolation; SAST on every PR | reference |
| A.5.3 Segregation of Duties | A change author is not its sole approver | reference |
| A.5.34 / A.6.8 PII + incident reporting | PII not leaked in API responses; reported events create a trackable ticket | reference |
Worked example: proving A.8.5 Secure Authentication end to end
Control (A.8.5): access is granted only after authentication that includes a second factor. Per isms.online/iso-27001/annex-a (opens in new window) (fetched 2026-06-04) this is a technological control with runtime behavior, so a Stage 2 auditor wants evidence the enforcement actually holds - not just a policy stating it should. The test proves the control from both sides and emits the pass record that becomes that evidence.
import pyotp
def test_a_8_5_second_factor_is_enforced(client, seed_user):
"""A.8.5: authentication must require a second factor before issuing a session."""
user = seed_user(email="alice@example.com", password="C0rrect-Horse", mfa_enabled=True)
# Negative path: password alone must not issue a session token.
no_factor = client.post("/auth/login", json={
"email": user.email,
"password": "C0rrect-Horse",
})
assert no_factor.status_code in (401, 403)
assert "session_token" not in no_factor.json()
# Positive path: password plus a valid OTP issues a session.
otp = pyotp.TOTP(user.mfa_secret).now()
with_factor = client.post("/auth/login", json={
"email": user.email,
"password": "C0rrect-Horse",
"otp": otp,
})
assert with_factor.status_code == 200
assert "session_token" in with_factor.json()
# Evidence: the pass record feeds the Stage 2 bundle, keyed to the control.
record_control_evidence(control="A.8.5",
test="test_a_8_5_second_factor_is_enforced",
outcome="PASS")Run this on a production-equivalent environment on a schedule (not only in CI against dev data), so the evidence demonstrates the control operating over the audit's observation period. The record_control_evidence call is the seam into soc2-evidence-collector's evidence-packaging workflow, which assembles the control-evidence matrix and chain-of-custody notes.
Evidence patterns for certification audits
Per isms.online/iso-27001/ (opens in new window) (fetched 2026-06-04), certification follows two stages:
Stage 1 audit: Documentation review. Auditor reads the ISMS, policies, risk assessment, and Statement of Applicability. Evidence needed: policy documents, risk register, SoA with justifications.
Stage 2 audit: Implementation verification. Auditor samples control operation. Evidence needed: test pass-history, access-review records, audit logs, deployment pipeline outputs, training records.
| Control cluster | Evidence type | Collector pattern |
|---|---|---|
| A.5.15, A.8.2, A.8.3 | IDP audit logs (access grants, reviews) | Daily export from IDP |
| A.8.5 | MFA enforcement logs | Per-login event stream |
| A.8.15, A.8.16 | SIEM event history | Continuous alert feed (append-only) |
| A.8.24 | KMS key configuration + rotation history | Quarterly attestation export |
| A.8.25 - A.8.31 | CI pipeline pass-history (SAST, security tests) | Per-PR run history |
| A.5.18, A.6.5 | Provisioning/deprovisioning tickets | ITSM export per hire/departure |
| A.6.3 | Training completion records | LMS export |
| A.5.26, A.5.27 | Incident post-mortem records | Incident management system export |
Evidence storage requirements: append-only, immutable (object-store versioning or equivalent), with collector-run metadata so auditors can verify continuity.
Statement of Applicability (SoA)
Per isms.online/iso-27001/statement-of-applicability (opens in new window) (fetched 2026-06-04):
The SoA is a mandatory document under ISO/IEC 27001:2022 clause 6.1.3. It must list all 93 Annex A controls and for each declare:
A control marked "not applicable" must include:
The not-applicable verdict logic refuses to accept scope exclusions without all four required fields.
Key compliance gaps tests should catch
| Gap | Detection |
|---|---|
| MFA bypassed on legacy endpoints | A.8.5 per-endpoint test |
| Audit logs deletable via API | A.8.15 append-only assertion |
| Dev pipeline merges without SAST | A.8.28 PR check audit |
| Prod and dev share a database credential | A.8.31 credential isolation test |
| Access rights not reviewed within 90 days | A.8.2 review-recency assertion |
| Same engineer authors and approves a change | A.5.3 PR approval test |
| Log timestamps drift from NTP by more than 1 s | A.8.17 clock-sync test |
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| SoA excludes a control with no justification | Stage 1 audit rejects SoA; certification blocked | Document reason + approver + re-review date per clause 6.1.3 |
| Tests run only in CI against dev data | Auditor cannot verify production control operation | Run periodic evidence-collection in production equivalents |
| Audit log evidence stored in mutable storage | Auditor disputes log integrity | Append-only immutable storage (object-store versioning or equivalent) |
| Map one SAST scan to all A.8.25-A.8.31 controls | Each control needs its own dedicated assertion | Per-control test (A.8.29 gates pipeline; A.8.31 tests env isolation separately) |
| Skip clock-sync test (A.8.17) | Log correlation fails during incident forensics | NTP-drift assertion (A.8.17 pattern) |
Limitations
References
Technical-control test patterns (ISO/IEC 27001:2022 Annex A)
View source (opens in new window)Technical-control test patterns (ISO/IEC 27001:2022 Annex A)
Deep reference behind iso27001.md (opens in new window). The complete per-control test-pattern catalog for the A.8.x (plus two A.5/A.6) controls verifiable through automated tests. Consult when authoring the actual test for a control; iso27001.md keeps the summary table and one fully worked example (A.8.5).
All control IDs and names are sourced from isms.online/iso-27001/annex-a (opens in new window) (fetched 2026-06-04). Test templates use the team's existing frameworks; the API names (e.g. kms.describe_key, siem.get_alerts) must be adapted to the target stack. A.5, A.6, and A.7 controls are otherwise verified through document review and site inspection; automated tests exist only where the control has a runtime behavior.
A.8.2 - Privileged Access Rights
Privileged accounts must be separately provisioned, minimally scoped, and reviewed.
def test_privileged_accounts_are_separate_identities():
"""A.8.2: privileged access uses dedicated accounts, not shared admin."""
admin_accounts = iam.list_accounts(role='admin')
regular_accounts = iam.list_accounts(role='user')
# No account should appear in both sets:
overlap = set(admin_accounts) & set(regular_accounts)
assert overlap == set(), f"Shared admin/user identities: {overlap}"
def test_privileged_access_reviewed_within_period():
"""A.8.2: quarterly access review must be on record."""
last_review = AccessReview.objects.filter(scope='privileged').order_by('-completed_at').first()
assert last_review is not None
assert (timezone.now() - last_review.completed_at).days <= 90A.8.3 - Information Access Restriction
def test_user_cannot_access_data_outside_their_classification():
"""A.8.3: access restriction enforced per data classification."""
user = User.objects.get(clearance='internal')
response = client_for(user).get('/data/confidential/records')
assert response.status_code == 403
def test_role_change_revokes_prior_access_immediately():
"""A.8.3 + A.5.18: reclassification of role removes prior grants."""
user = grant_role(user, 'analyst')
revoke_role(user, 'analyst')
response = client_for(user).get('/reports/analyst-only')
assert response.status_code == 403A.8.4 - Access to Source Code
def test_only_developers_have_write_access_to_source():
"""A.8.4: source code write permissions restricted to dev group."""
non_dev_users = User.objects.exclude(groups__name='developers')
for user in non_dev_users[:10]: # sample
assert not scm.has_write_access(user, repo='main-app'), \
f"{user.email} has unexpected write access"A.8.5 - Secure Authentication
The end-to-end narrated version of this control is the Worked example in SKILL.md; the terse catalog form is below.
def test_mfa_enforced_for_all_users():
"""A.8.5: MFA required; login without second factor must fail."""
response = client.post('/auth/login', json={
'email': 'alice@example.com',
'password': 'correct-password',
# no OTP supplied
})
# Must not issue session token without second factor:
assert response.status_code in [401, 403]
assert 'session_token' not in response.json()
def test_password_complexity_policy_enforced():
"""A.8.5: weak passwords rejected at registration."""
response = client.post('/auth/register', json={
'email': 'bob@example.com',
'password': 'password123', # weak
})
assert response.status_code == 400
assert 'password' in response.json().get('errors', {})A.8.15 - Logging
def test_authentication_events_produce_audit_records():
"""A.8.15: login success and failure must be logged."""
with capture_audit_log() as logs:
client.post('/auth/login', json={'email': 'alice@example.com', 'password': 'wrong'})
events = [e for e in logs if e['event_type'] == 'auth.login.failure']
assert len(events) >= 1
record = events[0]
# Required fields per A.8.15:
assert 'timestamp' in record
assert 'user_identifier' in record
assert 'source_ip' in record
assert 'outcome' in record
def test_audit_log_is_append_only():
"""A.8.15: log records must not be deletable via application API."""
log_id = AuditLog.objects.last().id
response = client.delete(f'/audit-logs/{log_id}')
# No DELETE endpoint should exist; log must survive:
assert response.status_code in [404, 405]
assert AuditLog.objects.filter(id=log_id).exists()A.8.16 - Monitoring Activities (new in 2022)
def test_anomaly_detection_alert_fires_on_excessive_failures():
"""A.8.16: sustained login failures must trigger a monitoring alert."""
for _ in range(20):
client.post('/auth/login', json={'email': 'alice@example.com', 'password': 'wrong'})
# SIEM / monitoring platform must have received the alert:
alerts = siem.get_alerts(rule='excessive_login_failures', within_minutes=5)
assert len(alerts) >= 1
def test_privileged_action_monitored_in_realtime():
"""A.8.16: privileged admin operations must appear in monitoring stream."""
with monitor_events() as stream:
admin_client.post('/admin/users/bulk-export')
assert any(e['category'] == 'privileged_operation' for e in stream.events)A.8.17 - Clock Synchronization
def test_audit_log_timestamps_within_ntp_tolerance():
"""A.8.17: log timestamps must be within NTP drift tolerance (typically 1 s)."""
ntp_time = get_ntp_time()
for record in AuditLog.objects.order_by('-created_at')[:50]:
drift = abs((record.created_at - ntp_time).total_seconds())
assert drift < 1.0, f"Log timestamp drift {drift}s exceeds 1 s NTP tolerance"A.8.24 - Use of Cryptography
def test_data_at_rest_encrypted_with_approved_algorithm():
"""A.8.24: storage encryption must use AES-256 or equivalent."""
config = kms.describe_key(key_id='data-encryption-key')
assert config['KeySpec'] in ['AES_256', 'SYMMETRIC_DEFAULT']
assert config['KeyState'] == 'Enabled'
def test_tls_minimum_version_enforced():
"""A.8.24: TLS 1.2 is the minimum acceptable; TLS 1.0/1.1 must be rejected."""
for old_version in ['TLSv1', 'TLSv1.1']:
connection = attempt_tls_connection(host='api.example.com', version=old_version)
assert connection.failed, f"TLS {old_version} should be rejected but was accepted"
def test_encryption_key_rotation_on_schedule():
"""A.8.24: key rotation must occur within policy window (e.g., 365 days)."""
key_meta = kms.get_key_rotation_status('data-encryption-key')
assert key_meta['KeyRotationEnabled'] is True
last_rotation = key_meta['LastRotatedDate']
days_since = (datetime.utcnow() - last_rotation).days
assert days_since <= 365, f"Key last rotated {days_since} days ago; exceeds 365-day policy"A.8.25 - A.8.31 Secure development controls
The secure-development cluster covers: Secure Development Life Cycle (A.8.25), Application Security Requirements (A.8.26), Secure System Architecture (A.8.27), Secure Coding (A.8.28, new in 2022), Security Testing in Development and Acceptance (A.8.29), Outsourced Development (A.8.30), and Separation of Environments (A.8.31).
def test_security_testing_required_before_production_deployment():
"""A.8.29: release pipeline must gate on security test pass."""
pipeline = ci.get_last_pipeline(branch='main')
stages = [s['name'] for s in pipeline['stages']]
assert 'security-scan' in stages, "Security scan stage missing from pipeline"
security_stage = next(s for s in pipeline['stages'] if s['name'] == 'security-scan')
assert security_stage['status'] == 'passed'
def test_production_and_dev_environments_share_no_credentials():
"""A.8.31: dev/test must not use production secrets."""
prod_db_url = secrets.get('DATABASE_URL', env='production')
dev_db_url = secrets.get('DATABASE_URL', env='development')
assert prod_db_url != dev_db_url, "Production and development share a database credential"
def test_sast_scan_in_pr_pipeline():
"""A.8.28: static analysis for secure coding must run on every PR."""
last_prs = scm.list_merged_prs(count=20)
for pr in last_prs:
checks = scm.get_check_runs(pr['head_sha'])
sast_checks = [c for c in checks if 'sast' in c['name'].lower() or 'semgrep' in c['name'].lower()]
assert len(sast_checks) > 0, f"PR #{pr['number']} merged without SAST check"
assert all(c['conclusion'] == 'success' for c in sast_checks), \
f"PR #{pr['number']} merged with SAST failures"A.5.3 - Segregation of Duties
A.5.3 is organizational but has a testable runtime assertion:
def test_same_user_cannot_approve_their_own_change():
"""A.5.3: the author of a change must not be its sole approver."""
prs = scm.list_merged_prs(count=50)
for pr in prs:
author = pr['author']
approvers = [r['user'] for r in pr['reviews'] if r['state'] == 'APPROVED']
# Author approval alone must not satisfy the merge gate:
non_author_approvals = [a for a in approvers if a != author]
assert len(non_author_approvals) >= 1, \
f"PR #{pr['number']} approved only by its author ({author})"A.5.34 / A.6.8 - PII protection and incident reporting
def test_pii_fields_not_exposed_in_api_responses():
"""A.5.34: PII must not leak via standard API responses."""
response = client.get('/api/users')
users = response.json()['results']
for user in users:
assert 'password' not in user
assert 'ssn' not in user
assert 'tax_id' not in user
def test_security_event_report_creates_ticket():
"""A.6.8: employee-reported security events must generate a trackable record."""
response = employee_client.post('/security/report-event', json={
'description': 'Suspicious email with attachment',
'severity': 'medium',
})
assert response.status_code == 201
ticket_id = response.json()['ticket_id']
assert IncidentTicket.objects.filter(id=ticket_id).exists()Source
Related skills
audit-trail-test-author
Build-an-X for audit-log tests across compliance frameworks - required-events catalog (auth events / privilege change / data access / admin action / config change / export / impersonation); structured-log-format assertions per OWASP A09:2021; tamper-evident chain (hash-chain + signed-batch patterns) for HIPAA §164.312(b) + PCI Req 10 + SOC 2 CC7.3; immutability + retention per framework; query-replay tests for forensic reconstruction. Use when authoring audit log tests for any compliance framework (HIPAA / PCI / SOC 2 / GDPR / etc.).
gdpr-test-patterns
Reference catalog of GDPR-aligned test patterns - data-subject-rights workflows (Art. 15 access, Art. 16 rectification, Art. 17 erasure / "right to be forgotten", Art. 18 restriction, Art. 20 portability, Art. 21 objection); consent recording + revocation per Art. 7; data-residency assertions per Art. 44 - 50 international transfers; breach-notification timing tests per Art. 33 (72 hours); data-minimization assertions in fixtures per Art. 5(1)(c). The California analogue - CCPA/CPRA patterns by Cal. Civ. Code section, including Global Privacy Control (GPC) opt-out, right-to-know, deletion, right-to-correct, and sensitive-PI limits - lives in references/ccpa.md. Use when authoring GDPR- or CCPA/CPRA-readiness tests for any product processing EU or California personal data.
hipaa-test-patterns
Reference catalog of HIPAA Security Rule-aligned test patterns - administrative safeguards (45 CFR §164.308: workforce training, access management, contingency planning), physical safeguards (§164.310: facility access, workstation security, device disposal), technical safeguards (§164.312: access control, audit logs, integrity, transmission security); PHI handling assertions in fixtures; minimum-necessary tests per §164.502(b); BAA-scope boundary verification. Use when authoring HIPAA-readiness tests for any product handling Protected Health Information.
pci-dss-control-test-author
Build-an-X for PCI DSS v4.0 scope verification - cardholder data environment (CDE) boundary tests, segmentation tests (PCI Req 1), prohibited-data-storage assertions per Req 3 (no full track data, no CVV/CAV2/CVC2/CID, no PIN/PIN block post-authorization), key-management tests per Req 3.6, encryption-of-transmissions per Req 4; includes the scope catalog (SAQ A / A-EP / D levels, PAN-storage rules, hosted-fields / tokenization scope-reduction patterns) in references/pci-scope.md. Use when authoring PCI DSS scope-reduction + control tests for any system handling payment-card data, or when determining a payment integration's SAQ level.
soc2-evidence-collector
Build-an-X for SOC 2 Type II evidence collection and auditor-facing packaging - per-Trust-Services-Criterion test artifacts (Common Criteria CC1.1 - CC9.2; plus Availability A1, Confidentiality C1, Processing Integrity PI1, Privacy P1 - P9 if in scope); auto-collection from CI logs + audit trails + access logs + change-management records; alignment with Vanta / Drata / Secureframe evidence shapes; observation-period sampling. Cross-framework evidence packaging (control-evidence matrix, timestamped bundles, chain-of-custody notes per NIST SP 800-72 - also for ISO 27001 / HIPAA / PCI DSS / GDPR / FedRAMP) lives in references/evidence-packaging.md. Use when the team is preparing for SOC 2 Type II audit and needs continuous evidence collection, or when any audit engagement requires auditor-ready evidence packages built from automated test output.