Skip to content

feat(catalog)!: embed the CIS AWS Benchmark v6.0 controls only - #28

Merged
antonbabenko merged 4 commits into
masterfrom
fix/report-unknown-control
Nov 9, 2026
Merged

antonbabenko merged 4 commits into
masterfrom
fix/report-unknown-control

Conversation

@antonbabenko

Copy link
Copy Markdown
Member

Problem

ctfkit report exited 2 when a served module manifest named a control id the embedded catalog does not hold:

ctfkit: ctf.coverage.delegation_unresolved: ctfkit: finding ctf.coverage.delegation_unresolved names control vpc_security_group_remote_administration_ipv4, which the catalog 2026.9.3 does not hold

delegation_unresolved copied the manifest's control id into ctf.controlIds. ValidateFor refuses an id the catalog lacks, so one unknown id failed the whole run.

Changes

  • report: a delegation warning for an id the loaded catalog lacks names the id in its message, carries no ctf.controlIds, and the report completes. ValidateFor stays strict for analyzer bugs.
  • Embedded catalog: holds the CIS AWS Benchmark v6.0 controls only (69), each with its CIS v6.0 clauses, plus the module entries. A full catalog is read with --catalog FILE or from ~/.ctf/control-requirements.yaml; ctfkit catalog pull is planned. TestEmbeddedCatalogIsTheCISSubset fails if the embedded catalog holds anything more.
  • disabled_control_unknown is now an error. Disabling a real control the loaded catalog does not hold lands in this rule. At warning level, that disable would pass CI under the default --fail-on error.
  • module_source: a raw-resource finding lists only the controls the suggested module enforces, as ModuleRef.Controls documents. The catalog can place controls on a covered type that the module does not enforce.
  • Fixtures: plan cases 06 and 07 waive the raw-bucket finding with a rule: ctf.coverage.module_source entry, because that finding now carries no control id. Goldens are re-recorded, and testdata/fixtures/README.md records the decision.
  • Docs: docs/configuration.md (catalog, exceptions), docs/analyzers.md, the GitHub adapter README, the sample repo, and DEVELOPING.md.

Breaking changes

  • With the embedded catalog, findings carry control ids and frameworks for CIS v6.0 controls only. ctf.risk.sensitive_change_* and delegation findings for other controls still fire, with no control id.
  • A control-keyed exception for a control outside CIS v6.0 no longer matches. Use a rule entry, or load the full catalog.
  • ctf.policy.disabled_control_unknown is reported at error level, not warning.
  • The GitHub Action and Spacelift configurations pass --catalog embedded by default. To use a full catalog there, pass its file.

Tests

  • TestReportToleratesAControlTheCatalogLacks (end to end) and TestDelegationUnresolvedToleratesAControlTheCatalogLacks: both fail with the exit-2 error before the fix.
  • TestRawResourceCarriesOnlyTheModulesControls, TestEmbeddedCatalogIsTheCISSubset, and TestFixture03ControlIDs updated for the unknown-disable error.
  • go test -race ./..., golangci-lint v2.15.0 (0 issues), make conformance schemacheck hygiene vulncheck and prek run --all-files pass.
  • The reported reproduction (vpc, kms, sns, ecs, alb, ec2-instance and route53 from the ISO 27001 endpoint) writes the coverage table; the two remote-administration ids resolve as CIS v6.0 controls.

…exiting 2

A served manifest newer than the embedded catalog can delegate a control
id the catalog does not hold. delegation_unresolved put that id in
ctf.controlIds, the runner's emit check refused it, and report exited 2.
The finding now names the id in its message, carries no control id, and
the report completes.
A raw-resource finding carried every control the catalog places on the
resource type, alongside a remediation naming one module. ModuleRef.Controls
documents that the remediation holds every id the finding carries; the
finding now carries the intersection, so it never says the module closes a
control it does not enforce.
The catalog embedded in ctfkit now holds the CIS v6.0 controls, each with
its CIS v6.0 clauses, and the module entries. It includes
vpc_security_group_remote_administration_ipv4 and _ipv6. A full catalog is
read with --catalog FILE or from ~/.ctf/control-requirements.yaml;
ctfkit catalog pull is planned. TestEmbeddedCatalogIsTheCISSubset fails if
the embedded catalog holds more.

ctf.policy.disabled_control_unknown is now an error: a ?disable= of a real
control outside the loaded catalog lands there, and a warning would let it
pass CI.

BREAKING CHANGE: with the embedded catalog, findings carry control ids and
frameworks for CIS v6.0 controls only. A control-keyed exception for a
control outside CIS v6.0 no longer matches; use a rule entry or load the
full catalog. ctf.policy.disabled_control_unknown is reported at error
level instead of warning.
The unknown-control messages of delegation_unresolved and
disabled_control_unknown said the embedded catalog holds CIS v6.0 only and
suggested --catalog even when a full catalog was loaded. The hint now
appears only when the loaded catalog has the embedded catalog's version.
The end-to-end report test also asserts the delegation finding for an
unknown id carries no control id.
@antonbabenko
antonbabenko merged commit 4daade7 into master Nov 9, 2026
2 checks passed
@antonbabenko
antonbabenko deleted the fix/report-unknown-control branch November 9, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant