Reference catalog of ISO/IEC 27001:2022 Annex A test patterns: testable technical controls with code-level assertions for access control (A.8.2-A.8.5), logging and monitoring (A.8.15-A.8.16), cryptography (A.8.24), and secure development (A.8.25-A.8.31), plus evidence patterns for Stage 1 and Stage 2 certification audits and Statement of Applicability scoping. The full 93-control Annex A index (four themes: organizational A.5, people A.6, physical A.7, technological A.8) and the exhaustive per-control test code live in references/. Use when authoring ISMS test coverage for an ISO 27001:2022 certification engagement or gap assessment.
74
93%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Per isms.online/iso-27001/annex-a (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 references/annex-a-control-index.md. 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 references/technical-control-test-patterns.md.
kms.describe_key,
siem.get_alerts, scm.list_merged_prs) are placeholders for the real ones.compliance-evidence-generator).Each row maps a control to what its test asserts; the full multi-assertion code is in references/technical-control-test-patterns.md.
| 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 |
Control (A.8.5): access is granted only after authentication that includes a second factor. Per isms.online/iso-27001/annex-a (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
compliance-evidence-generator, which assembles the control-evidence matrix and
chain-of-custody notes.
Per isms.online/iso-27001/ (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.
Per isms.online/iso-27001/statement-of-applicability (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.
| 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-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) |
kms.describe_key, siem.get_alerts) must be adapted to the target stack.soc2-evidence-collector - sister: SOC 2 Type II evidence collection (AICPA TSC)gdpr-test-patterns - sister: GDPR per-Article test catalogaudit-trail-test-author - companion: audit log emission tests (feeds A.8.15 evidence)