Summary
After any successful file_create remediation, the orchestrator calls update_config_after_file_create with the hard-coded Python CONTROL_REFERENCE_MAPPING, which overwrites whatever the user had at that .project/ path. The mapping disagrees with the TOML (locator.project_path) and with what the remediation actually creates:
| Control |
Creates |
Mapping writes |
| DO-02.01 |
.github/ISSUE_TEMPLATE/bug_report.md |
security.policy |
| DO-01.01 |
README.md |
quality.changelog |
| GV-01.01 |
GOVERNANCE.md |
governance.maintainers |
| GV-01.02 |
MAINTAINERS.md |
governance.code_of_conduct |
| LE-01.01 |
LICENSE |
legal.contributor_agreement |
The mapping also lists three IDs not in the TOML (DO-01.02, DO-01.03, LI-01.01), and disagrees with locator.project_path for 11 controls. The write happens even when file_create skipped because the file already existed. Note the TOML itself also sets DO-02.01 locator.project_path = "security.policy".
PROJECT_TYPE_EXCLUSIONS and DEFAULT_FILE_LOCATIONS in the same module have no callers (re-exported only).
Impact
.project/project.yaml is user-authored, CNCF-shared metadata. Remediation silently repoints the security policy at an issue template and records README as the changelog, corrupting data other tools and later darnit runs consume. This violates CLAUDE.md "TOML is Source of Truth".
Evidence
- Mapping:
|
CONTROL_REFERENCE_MAPPING: dict[str, str] = { |
|
# Security (standard .project fields) |
|
# SECURITY.md addresses multiple controls (VM and DO domains) |
|
"OSPS-VM-01.01": "security.policy", # SECURITY.md - vulnerability reporting contact |
|
"OSPS-VM-02.01": "security.policy", # SECURITY.md - vulnerability reporting process |
|
"OSPS-VM-03.01": "security.policy", # SECURITY.md - response timeline |
|
"OSPS-DO-02.01": "security.policy", # SECURITY.md (documentation domain) |
|
"OSPS-SA-03.02": "security.threat_model", # Threat model |
|
|
|
# Governance (standard .project fields) |
|
# CONTRIBUTING.md addresses multiple controls |
|
"OSPS-GV-03.01": "governance.contributing", # CONTRIBUTING.md - contribution guide exists |
|
"OSPS-GV-03.02": "governance.contributing", # CONTRIBUTING.md - development process documented |
|
"OSPS-DO-01.02": "governance.contributing", # CONTRIBUTING.md (documentation domain) |
|
"OSPS-GV-04.01": "governance.codeowners", # CODEOWNERS |
|
|
|
# Governance (extension fields - to be upstreamed) |
|
"OSPS-GV-01.01": "governance.maintainers", # MAINTAINERS.md (extension) |
|
"OSPS-GV-01.02": "governance.code_of_conduct", # CODE_OF_CONDUCT.md (standard) |
|
|
|
# Legal (standard .project fields) |
|
"OSPS-LI-01.01": "legal.license", # LICENSE |
|
|
|
# Legal (extension fields) |
|
"OSPS-LE-01.01": "legal.contributor_agreement", # DCO/CLA (extension) |
|
|
|
# Artifacts (extension fields) |
|
"OSPS-BR-02.01": "artifacts.sbom", # SBOM (extension) |
|
"OSPS-BR-03.01": "artifacts.signing", # Release signing (extension) |
|
"OSPS-BR-03.02": "artifacts.provenance", # Provenance (extension) |
|
|
|
# Quality (extension fields) |
|
"OSPS-DO-01.01": "quality.changelog", # CHANGELOG (extension) |
|
|
|
# Documentation (standard .project fields) |
|
"OSPS-DO-01.03": "documentation.readme", # README |
|
"OSPS-DO-03.01": "documentation.support", # SUPPORT.md |
|
} |
|
|
- Call site:
|
|
|
# Update .project/ config with reference to created file |
|
config_updated = False |
|
for handler_inv in remediation_config.handlers: |
|
if handler_inv.handler == "file_create": |
|
extra = handler_inv.model_extra or {} |
|
created_path = extra.get("path") |
|
if created_path: |
|
config_updated = update_config_after_file_create( |
|
local_path=local_path, |
|
control_id=control_id, |
|
created_file_path=created_path, |
|
control_reference_mapping=CONTROL_REFERENCE_MAPPING, |
|
) |
|
if config_updated: |
|
logger.info(f"Updated .project/ with reference: {created_path}") |
|
break |
- Overwrite when different:
|
# Check if reference already exists |
|
existing_path = config.get_path(section, field) |
|
if existing_path == created_file_path: |
|
logger.debug( |
|
f"Control {control_id}: .project/ reference already set to {created_file_path}" |
|
) |
|
return False # Already set, no change needed |
|
|
|
# Set the path reference |
|
_set_config_path(config, section, field, created_file_path) |
|
|
|
# Save the config |
|
save_project_config(config, local_path) |
|
logger.info( |
- DO-02.01 locator:
|
[controls."OSPS-DO-02.01".locator] |
|
project_path = "security.policy" |
Reproduction
Repo with .github/SECURITY.md, README.md, and .project/project.yaml:
name: demo
security:
policy:
path: .github/SECURITY.md
Run _apply_control_remediation("OSPS-DO-02.01", repo, owner="example-org", repo="example-repo", dry_run=False) (from darnit_baseline.remediation.orchestrator), then the same for OSPS-DO-01.01.
- Actual: both
applied, config_updated=True; security.policy.path is now .github/ISSUE_TEMPLATE/bug_report.md; .project/darnit.yaml gains quality.changelog: README.md.
- Expected:
security.policy untouched; no reference written that the TOML does not declare.
Suggested direction
Delete CONTROL_REFERENCE_MAPPING and derive the target from TOML (locator.project_path or an explicit project_update), fix DO-02.01's locator, and never overwrite an existing user-set reference without confirmation. Remove the two unused tables.
Summary
After any successful
file_createremediation, the orchestrator callsupdate_config_after_file_createwith the hard-coded PythonCONTROL_REFERENCE_MAPPING, which overwrites whatever the user had at that.project/path. The mapping disagrees with the TOML (locator.project_path) and with what the remediation actually creates:.github/ISSUE_TEMPLATE/bug_report.mdsecurity.policyREADME.mdquality.changelogGOVERNANCE.mdgovernance.maintainersMAINTAINERS.mdgovernance.code_of_conductLICENSElegal.contributor_agreementThe mapping also lists three IDs not in the TOML (DO-01.02, DO-01.03, LI-01.01), and disagrees with
locator.project_pathfor 11 controls. The write happens even whenfile_createskipped because the file already existed. Note the TOML itself also sets DO-02.01locator.project_path = "security.policy".PROJECT_TYPE_EXCLUSIONSandDEFAULT_FILE_LOCATIONSin the same module have no callers (re-exported only).Impact
.project/project.yamlis user-authored, CNCF-shared metadata. Remediation silently repoints the security policy at an issue template and records README as the changelog, corrupting data other tools and later darnit runs consume. This violates CLAUDE.md "TOML is Source of Truth".Evidence
darnit/packages/darnit-baseline/src/darnit_baseline/config/mappings.py
Lines 56 to 94 in 032493b
darnit/packages/darnit-baseline/src/darnit_baseline/remediation/orchestrator.py
Lines 559 to 575 in 032493b
darnit/packages/darnit/src/darnit/config/resolver.py
Lines 131 to 144 in 032493b
darnit/packages/darnit-baseline/src/darnit_baseline/openssf-baseline.toml
Lines 1420 to 1421 in 032493b
Reproduction
Repo with
.github/SECURITY.md,README.md, and.project/project.yaml:Run
_apply_control_remediation("OSPS-DO-02.01", repo, owner="example-org", repo="example-repo", dry_run=False)(fromdarnit_baseline.remediation.orchestrator), then the same forOSPS-DO-01.01.applied,config_updated=True;security.policy.pathis now.github/ISSUE_TEMPLATE/bug_report.md;.project/darnit.yamlgainsquality.changelog: README.md.security.policyuntouched; no reference written that the TOML does not declare.Suggested direction
Delete
CONTROL_REFERENCE_MAPPINGand derive the target from TOML (locator.project_pathor an explicitproject_update), fix DO-02.01's locator, and never overwrite an existing user-set reference without confirmation. Remove the two unused tables.