Skip to content

[finding] The provenance exception's recompute trusts the PR tree's own scripts/docs-audit/** — a PR editing the generator and the workflow artifact together can self-certify; cheap hardening: fail closed on that co-edit #11084

Description

@os-zhuang

Found by the skills seat during the contract-grade review of the #9866 landing (PR #11078), session session_01MsbKEG4LtERSLaDrbehM3e, 2026-08-22. Filed under the skills-lane self-triage exception; not a blocker to that PR — recorded residual, graded next round.

The residual

recomputeDocsAuditProvenance derives the doc set by executing scripts/docs-audit/affected-docs.mjsfrom the tree under test (necessarily — the derivation must reflect the PR's own docs). Consequence: a PR that edits scripts/docs-audit/affected-docs.mjs (or the generator) and hand-edits .claude/workflows/docs-accuracy-audit.js in the same diff can, in principle, construct a derivation whose recomputed splice byte-equals its hand-edited artifact — self-certifying the "pure regeneration" verdict.

Why it is residual, not a hole

  • The attacker-controlled surface is limited to the generated block's list contents (everything outside the block must still equal the merge-base bytes).
  • The co-edit is fully visible in the PR's file list, and scripts/docs-audit/** edits are unusual on a docs PR — a reviewing seat reading the file list sees it.
  • The pre-existing trust posture is unchanged in kind: the predicate has always been runnable from the PR's own checkout.

Cheap hardening (grading input)

In --test, before consulting provenance: if the submitted path list also contains any scripts/docs-audit/** path, skip the recompute and fail closed with a stated reason ("the tree under test modifies the generator this exception trusts — the path stays governed"). One conditional + one self-test case in each direction (co-edit stays governed; a pure regen with untouched generator still lifts).

Refs: #9866 (the ruled exception) · PR #11078 (the landing under review when found).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions