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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 34 additions & 3 deletions spec/audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,11 +340,22 @@ def check_interface(path, contract, text):
return findings


# A `uses: <action>@<40-hex sha>` pin, plus only a trailing Dependabot version comment (` # v1.2.3` - the
# leading `v`-or-digit is required). Dependabot bumps both per repo, so that drift is governed (like EOL), not a
# fidelity deviation. Anchored to `uses:`, so a 64-hex docker digest and a tag/branch ref (`@v4`) do not match.
# Hex is case-insensitive, and a hand-written note on a pin is not version-shaped, so it survives to be compared.
_ACTION_PIN = re.compile(r"(\buses:[ \t]*[^\s@]+)@[0-9a-fA-F]{40}(?:[ \t]+#[ \t]*v?[0-9][\w.\-]*)?")


def normalize(text):
"""Reduce a carried unit to its comparable form: neutralize line endings, since EOL variance is governed
separately, not a fidelity deviation. No placeholder masking - see spec/fidelity-model.md "Normalization".
"""Reduce a carried unit to its comparable form: neutralize line endings (EOL variance is governed
separately, not a fidelity deviation) and neutralize a Dependabot-owned action pin - the 40-hex commit a
`uses: <action>@<sha>` line pins, together with its trailing ` # vN` version comment - since Dependabot
bumps those per repo and that drift is governed, same category as EOL. This is NOT placeholder masking of
declared per-file tokens; see spec/fidelity-model.md "Normalization".
"""
return text.replace("\r\n", "\n").replace("\r", "\n")
text = text.replace("\r\n", "\n").replace("\r", "\n")
return _ACTION_PIN.sub(r"\1@<pin>", text)


@functools.lru_cache(maxsize=1024) # bounded; the keys that recur across repos are the canonical and its history
Expand Down Expand Up @@ -773,6 +784,26 @@ def _selftest():
if got != want:
ok = False
print(f" {'ok ' if got == want else 'FAIL'} want={str(want):>8} got={str(got):>8} verbatim: {label}")
# Action-pin neutralization: a Dependabot uses:@<sha> bump (both the 40-hex sha and its ` # vN` comment)
# must not count as verbatim drift, but a changed action name must. This is what lets a verbatim workflow
# region survive routine action bumps while still catching a real fork.
pin_a = " - uses: actions/checkout@" + "a" * 40 + " # v7.0.0\n"
pin_b = " - uses: actions/checkout@" + "B" * 40 + " # v7.0.1\n" # uppercase hex + version bump
pin_struct = " - uses: actions/setup-node@" + "a" * 40 + " # v7.0.0\n"
note_x = " - uses: actions/checkout@" + "a" * 40 + " # kept for the audited build\n"
note_y = " - uses: actions/checkout@" + "a" * 40 + " # kept for a different reason\n"
if content_hash(pin_a) != content_hash(pin_b):
ok = False
print(" FAIL action-pin: a uses:@<sha> bump (sha + # vN, uppercase hex) should normalize equal")
elif content_hash(pin_a) == content_hash(pin_struct):
ok = False
print(" FAIL action-pin: a changed action name must still hash differently")
elif content_hash(note_x) == content_hash(note_y):
ok = False
print(" FAIL action-pin: a hand-written (non-version) pin comment must survive to be compared")
else:
print(" ok action-pin: version bump normalizes equal, changed action differs, hand-written note survives")

# Region extraction and hashing: a forked github-release block must hash differently from the canonical.
region = split_jobs(rel_ok).get("github-release")
forked_region = split_jobs(rel_ok.replace(" merge-multiple: true\n", " artifact-ids: 1\n")).get("github-release")
Expand Down
4 changes: 2 additions & 2 deletions spec/fidelity-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Each [`spec/files.json`][files] entry declares one `fidelity`, defaulting to `pr

- **presence** - the unit exists (a file, or a markdown section heading). The audit's baseline check.
- **intent** - carried faithfully but judged by meaning, not bytes. A downstream copy legitimately differs (a governed divergence or a paraphrase), and equivalence is a human call via `intentRef`. The audit asserts nothing beyond presence.
- **verbatim** - byte-identical to the hub's canonical after line-ending normalization. The audit content-hashes the downstream copy against canonical. It applies to a whole file, a workflow job region (a job selected by key), or a markdown section region (a `## heading` block selected by name). The section granularity lets one file be **intent overall while a few of its sections are verbatim** - a universal rule block stays byte-identical fleet-wide even though the rest of the document is a repo-adapted paraphrase, so a stale section or a missing rule is caught while its heading still passes the presence check.
- **verbatim** - byte-identical to the hub's canonical after line-ending and action-pin normalization. The audit content-hashes the downstream copy against canonical. It applies to a whole file, a workflow job region (a job selected by key), or a markdown section region (a `## heading` block selected by name). The section granularity lets one file be **intent overall while a few of its sections are verbatim** - a universal rule block stays byte-identical fleet-wide even though the rest of the document is a repo-adapted paraphrase, so a stale section or a missing rule is caught while its heading still passes the presence check.
- **interface** - an overridable body that must honor a named contract. The audit checks the contract by name and wiring, never the body.

Fidelity is a declared field defaulting to `presence`, never inferred from `whole`/`placeholders`. `.editorconfig` and `.markdownlint-cli2.jsonc` are both whole with no placeholders yet sit at opposite fidelity, because the discriminator is governance, not field shape.
Expand All @@ -30,7 +30,7 @@ The fixed interface of a workflow is stated in [`AGENTS.md`][agents] ("Orchestra

## Normalization

A verbatim check compares content by hash after **line-ending normalization only** - EOL variance is governed by the line-ending rules, not a fidelity deviation. It does **not** mask placeholders: a verbatim unit carries none. The files that declare a `placeholders` list (for example `.github/copilot-instructions.md` with `<owner>`, `<repo>`, `<N>`) are fidelity `intent`, judged by hand and never hashed. Masking could not serve a hash anyway - a downstream copy holds the substituted value (`ptr727`), not the token (`<owner>`), so masking the token in the canonical alone would guarantee a mismatch. A verbatim unit that ever needed a per-repo substitution would require template-matching (the canonical as a pattern, the copy as an instance), not this content hash. None does today.
A verbatim check compares content by hash after **line-ending and action-pin normalization** - EOL variance is governed by the line-ending rules, and a `uses: <action>@<sha>` pin (with its trailing `# vN` comment) is Dependabot-owned and bumped per repo, so both are governed drift rather than a fidelity deviation. This keeps a verbatim workflow job region (the `github-release` job) from flagging on every routine action bump while still catching a real structural fork. It does **not** mask placeholders: a verbatim unit carries none. The files that declare a `placeholders` list (for example `.github/copilot-instructions.md` with `<owner>`, `<repo>`, `<N>`) are fidelity `intent`, judged by hand and never hashed. Masking could not serve a hash anyway - a downstream copy holds the substituted value (`ptr727`), not the token (`<owner>`), so masking the token in the canonical alone would guarantee a mismatch. A verbatim unit that ever needed a per-repo substitution would require template-matching (the canonical as a pattern, the copy as an instance), not this content hash. None does today.
Comment thread
ptr727 marked this conversation as resolved.

## Stale Versus Modified

Expand Down