Skip to content

Remediation rewrites unrelated .project/ references (e.g. security.policy -> bug_report.md) via stale CONTROL_REFERENCE_MAPPING #482

Description

@mlieberman85

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    help wantedExtra attention is neededmodule-baselineOSPS Baseline modulequalityCode quality and maintainability

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions