Forged `Entire-Checkpoint` lines in commit bodies can select and mutate unrelated checkpoints
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 5.1k
- Forks
- 475
- Avg merge
- 1d 11h
- Merged PRs (30d)
- 178
Description
Summary
Checkpoint resolution parses trailers out of the whole commit message, including in code paths that decide whether to create, reuse, or mutate checkpoint storage. A line shaped like Entire-Checkpoint: <id> anywhere in the body is indistinguishable from a real squashed trailer. So a commit body that names an existing checkpoint ID, whether pasted, reverted, or written by someone else, gets treated as authority over that checkpoint.
Impact
A commit body naming an existing checkpoint ID can:
- make PostCommit condensation bind the commit to a checkpoint that has nothing to do with it, instead of minting a fresh one
- make
entire checkpoint attachwrite the attached session into the unrelated checkpoint - make
entire checkpoint explain --commit ... --generatepersist a generated summary into it viastore.Write - make legacy session migration (
migrateShadowBranchIfNeeded) advanceBaseCommitandAttributionBaseCommitoff a body ID, skipping normal migration and changing future attribution
Checking that the ID exists in storage doesn't help. Existence proves nothing about the ID's relationship to this commit.
Steps to reproduce
-
Work in a repo with Entire enabled, in an agent session that has already produced at least one checkpoint. Note an existing checkpoint ID from
entire checkpoint list(call it<unrelated-id>). -
Make an unrelated code change in a new agent session.
-
Commit it with a message whose body contains a checkpoint-shaped line naming the existing ID:
git commit -m "innocent change Entire-Checkpoint: <unrelated-id>" -
Let the PostCommit hook run, then inspect
entire checkpoint listand the commit's linkage.
Expected: the body line is ignored. The commit gets a fresh checkpoint for the new session's work.
Actual: the parser picks up <unrelated-id> from the body and condensation binds the commit to that unrelated checkpoint. The same forged line steers attach, explain --generate, and legacy migration, since they resolve IDs through the same whole-message parse.
The indented variant also works, even though Git itself doesn't consider it a trailer: Entire-Checkpoint: <unrelated-id> (leading whitespace) is left-trimmed before classification and accepted, while git interpret-trailers --parse returns nothing for the same message.
Root cause
One parser answers two different questions.
Discovery asks which checkpoints a commit's history mentions. Scanning the whole message is correct there, because GitHub and native squash commits embed several checkpoint trailers in the body.
Authorization asks which checkpoint an operation may mutate. Only the final trailer block can answer that, and the parser never made the distinction.
The parser is also looser than Git's own trailer grammar, which widens the hole. Besides the indentation issue above, commit-message-file cleanup hard-codes # instead of honoring core.commentChar.
Suggested fix
Split the parser in two. Keep whole-message parsing for read-only discovery only (squash-history resume, listing), so commits with several embedded trailers keep working. Add a strict parser that reads only the final trailer block and matches Git's grammar: no leading whitespace, comment-char-aware cleanup. Route every mutation decision through the strict parser: commit hooks, attach, explain --generate, and legacy migration reconciliation. When the recorded commit hash is unavailable in migration, decline reconciliation rather than trusting body text.
Related: #2168 covers broader trailer-line anchoring and doesn't overlap with this.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Locate the shared checkpoint trailer parser and trace its callers in PostCommit, checkpoint attach, explain --generate, and migrateShadowBranchIfNeeded; inspect store.Write and Git's trailer parsing behavior first. Reproduce the forged body-line case and cover the affected mutation paths. Done means discovery still handles embedded trailers, while mutation decisions accept only a strict final trailer block with comment-char-aware cleanup.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- git, go
- Domain
- cli, devtools, security
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100