Summary
data/registry.json currently maps every M365 check identically to BOTH iso-27001 and iso-27002 — 1020/1020 mappings are identical, 0 divergent. This is silently wrong: ISO 27001 (the ISMS standard) and ISO 27002 (the implementation guidance for Annex A controls) are different frameworks with overlapping but not identical control sets.
Surfaced by Galvnyz/M365-Assess#871 — M365-Assess has a Pester regression test (tests/Behavior/Iso-27001-27002-Mapping-Audit.Tests.ps1) currently -Skip:$true waiting for this upstream fix. Once corrected here, that test un-skips automatically on their weekly registry sync.
Why the two are different
- ISO/IEC 27001:2022 — the ISMS standard. Contains clauses 4–10 (process: risk assessment, internal audit, statement of applicability, management review) plus a normative reference to Annex A's 93 controls.
- ISO/IEC 27002:2022 — the implementation guidance for those Annex A controls. 93 controls, structured the same way as 27001 Annex A, but the document is the "how to implement" guide, not the certification standard.
A check verifying e.g. "MFA on admin accounts":
- Should map to ISO 27001 Annex A 8.5 (the control reference)
- Should map to ISO 27002 8.5 (the implementation guidance for that control)
- Should NOT map both to identical strings — the framework purposes are different even when the control numbers match.
For a check verifying e.g. "documented information security policy" (an ISMS-process control):
- Should map to ISO 27001 §5.2 (clause 5.2 — Policy)
- Should NOT map to ISO 27002 at all (27002 doesn't cover ISMS process clauses; only Annex A controls)
So the mappings should diverge in two ways:
- Identical-numbered Annex A mappings still need different
controlId semantics (A.X.Y vs X.Y — 27001 prefixes with A. because it's referencing Annex A explicitly)
- ISMS-process controls have ISO 27001 mappings without ISO 27002 mappings
Reproducer
python -c "
import json
reg = json.load(open('data/registry.json', encoding='utf-8'))
identical = different = absent_27002 = absent_27001 = 0
for c in reg['checks']:
fw = c.get('frameworks', {})
has_27001 = 'iso-27001' in fw
has_27002 = 'iso-27002' in fw
if not has_27001 and not has_27002: continue
if has_27001 and not has_27002: absent_27002 += 1
elif has_27002 and not has_27001: absent_27001 += 1
elif fw['iso-27001'].get('controlId') == fw['iso-27002'].get('controlId'):
identical += 1
else:
different += 1
print(f'Identical: {identical}, Different: {different}, 27001-only: {absent_27002}, 27002-only: {absent_27001}')
"
# Currently: Identical: 1020, Different: 0, 27001-only: 0, 27002-only: 0
# Target: Different > 0 (Annex A mappings should use A.X.Y vs X.Y form);
# 27001-only > 0 (ISMS-process clause checks);
# Identical: 0
Likely root cause
Probably one of:
data/scf-framework-map.json maps both iso-27001 and iso-27002 to the same SCF framework ID. The build then derives identical mappings.
- SCF database
controls_mappings table has identical entries for both framework IDs (upstream SCF data issue rather than CheckID).
- Manual override path copies the 27001 mapping into 27002 (or vice versa) somewhere in
Build-Registry.py.
Investigation: trace the build for one M365 check (e.g., ENTRA-ADMIN-001) and see where the iso-27001 + iso-27002 entries get populated.
Suggested fix scope
Path A — Different controlId formats (Annex A reference vs implementation guidance)
If ISO 27001 should always be A.X.Y and ISO 27002 should always be X.Y, the fix is mechanical: build-time format the SCF-derived ID with the appropriate prefix per framework. Verify that 27001's intent is "this check satisfies Annex A control X.Y" and 27002's intent is "this check implements 27002 guidance X.Y."
Path B — ISMS-process checks should have ISO 27001 only
Identify which CheckIDs map to ISO 27001 clauses 4–10 (ISMS process). Today these likely don't exist as ISO 27001 mappings at all (only Annex A). May want to file a separate issue if introducing ISMS-process coverage is in scope.
Path C — Both A and B
Most thorough. May warrant breaking into separate PRs.
Acceptance criteria
Cross-impact
- M365-Assess#871 — un-skip and tick the boxes in their audit doc
- The community example from M365-Assess#618 — a tenant with admin-MFA gap should NOT mark ISO 27001 as failing if MFA isn't an ISO 27001 §5.x ISMS-process clause. Verify this scenario after fix.
Out of scope
Related
Summary
data/registry.jsoncurrently maps every M365 check identically to BOTHiso-27001andiso-27002— 1020/1020 mappings are identical, 0 divergent. This is silently wrong: ISO 27001 (the ISMS standard) and ISO 27002 (the implementation guidance for Annex A controls) are different frameworks with overlapping but not identical control sets.Surfaced by Galvnyz/M365-Assess#871 — M365-Assess has a Pester regression test (
tests/Behavior/Iso-27001-27002-Mapping-Audit.Tests.ps1) currently-Skip:$truewaiting for this upstream fix. Once corrected here, that test un-skips automatically on their weekly registry sync.Why the two are different
A check verifying e.g. "MFA on admin accounts":
For a check verifying e.g. "documented information security policy" (an ISMS-process control):
So the mappings should diverge in two ways:
controlIdsemantics (A.X.YvsX.Y— 27001 prefixes withA.because it's referencing Annex A explicitly)Reproducer
Likely root cause
Probably one of:
data/scf-framework-map.jsonmaps bothiso-27001andiso-27002to the same SCF framework ID. The build then derives identical mappings.controls_mappingstable has identical entries for both framework IDs (upstream SCF data issue rather than CheckID).Build-Registry.py.Investigation: trace the build for one M365 check (e.g.,
ENTRA-ADMIN-001) and see where the iso-27001 + iso-27002 entries get populated.Suggested fix scope
Path A — Different controlId formats (Annex A reference vs implementation guidance)
If ISO 27001 should always be
A.X.Yand ISO 27002 should always beX.Y, the fix is mechanical: build-time format the SCF-derived ID with the appropriate prefix per framework. Verify that 27001's intent is "this check satisfies Annex A control X.Y" and 27002's intent is "this check implements 27002 guidance X.Y."Path B — ISMS-process checks should have ISO 27001 only
Identify which CheckIDs map to ISO 27001 clauses 4–10 (ISMS process). Today these likely don't exist as ISO 27001 mappings at all (only Annex A). May want to file a separate issue if introducing ISMS-process coverage is in scope.
Path C — Both A and B
Most thorough. May warrant breaking into separate PRs.
Acceptance criteria
Different > 0andIdentical: 0after fixtests/Behavior/Iso-27001-27002-Mapping-Audit.Tests.ps1un-skips and passes (their CI catches the change automatically on next registry sync)Cross-impact
Out of scope
Related