Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
59 changes: 54 additions & 5 deletions .github/workflows/update-scf.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,18 +125,67 @@ jobs:
echo
echo "Full report: workflow-run artifact \`validation-report-${{ steps.resolve.outputs.version }}\`."
echo
echo "Your job: add an override entry (with a reason) to overrides/overrides.json,"
echo "or report the workbook bug upstream — then re-dispatch this workflow."
echo "Your job: add an override entry (with a reason) to overrides/overrides.json."
echo "A renamed tab or column header goes in the \`resolution\` section of that"
echo "same file (sheets[] / columns[]) — no code change needed. Then re-dispatch"
echo "this workflow. If the workbook itself is wrong, report it upstream."
echo
echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
} > /tmp/issue-body.md
gh issue create \
--title "SCF ${{ steps.resolve.outputs.version }}: validation gate failed" \
--body-file /tmp/issue-body.md
# Re-notify on an existing open issue rather than filing a duplicate:
# this job is a weekly cron, so the same blockage recurs every Monday.
TITLE="SCF ${{ steps.resolve.outputs.version }}: validation gate failed"
EXISTING="$(gh issue list --state open --search "$TITLE in:title" \
--json number,title \
--jq "[.[] | select(.title == \"$TITLE\")] | .[0].number // empty")"
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --body-file /tmp/issue-body.md
echo "Commented on existing issue #$EXISTING"
else
gh issue create --title "$TITLE" --body-file /tmp/issue-body.md
fi
exit 1

- name: Full hermetic gate
id: hermetic
if: steps.resolve.outputs.skip == 'false' && steps.pipeline.outputs.gate_rc == '0'
run: nix flake check -L

# The validation gate passed but the hermetic check did not, so nothing
# is pushed. Without this the run just goes red with no issue filed.
- name: Summon a human (hermetic gate failed)
if: failure() && steps.hermetic.outcome == 'failure'
run: |
TITLE="SCF ${{ steps.resolve.outputs.version }}: hermetic gate failed"
{
echo "\`nix flake check\` failed for SCF ${{ steps.resolve.outputs.version }}"
echo "*after* the validation gate passed, so nothing was pushed."
echo
echo "The dataset parsed and validated cleanly — this is a repo-side"
echo "inconsistency (schemas, docs freshness, or version consistency)."
echo "Reproduce locally:"
echo
echo '```'
echo "nix develop --command python3 -m scf_api.cli parse \\"
echo " --workbook <2026.x workbook> \\"
echo " --scf-version ${{ steps.resolve.outputs.version }} --out data \\"
echo " --overrides overrides/overrides.json \\"
echo " --report validation/validation-report.json"
echo "nix develop --command python3 -m scf_api.cli build --data data --out docs"
echo "nix flake check -L"
echo '```'
echo
echo "Run: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
} > /tmp/hermetic-body.md
EXISTING="$(gh issue list --state open --search "$TITLE in:title" \
--json number,title \
--jq "[.[] | select(.title == \"$TITLE\")] | .[0].number // empty")"
if [ -n "$EXISTING" ]; then
gh issue comment "$EXISTING" --body-file /tmp/hermetic-body.md
else
gh issue create --title "$TITLE" --body-file /tmp/hermetic-body.md
fi

- name: Commit and push (or no-op)
if: steps.resolve.outputs.skip == 'false' && steps.pipeline.outputs.gate_rc == '0'
run: |
Expand Down
2 changes: 1 addition & 1 deletion .scf-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
2026.1.1
2026.2
67 changes: 67 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,72 @@
# Changelog

## SCF 2026.2

Upstream restructured the workbook: the **Authoritative Sources** tab was
renamed **Focal Documents**, and the compensating-controls **SCF Control #**
header was shortened to **SCF #**. Both broke the weekly autopilot (issues #1,
#2). Sheet and column resolution now accept an ordered list of historical
names, so one parser reads every supported release, and tab names are matched
with the same normalization column headers already got.

New this release: the **QTS** (Quantum Security) control family, threat **MT-28**
(mapped to nearly every control, which is why almost all controls show a
`threats` change), and 25 crosswalk frameworks — most of the churn is upstream
adding explicit year suffixes to framework names.

The MON-03.3 evidence-request typo is still present upstream, so its override
is no longer version-scoped (`mon-03.3-erl-typo`); the gate resolves it
automatically and will warn once upstream fixes it.

Equivalence against the archived hackIDLE/scf-api dataset stays frozen at
2026.1.1 — that archive has no 2026.2 to compare against. Parsing the 2026.1.1
workbook with this release still reproduces the committed 2026.1.1 dataset
byte-for-byte.

## SCF 2026.1.1 → 2026.2

### Controls

66 added, 0 removed, 1468 changed (1534 total)

**Added:** AAT-01.5, AAT-12.5, AAT-12.6, AAT-29.24, AAT-33, AST-05.2, CFG-09, CFG-09.1, CFG-09.2, CFG-09.3, CPL-03.8, GOV-01.4, GOV-10.1, GOV-19.3, GOV-21, IAC-01.4, IAC-15.10, NET-06.8, NET-06.9, PRI-01.12, QTS-01, QTS-01.1, QTS-01.2, QTS-01.3, QTS-01.4, QTS-02, QTS-02.1, QTS-02.2, QTS-02.3, QTS-03, QTS-03.1, QTS-03.2, QTS-03.3, QTS-03.4, QTS-04, QTS-04.1, QTS-04.2, QTS-04.3, QTS-05, QTS-05.1, QTS-05.2, QTS-06, QTS-06.1, QTS-06.10, QTS-06.2, QTS-06.3, QTS-06.4, QTS-06.5, QTS-06.6, QTS-06.7 …
**Changed:** AAT-01 (threats); AAT-01.1 (threats); AAT-01.2 (threats); AAT-01.3 (maturity, threats); AAT-01.4 (threats); AAT-02 (threats); AAT-02.1 (threats); AAT-02.2 (errata, threats); AAT-02.3 (errata, maturity, threats); AAT-02.4 (threats); AAT-03 (threats); AAT-03.1 (maturity, threats); AAT-03.2 (threats); AAT-04 (threats); AAT-04.1 (threats); AAT-04.2 (threats); AAT-04.3 (threats); AAT-04.4 (threats); AAT-05 (threats); AAT-06 (threats); AAT-07 (threats); AAT-07.1 (threats); AAT-07.2 (threats); AAT-07.3 (threats); AAT-08 (threats); AAT-09 (threats); AAT-09.1 (description, maturity, scf_question, threats); AAT-10 (maturity, threats); AAT-10.1 (maturity, threats); AAT-10.10 (maturity, threats); AAT-10.11 (maturity, threats); AAT-10.12 (maturity, threats); AAT-10.13 (maturity, threats); AAT-10.14 (maturity, threats); AAT-10.15 (maturity, threats); AAT-10.16 (maturity, threats); AAT-10.17 (maturity, scf_question, threats); AAT-10.18 (maturity, threats); AAT-10.19 (threats); AAT-10.2 (maturity, threats); AAT-10.3 (maturity, threats); AAT-10.4 (maturity, threats); AAT-10.5 (maturity, threats); AAT-10.6 (maturity, threats); AAT-10.7 (maturity, threats); AAT-10.8 (maturity, threats); AAT-10.9 (maturity, threats); AAT-11 (threats); AAT-11.1 (threats); AAT-11.2 (threats) …

### Assessment objectives

180 added, 0 removed, 4 changed (5956 total)

**Added:** AAT-01.5_A01, AAT-01.5_A02, AAT-01.5_A03, AAT-01.5_A04, AAT-01.5_A05, AAT-01.5_A06, AAT-01.5_A07, AAT-12.5_A01, AAT-12.6_A01, AAT-29.24_A01, AAT-29.24_A02, AAT-29.24_A03, AAT-29.24_A04, AAT-33_A01, AAT-33_A02, AAT-33_A03, AAT-33_A04, AST-05.2_A01, AST-05.2_A02, AST-05.2_A03, CFG-09.1_A01, CFG-09.2_A01, CFG-09.2_A02, CFG-09.2_A03, CFG-09.2_A04, CFG-09.3_A01, CFG-09_A01, CPL-03.8_A01, CPL-03.8_A02, GOV-01.4_A01, GOV-01.4_A02, GOV-10.1_A01, GOV-10.1_A02, GOV-19.3_A01, GOV-19.3_A02, GOV-19.3_A03, GOV-19.3_A04, GOV-21_A01, GOV-21_A02, GOV-21_A03, GOV-21_A04, GOV-21_A05, GOV-21_A06, GOV-21_A07, IAC-01.4_A01, IAC-01.4_A02, IAC-01.4_A03, IAC-15.10_A01, NET-06.8_A01, NET-06.8_A02 …
**Changed:** MON-03_A13 (scf_defined_parameters); RSK-01_A12 (scf_defined_parameters); RSK-09_A31 (scf_defined_parameters); TPM-05_A05 (scf_defined_parameters)

### Evidence requests

13 added, 0 removed, 3 changed (316 total)

**Added:** E-QTS-01, E-QTS-02, E-QTS-03, E-QTS-04, E-QTS-05, E-QTS-06, E-QTS-07, E-QTS-08, E-QTS-09, E-QTS-10, E-QTS-11, E-QTS-12, E-QTS-13
**Changed:** E-CPL-01 (scf_controls); E-SAT-05 (scf_controls); E-THR-03 (scf_controls)

### Threats

1 added, 0 removed, 1 changed (42 total)

**Added:** MT-28
**Changed:** MT-12 (description)

### Compensating controls

66 added, 0 removed, 0 changed (1371 total)

**Added:** AAT-01.5, AAT-12.5, AAT-12.6, AAT-29.24, AAT-33, AST-05.2, CFG-09, CFG-09.1, CFG-09.2, CFG-09.3, CPL-03.8, GOV-01.4, GOV-10.1, GOV-19.3, GOV-21, IAC-01.4, IAC-15.10, NET-06.8, NET-06.9, PRI-01.12, QTS-01, QTS-01.1, QTS-01.2, QTS-01.3, QTS-01.4, QTS-02, QTS-02.1, QTS-02.2, QTS-02.3, QTS-03, QTS-03.1, QTS-03.2, QTS-03.3, QTS-03.4, QTS-04, QTS-04.1, QTS-04.2, QTS-04.3, QTS-05, QTS-05.1, QTS-05.2, QTS-06, QTS-06.1, QTS-06.10, QTS-06.2, QTS-06.3, QTS-06.4, QTS-06.5, QTS-06.6, QTS-06.7 …

### Crosswalk frameworks

25 added, 22 removed, 65 with mapping changes (252 total)

**Added:** americas-can-osfi-self-assessment, americas-can-osfi-self-assessment-2, apac-aus-ism-2026-march, apac-jpn-appi-2020, apac-mys-bnm-rmit-2025, emea-aut-dpa-2018, emea-bel-act-30-2018, emea-esp-ccn-stic-825-2026, emea-eu-cyber-resilience-act-2024, emea-eu-cyber-resilience-act-annex-i-2024, emea-eu-psd2-2015, emea-gbr-dpa-2018, emea-hun-act-cxii-2011, emea-irl-dpa-2018, emea-isr-cmo-2-0, emea-isr-ppl-5741-2025, emea-ita-pdpc-2018, emea-pol-act-10-2018, emea-rus-152-fz-2025, general-mitre-att_ck-16-1, general-nist-800-172-r3, general-nist-800-172a-r3, general-nist-cswp-39, usa-federal-law-33-cfr-part-101-subpart-f, usa-state-nv-privacy-law-2023
**Removed:** amaericas-can-osfi-self-assessment, apac-aus-ism-2024-june, apac-aus-privacy-act-1998, apac-jpn-ppi-2020, emea-aut-fappd-2000, emea-bel-act-8-1992, emea-esp-boe-a-2022-7191, emea-esp-ccn-stic-825-2023, emea-esp-decree-1720-2007, emea-eu-cyber-resilience-act-2022, emea-eu-cyber-resilience-act-annexes-2022, emea-gbr-dpa-1998, emea-hun-isdfi-2011, emea-irl-dpa-2003, emea-isr-cmo-1-0, emea-isr-ppl-5741-1981, emea-ita-pdpc-2003, emea-pol-act-29-1997, emea-rus-federal-law-27-2006, emea-us-psd2-2015, general-mitre-att&ck-16-1, general-nist-800-172
**Mapping changes:** americas-arg-ppd-2018, americas-bhs-dpa-2003, americas-bmu-mba-coc-2020, americas-bra-lgpd-2018, americas-can-itsp-10-171-2025, americas-can-pipeda-2000, americas-chl-act-19628-1999, americas-col-law-1581-2012, americas-mex-fdpa-2010, apac-aus-cop-sitc-2020, apac-aus-privacy-principles-2026, apac-aus-ps-cps-230-2023, apac-aus-ps-cps-234-2019, apac-chn-csnip-2012, apac-chn-data-security-law-2021, apac-chn-pipl-2021, apac-hkg-pdo-2022, apac-ind-privacy-rules-2011, apac-jpn-ismap, apac-kor-pipa-2011, apac-mys-pdpa-2010, apac-nzl-hisf-microsmall-2023, apac-nzl-hisf-mlhsp-2023, apac-nzl-ism-3-9, apac-nzl-privacy-act-2020, apac-phl-dpa-2012, apac-sgp-cyber-hygiene-practice-2019, apac-sgp-mas-trm-2021, apac-sgp-pdpa-2012, apac-twn-pdpa-2025 …

## SCF 2026.1.1 — initial canonical release

First release of sc2in/scf: 1,468 controls across 33 families, 249
Expand Down
26 changes: 23 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,33 @@ ground truth, output contracts, and acceptance proofs. Work milestone by milesto
- praxis: `/home/tsunami/code/sc2/praxis` — `scripts/generate-facts.py` (hardcodes
`scf-2026-1-1.json`), `modules/flake/checks.nix` `praxis-facts-fresh`. Acceptance:
facts.json regenerated from this repo's `data/` is **byte-identical**.
**The catalog filename carries the SCF version**, so it is now `scf-2026-2.json` —
de-hardcode praxis (glob `scf-*.json` or read `.scf-version`) before bumping its pin.
- policypress: `/home/tsunami/code/sc2/policypress` — `src/control_report.zig` reads
`data/scf.json` (bare array of `{domain, control_id, control}`).
- grc: `/home/tsunami/code/sc2/grc` — `import/import-scf.mjs` consumes
`scf-api/docs/api/*.json` (submodule); count asserts are its acceptance test.
- Known 2026.1.1 deviations (documented in PLAN.md): MON-03.3 evidence_requests workbook
typo (override: drop), TDA-11.2 `["[object Object]"]` upstream parser bug (we emit real
values; allowlisted in the tree proof).
- Known deviations (documented in PLAN.md): MON-03.3 evidence_requests workbook typo
(override: drop — still present at 2026.2, so the entry is deliberately NOT
version-scoped), TDA-11.2 `["[object Object]"]` upstream parser bug (we emit real
values; allowlisted in the tree proof). The hackIDLE oracle equivalence proof is frozen
at 2026.1.1 — the archive has no later release to compare against.

## Upstream renames tabs and headers between releases

SCF 2026.2 renamed the `Authoritative Sources` tab to `Focal Documents` and shortened the
compensating-controls `SCF Control #` header to `SCF #`, breaking autopilot. So:

- `SHEET_PATTERNS` (workbook.py) maps each logical kind to an **ordered tuple** of accepted
patterns, current name first; `Sheet.col(..., alts=(...))` does the same for headers.
Add the new name, keep the old — one parser must read every supported release, and
`tests/test_integration_real.py` pins ground-truth counts per version.
- Tab names match via `norm_key`, same as column headers, so invisible whitespace drift
does not summon a human.
- A rename found in production is fixable **without a code change**: the `resolution`
section of `overrides/overrides.json` carries `sheets[]`/`columns[]` fallbacks. Those are
tried only after the built-in patterns miss and raise a `resolution-alias` WARN, so the
alias gets folded back into `SHEET_PATTERNS` later.

## House style

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ SC2's canonical, validated [Secure Controls Framework](https://securecontrolsfra
dataset: official SCF Excel workbook → validated `data/*.json` → static JSON API under
`docs/` (GitHub Pages).

- **1,468 controls** across 33 families (SCF 2026.1.1), 249 framework crosswalks,
5,776 assessment objectives, 303 evidence requests, risk/threat catalogs,
- **1,534 controls** across 34 families (SCF 2026.2), 252 framework crosswalks,
5,956 assessment objectives, 316 evidence requests, risk/threat catalogs,
compensating controls, privacy principles.
- **Validated**: every id-bearing cell is checked for cross-sheet referential
integrity before a release ships. The SCF master workbook is maintained by hand;
Expand Down
Loading
Loading