Testland
Browse all skills & agents

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-scoring
View source

Compliance 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.

FrameworkName it asSource of record
GDPRRegulation (EU) 2016/679eur-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 CPRACalifornia Civil Code Title 1.81.5, beginning at §1798.100Statute at leginfo.legislature.ca.gov (opens in new window); regulator overview at oag.ca.gov/privacy/ccpa (opens in new window)
SOC 2AICPA 2017 Trust Services Criteria (with revised points of focus, 2022)aicpa-cima.com (opens in new window)
HIPAA Security Rule45 CFR Part 164 Subpart C, §§164.302 to 164.318ecfr.gov (opens in new window)
PCI DSSThe exact version, for example PCI DSS v4.0.1pcisecuritystandards.org/document_library (opens in new window)
ISO/IEC 27001ISO/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.

StateEvidence requiredNot sufficient
metA named artifact (test file, log export, signed record, deviation memo) mapped to that one criterion ID, dated inside the assessment window, and carrying its resultA control that exists in a policy but produced no artifact in the window
partialAn artifact exists but covers only part of the criterion's scope, or appears intermittently across the window, or verifies design without verifying operationTreating "we mostly do this" as met
not metNo artifact maps to the criterion, or the only mapped artifact failed and was not remediated in the windowLeaving it blank, or folding it into a neighbouring criterion
not applicableA scope exclusion carrying all four fields from Step 3, still inside its re-review dateAn assertion that it does not apply, with no record behind it

Four scoring rules that decide most disputed cells:

  1. One criterion, one dedicated verification. An artifact claimed to cover several criteria at once is scored partial for each of them until each criterion has its own verification, or the composite artifact names each criterion and shows a distinct result per criterion. Assessors sample per criterion, so a bundled artifact reads as a gap on whichever one gets sampled.
  2. Evidence is window-bound. For a period-based engagement, an artifact dated before the period opened does not count toward that period. Score it not met for the current window even if it was met last cycle.
  3. Design is not operation. A policy, a runbook, or a one-off screenshot evidences that a control was designed. For a period-based engagement, score design-only evidence partial until there is recurring evidence spanning the window.
  4. A failing artifact is worse than a missing one. If the mapped test ran and failed inside the window, score not met and flag it separately: unremediated known failures are the finding an assessor writes up first.

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.

FieldWhat makes it validWhat voids it
CriterionAn exact reference to a single criterion, for example 164.504(e) or Art. 37A section range, a whole domain, or the word "general"
ReasonThe 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
ApproverA named accountable individual with role and date, typically the DPO, CISO, or compliance officerA team name, a shared alias, an unattributed "management approved"
Re-review dateA calendar date by which the exclusion must be re-justifiedAn open-ended exclusion, or a date already past

Enforcement rules:

  • An exclusion missing any field is not not applicable. Score the criterion not met and list the missing field as the action item.
  • An exclusion past its re-review date is void on the day it expires. The criterion reverts to not met automatically.
  • An exclusion is bound to the scope statement it was approved under. If the scope changes (new system, new data category, new processing purpose, new cardholder-data flow), every exclusion is void until re-approved.
  • Exclusions are the first thing a qualified assessor challenges, and several are legal determinations rather than engineering ones. GDPR Art. 27 (opens in new window) (representatives of controllers or processors not established in the Union) and Art. 37 (opens in new window) (designation of a data protection officer) are routinely excluded on entity-specific grounds, and those grounds need the named approver's judgment recorded, not an engineer's inference.

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:

BandCondition
NOT READYAny in-scope criterion is not met, or any exclusion is invalid or expired
NEEDS WORKNothing not met, at least one partial
READY FOR ASSESSMENTEvery 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.

  1. Before the window opens. Score the full criteria list while gaps are still fixable. This run is the point of the whole exercise.
  2. Monthly during the window. Re-score to catch evidence-collection breakage: a job that stopped writing artifacts in month three turns a met criterion into partial without anyone noticing. Monthly is a convention, not a requirement of any framework; pick a cadence shorter than the time it would take to notice a broken collector.
  3. At window close. Final pass before the assessor engages. Anything still partial should be disclosed rather than discovered.
  4. After the assessment. Re-baseline the matrix against the actual findings. Where the assessor scored a criterion differently than you did, the difference is the calibration signal: fix the scoring rule, not just the criterion.
  5. On any framework version change. A version change invalidates the mapping, not just the content. Rebuild the list from the new source of record and re-score from zero.

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-collected

Map each in-scope criterion to the discovered tests and evidence, score per Step 2, then apply the refusal rules. The reviewer refuses to:

  • Mark "ready" if any required criterion is not met.
  • Accept not applicable without all four required fields (criterion, reason, approver, re-review date).
  • Accept a scope exclusion older than its re-review date.
  • Approve a coverage map where audit-evidence is older than the observation period start.
  • Map a single test to multiple criteria as "covers all" - each criterion needs its own dedicated assertion or composite test with explicit per-criterion verification.
  • Skip the audit-trail criterion in any framework requiring it (HIPAA, PCI, SOC 2, GDPR Art. 5(1)(f)).

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-patternWhy it failsInstead
Matrix with no framework version in the headerNobody can tell which criteria list was scored, so the result cannot be reproduced or re-usedName the edition in the header (Step 1)
One artifact marked as covering several criteriaAssessors sample per criterion; the bundle reads as a gap on whichever criterion is sampledPer-criterion verification, else partial (Step 2, rule 1)
Scope exclusion used to shrink the workloadThe exclusion gets challenged, the work comes back, and the schedule is goneFour mandatory fields with a named approver (Step 3)
Exclusions with no re-review dateThe scope drifts and stale exclusions silently persist across cyclesExpiry voids the exclusion automatically (Step 3)
Reusing last cycle's matrix after a version changeCriterion numbering and content both movedRebuild 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 claimKeep 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

  • Criteria lists here are pointers to sources of record, not copies. Regulatory, AICPA, and ISO texts change on their own schedules; re-read the source each cycle.
  • SOC 2 criterion IDs are deliberately absent; the authoritative numbering lives in the AICPA document. Paste the real IDs from there.
  • Applying a framework to a specific business, and deciding what is out of scope, are legal determinations. Scoring surfaces the question; a qualified adviser answers it.

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).

IDControl name
A.5.1Policies for Information Security
A.5.2Information Security Roles and Responsibilities
A.5.3Segregation of Duties
A.5.4Management Responsibilities
A.5.5Contact With Authorities
A.5.6Contact With Special Interest Groups
A.5.7Threat Intelligence (NEW 2022)
A.5.8Information Security in Project Management
A.5.9Inventory of Information and Other Associated Assets
A.5.10Acceptable Use of Information and Other Associated Assets
A.5.11Return of Assets
A.5.12Classification of Information
A.5.13Labelling of Information
A.5.14Information Transfer
A.5.15Access Control
A.5.16Identity Management
A.5.17Authentication Information
A.5.18Access Rights
A.5.19Information Security in Supplier Relationships
A.5.20Addressing Information Security Within Supplier Agreements
A.5.21Managing Information Security in the ICT Supply Chain
A.5.22Monitoring, Review and Change Management of Supplier Services
A.5.23Information Security for Use of Cloud Services (NEW 2022)
A.5.24Information Security Incident Management Planning and Preparation
A.5.25Assessment and Decision on Information Security Events
A.5.26Response to Information Security Incidents
A.5.27Learning From Information Security Incidents
A.5.28Collection of Evidence
A.5.29Information Security During Disruption
A.5.30ICT Readiness for Business Continuity (NEW 2022)
A.5.31Legal, Statutory, Regulatory and Contractual Requirements
A.5.32Intellectual Property Rights
A.5.33Protection of Records
A.5.34Privacy and Protection of PII
A.5.35Independent Review of Information Security
A.5.36Compliance With Policies, Rules and Standards for Information Security
A.5.37Documented 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).

IDControl name
A.6.1Screening
A.6.2Terms and Conditions of Employment
A.6.3Information Security Awareness, Education and Training
A.6.4Disciplinary Process
A.6.5Responsibilities After Termination or Change of Employment
A.6.6Confidentiality or Non-Disclosure Agreements
A.6.7Remote Working
A.6.8Information 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.

IDControl name
A.7.1Physical Security Perimeters
A.7.2Physical Entry
A.7.3Securing Offices, Rooms and Facilities
A.7.4Physical Security Monitoring (NEW 2022)
A.7.5Protecting Against Physical and Environmental Threats
A.7.6Working In Secure Areas
A.7.7Clear Desk and Clear Screen
A.7.8Equipment Siting and Protection
A.7.9Security of Assets Off-Premises
A.7.10Storage Media
A.7.11Supporting Utilities
A.7.12Cabling Security
A.7.13Equipment Maintenance
A.7.14Secure 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:

FrameworkWho attestsWhat the attestation is
SOC 2A 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 27001A certification body whose competence an accreditation body has independently confirmed (iso.org (opens in new window))A certificate against a stated edition
PCI DSSA 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
GDPRA supervisory authority, on enforcementNo routine attestation exists
CCPA/CPRAThe 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

  • GDPR. The articles group into Chapter II Principles (Articles 5 to 11), Chapter III Rights of the data subject (Articles 12 to 23), and Chapter IV Controller and processor (Articles 24 to 43), per the structure published at gdpr-info.eu (opens in new window). Which chapters are in scope depends on whether the entity acts as controller, processor, or both. Decide that first, in writing, because it determines half the exclusions later.
  • CCPA/CPRA. Proposition 24 (the CPRA) amended the CCPA with protections that began 1 January 2023, including a right to correct inaccurate information and a right to limit use of sensitive personal information; implementing regulations sit at Title 11, Division 6, Section 7001 et seq. of the California Code of Regulations (oag.ca.gov (opens in new window)). Score against the statute and the regulations, not the statute alone.
  • SOC 2. The criteria are organized as common criteria plus category-specific criteria for security, availability, processing integrity, confidentiality, and privacy. The criterion identifiers are not reproduced here: AICPA distributes the Trust Services Criteria document itself, its numbering is authoritative there, and inventing criterion IDs is worse than having none. Read them out of the TSC document (opens in new window) and paste the real IDs into the matrix. Also record which categories are in scope, since only security is common to every SOC 2 engagement.
  • HIPAA Security Rule. Administrative safeguards are at §164.308 (opens in new window), physical at §164.310 (opens in new window), technical at §164.312 (opens in new window). The Privacy Rule is a different subpart (Subpart E, §§164.500 to 164.535, including §164.502 (opens in new window)) and Breach Notification is another (Subpart D, §§164.400 to 164.414); do not silently mix them into one Security Rule matrix.
  • PCI DSS. v4.0.1 was published 11 June 2024 as a limited revision of v4.0 (published March 2022) with "no additional or deleted requirements" (PCI SSC (opens in new window)). A matrix built against v3.2.1 numbering does not transfer, so state the version in the header and rebuild rather than remap.
  • ISO/IEC 27001. The current standard is Edition 3, published 2022-10 (iso.org (opens in new window)). Read the Annex A control list out of the edition you are being assessed against, not out of a blog summary. ISO asks that the standard be referred to by full reference, for example "certified to ISO/IEC 27001:2022" rather than "certified to ISO 27001"; carry that precision into the matrix header.

Naming precision in the output

Compliance wording carries legal weight, so the matrix should say only what is true:

  • SOC 2 is an examination over a defined scope and period, reported under AICPA attestation standards (illustrative type 2 report (opens in new window)). Never write "SOC 2 certified" or "passed SOC 2".
  • ISO asks for the full reference: "certified to ISO/IEC 27001:2022", not "certified to ISO 27001" (iso.org (opens in new window)).
  • PCI DSS results are stated with the version and the assessing QSA (pcisecuritystandards.org (opens in new window)).
  • "GDPR compliant" and "CCPA compliant" are not attestable states. Scope the claim to the articles or code sections that were assessed, over the window they were assessed in.

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:

ThemeControlsCount
A.5 OrganizationalA.5.1 - A.5.3737
A.6 PeopleA.6.1 - A.6.88
A.7 PhysicalA.7.1 - A.7.1414
A.8 TechnologicalA.8.1 - A.8.3434

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

  • Preparing for ISO 27001:2022 Stage 1 (documentation) or Stage 2 (implementation evidence) certification audit.
  • Scoping a Statement of Applicability (SoA) - deciding which of the 93 Annex A controls apply and what tests demonstrate their operation.
  • Gap assessment: identifying which technical controls have no test coverage.
  • New feature touches identity, access, cryptography, or secure development - confirming the relevant A.8.x controls are still covered.

How to use

  1. List in-scope controls from your SoA and the full index in annex-a-control-index.md (opens in new window).
  2. Find the testable ones in the summary table below - the A.8.x (plus two A.5/A.6) controls with a runtime behavior a test can assert.
  3. Pull the pattern: read the one fully worked control (A.8.5) below to see the shape, then copy the matching per-control code from technical-control-test-patterns.md (opens in new window).
  4. Adapt the assertion to your stack - the API names (kms.describe_key, siem.get_alerts, scm.list_merged_prs) are placeholders for the real ones.
  5. Wire it into a scheduled run on a production-equivalent environment, so each pass emits a timestamped record that becomes Stage 2 evidence (hand off to soc2-evidence-collector's evidence-packaging workflow).
  6. Record not-applicable controls in the SoA with all four required fields (see the SoA section).

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).

ControlTest assertsCode
A.8.2 Privileged Access RightsAdmin identities are separate from user identities; access reviewed within 90 daysreference
A.8.3 Information Access RestrictionAccess enforced per data classification; role change revokes prior grantsreference
A.8.4 Access to Source CodeOnly the dev group holds write accessreference
A.8.5 Secure AuthenticationMFA required (no session without a second factor); weak passwords rejectedWorked example below
A.8.15 LoggingAuth events logged with required fields; audit log is append-onlyreference
A.8.16 Monitoring Activities (NEW)Sustained failures raise an alert; privileged actions appear in the monitoring streamreference
A.8.17 Clock SynchronizationLog timestamps stay within NTP drift tolerancereference
A.8.24 Use of CryptographyApproved at-rest algorithm; TLS floor enforced; key rotation on schedulereference
A.8.25 - A.8.31 Secure developmentSecurity-test gate on release; env credential isolation; SAST on every PRreference
A.5.3 Segregation of DutiesA change author is not its sole approverreference
A.5.34 / A.6.8 PII + incident reportingPII not leaked in API responses; reported events create a trackable ticketreference

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.

  1. Arrange a user whose account has MFA enabled.
  2. Assert the negative path - a password-only login issues no session.
  3. Assert the positive path - password plus a valid OTP issues a session.
  4. Emit evidence - the passing run writes a record keyed to the control ID.
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 clusterEvidence typeCollector pattern
A.5.15, A.8.2, A.8.3IDP audit logs (access grants, reviews)Daily export from IDP
A.8.5MFA enforcement logsPer-login event stream
A.8.15, A.8.16SIEM event historyContinuous alert feed (append-only)
A.8.24KMS key configuration + rotation historyQuarterly attestation export
A.8.25 - A.8.31CI pipeline pass-history (SAST, security tests)Per-PR run history
A.5.18, A.6.5Provisioning/deprovisioning ticketsITSM export per hire/departure
A.6.3Training completion recordsLMS export
A.5.26, A.5.27Incident post-mortem recordsIncident 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:

  • Implementation status (implemented / not implemented)
  • Justification for inclusion or exclusion
  • Brief description of how the control is implemented, with policy reference

A control marked "not applicable" must include:

  1. Which specific control (ID + name, not "general")
  2. Reason the control does not apply to the organization
  3. Approver (CISO / DPO / compliance officer)
  4. Re-review date

The not-applicable verdict logic refuses to accept scope exclusions without all four required fields.

Key compliance gaps tests should catch

GapDetection
MFA bypassed on legacy endpointsA.8.5 per-endpoint test
Audit logs deletable via APIA.8.15 append-only assertion
Dev pipeline merges without SASTA.8.28 PR check audit
Prod and dev share a database credentialA.8.31 credential isolation test
Access rights not reviewed within 90 daysA.8.2 review-recency assertion
Same engineer authors and approves a changeA.5.3 PR approval test
Log timestamps drift from NTP by more than 1 sA.8.17 clock-sync test

Anti-patterns

Anti-patternWhy it failsFix
SoA excludes a control with no justificationStage 1 audit rejects SoA; certification blockedDocument reason + approver + re-review date per clause 6.1.3
Tests run only in CI against dev dataAuditor cannot verify production control operationRun periodic evidence-collection in production equivalents
Audit log evidence stored in mutable storageAuditor disputes log integrityAppend-only immutable storage (object-store versioning or equivalent)
Map one SAST scan to all A.8.25-A.8.31 controlsEach control needs its own dedicated assertionPer-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 forensicsNTP-drift assertion (A.8.17 pattern)

Limitations

  • This skill is a test-pattern catalog, not legal advice and not a substitute for a qualified ISO 27001 lead auditor.
  • The standard text (ISO/IEC 27001:2022) is paywalled at iso.org; this skill sources control IDs, names, and counts from isms.online/iso-27001/annex-a/ (fetched 2026-06-04).
  • Controls A.5, A.6, and A.7 are largely verified by document review and site inspection; automated test patterns are only listed where a runtime behavior exists.
  • ISO/IEC 27001:2022 clause numbering differs from the 2013 edition; verify the edition in scope for any active engagement.
  • Test templates use the team's existing frameworks; actual API names (e.g., kms.describe_key, siem.get_alerts) must be adapted to the target stack.

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 <= 90

A.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 == 403

A.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.