entireio / entireio/cli

Forged `Entire-Checkpoint` lines in commit bodies can select and mutate unrelated checkpoints

Open
#2,255 0 comments 0 reactions 0 assignees View on GitHub

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 attach write the attached session into the unrelated checkpoint
  • make entire checkpoint explain --commit ... --generate persist a generated summary into it via store.Write
  • make legacy session migration (migrateShadowBranchIfNeeded) advance BaseCommit and AttributionBaseCommit off 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

  1. 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>).

  2. Make an unrelated code change in a new agent session.

  3. 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>"
    
  4. Let the PostCommit hook run, then inspect entire checkpoint list and 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.