Skip to content

fix(gitread): run blame and grep without the repository's attributes - #781

Merged
ThomasK33 merged 9 commits into
mainfrom
blame-grep-no-tree-attributes
Nov 8, 2026
Merged

ThomasK33 merged 9 commits into
mainfrom
blame-grep-no-tree-attributes

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Nov 8, 2026 •

Copy link
Copy Markdown
Member

Why

Blame and grep read git's stderr to classify a failure (#760, #772). A repository's own .gitattributes files can put repository text into that stderr. A directory whose name holds a newline, with a crafted .gitattributes in it, makes git print a warning that quotes the path unescaped. That path can spell a whole line error: '<name>': unable to read <oid>, and grep then reads a good repository as unreadable and answers 500. git reads tree attributes in a work tree, in a bare repository with attr.tree or GIT_ATTR_SOURCE set, and in a bare repository by default on git 2.43.0 to 2.45.2. The shipped image does not read them by default. The root verifier of #772 found this.

This PR removes the repository text from git's stderr. It adds no text pattern.

What changes

  • Blame and Grep run git with GIT_ATTR_SOURCE set to the empty tree (noTreeAttrsEnv in internal/gitread/gitread.go). git then reads no .gitattributes from a tree or a work tree. It still reads $GIT_DIR/info/attributes and the global and system attribute files, which a push cannot write. git 2.41 and later honor the variable. An older git ignores it, and an older git reads no tree attributes in a bare repository.
  • Run still sets GIT_NO_REPLACE_OBJECTS for these children: gitlimit adds it to any Cmd.Env.
  • The repository's attributes also stop steering grep's -I binary detection and blame's textconv. Where git read them (a work tree, attr.tree or GIT_ATTR_SOURCE=HEAD in the server's environment, a bare repository on git 2.43.0 to 2.45.2), a text file marked binary or -diff is now searched, and a file with a NUL byte and the diff attribute is skipped. A bare repository on git before 2.43.0, or from 2.45.3 on, as the server runs, read no tree attributes before, so nothing changes there.
  • lfsscan's three git children start outside gitlimit's registry and disabled replacements per call with --no-replace-objects, which the new rule bans. They now get GIT_NO_REPLACE_OBJECTS through gitlimit.NoReplaceObjects.
  • Tests for four gaps in grep's read-error line that the fix: refuse read paths outside the repository, and read blame and grep failures right #772 verifier listed: a 64-hex (SHA-256) object id, a 300-byte path in one write, the line without its quoted name (not a read error), and ErrBadPattern at the gitread level.
  • A root AGENTS.md with a ## Code Review Rules section. Replacement objects: every git child runs with GIT_NO_REPLACE_OBJECTS=1, with no per-call handling. Git's stderr: do not add or widen a text pattern that catches forged lines, because repository text such as a path with a newline can spell any line. Keep repository text out of git's stderr or decide by object state instead. Updating a classifier for git's own message changes, or replacing one with an object-state check, is fine.

Verification

  • TestBlameAndGrepReadNoTreeAttributes builds a repository with two such directories (one forges grep's read-error line, one forges blame's fatal: -L line). It runs grep and blame in a work tree, and in a bare repository with GIT_ATTR_SOURCE=HEAD and with attr.tree=HEAD. Grep finds its matches and answers no match. Blame of a missing path is ErrNotFound, and blame past the end is ErrBadRange.
  • TestBlameAndGrepSetTheAttributeSource records the GIT_ATTR_SOURCE that each git child gets, and wants the empty tree for blame and for grep. The work-tree case of TestBlameAndGrepReadNoTreeAttributes skips on git before 2.41, which ignores the variable.
  • On git 2.55, a work tree with t.txt binary, m.txt -diff and b.dat diff (b.dat holds a NUL byte) greps -I to b.dat and plain.txt with the attributes, and to m.txt, plain.txt and t.txt with GIT_ATTR_SOURCE set to the empty tree.
  • TestGrepBadPatternIsOnlyGitsHead pins that only a last stderr line that starts with fatal: -e option, ' is ErrBadPattern: not that text later in the line, and not on an earlier line.
  • TestReachableLFSOIDs_IgnoresReplacementRefs (lfsscan) fails without GIT_NO_REPLACE_OBJECTS on the three children: "replacement ref hid a reachable pointer OID". It replaces a commit, which only rev-list reads. TestReachableLFSOIDs_IgnoresReplacedBlobs replaces the pointer blob by text, which cat-file --batch would read, and by an empty blob, which cat-file --batch-check would report as size 0. With the variable on only one or two of the three children, each of the four such mutants fails one of the two tests.
  • Mutant checks (each fails at least one test):
    • grep without noTreeAttrsEnv: attr_source_test.go:48 ("grep one = 0 files ... could not read the repository").
    • blame without noTreeAttrsEnv: attr_source_test.go:86 (blame's git gets no GIT_ATTR_SOURCE). Blame's classified failures read no attributes, so only the recording test catches this one.
    • The watcher's id pattern without the 64-hex form, without the ': prefix, or keeping only the head of a long write: grep_test.go:238.
    • runGrep returning a bad pattern's error without ErrBadPattern: grep_test.go:136.
    • The bad-pattern head matched with strings.Contains, against the whole stderr, or on any line: grep_test.go:279.
    • lfsscan's children without gitlimit.NoReplaceObjects: scan_test.go:299.
  • At 2aa7081, with -race: build, vet, golangci-lint (0 issues), the doc-link check and both --fingerprints-only checks pass, and internal/gitread/... and internal/lfsscan/... pass. 05a9f93 and 2aa7081 change only docs, comments and lfsscan tests after ccca7a5.
  • At ccca7a5, with -race: build, vet (also GOOS=darwin go vet of internal/gitread and internal/lfsscan), golangci-lint (0 issues), the doc-link check and both --fingerprints-only checks pass. internal/gitread/... and internal/lfsscan/... pass, and internal/jobs, which runs the LFS walk, passes on PostgreSQL.
  • Lanes ran at 522659b and at 48cce16, which changes only the comment on noTreeAttrsEnv. Build, vet, golangci-lint (0 issues), the doc-link check and both --fingerprints-only checks pass. internal/gitread/... passes. internal/api and cmd/openstorage pass on PostgreSQL (-tags=openstorage_viewcheck), SQLite and S3-WAL. The first PostgreSQL run of cmd/openstorage stopped before it reached the code (the postgres container exited at start), and its rerun at 48cce16 passes.

Size

220 changed lines: 37 of production Go, 172 of tests and 11 of docs.


Generated with xum • Model: anthropic:claude-opus-5-5 • Thinking: xhigh

GIT_ATTR_SOURCE, attr.tree, or a bare repository on git 2.43 to 2.45),
a .gitattributes under a directory whose name holds a newline makes git
print a warning that quotes the path unescaped, which can forge any
stderr line. blame and grep, which read git's stderr, now run with
GIT_ATTR_SOURCE set to the empty tree, so git reads no tree attributes.
git 2.41 and later honor the variable; an older git ignores it and reads
no tree attributes in a bare repository.

Change-Id: Icfb04e9ac6156e05ce73cf023f49f61987cc6ec2
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Change-Id: If4af6fcfc730cf5a666ff7a451c2c42c5afad672
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…ne and its quoted name

The tests now send git grep's unreadable-object line with a 64-hex id,
with a 300-byte path in one write, and without the quoted name (not a
read error), and check that a bad pattern is ErrBadPattern.

Change-Id: I688b50fe977874406a711c569bfe5102a052001a
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…s and git's stderr

Change-Id: Ib5521da28e2249608ccfc4121a0d489d54889a8e
Signed-off-by: Thomas Kosiewski <tk@coder.com>
Blame reads no attributes for the failures it classifies, so only grep's
answer shows the variable. A fake git now records it for both.

Change-Id: I9dddaccc0a6b76e709dfcd127ec46fc8fd414a6a
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…sEnv

The server writes no info/attributes file. Say which files git still
reads with GIT_ATTR_SOURCE set to the empty tree, and that a push cannot
write them.

Change-Id: Ia48fbfd730732921d6730b0dff892e665a00f822
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 8, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-08T16:31:43.805477Z 2aa7081 Manual request
🔒 Security Review ✅ Completed 2026-11-08T16:32:54.752232Z 2aa7081 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

Reviewed commit: 48cce16ad9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 48cce16ad9

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…rpen the review rules

Round 1 items on #781:
- AGENTS.md: the git stderr rule bans only adding or widening a text
  pattern that catches forged lines, says why, and allows updating a
  classifier for git's own message changes or replacing one with an
  object-state check. Both rules state outcomes, not function lists.
- lfsscan's three git children start outside gitlimit's registry and
  disabled replacements per call with --no-replace-objects. They now
  get GIT_NO_REPLACE_OBJECTS through gitlimit.NoReplaceObjects, and
  TestReachableLFSOIDs_IgnoresReplacementRefs goes red without it.
- noTreeAttrsEnv's comment and the gitread README say that grep's -I
  binary detection and blame's textconv no longer read the repository's
  attributes either.
- TestBlameAndGrepReadNoTreeAttributes skips its work-tree case on git
  before 2.41, which ignores GIT_ATTR_SOURCE, and the comment of
  TestGrepAttributeErrorsAreNotReadErrors says why grep still works.
- TestGrepBadPatternIsOnlyGitsHead pins that only a last line starting
  with git's "fatal: -e option, '" is ErrBadPattern.

Change-Id: I37aa13b9b8f4c671fb78f0937cbc86156c67a01b
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: ccca7a5f8d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: ccca7a5f8d

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

…attributes

A bare repository reads HEAD's attributes by default only on git 2.43.0
to 2.45.2 (attr.c sets the default at v2.45.2, not at v2.45.3). The
noTreeAttrsEnv comment, the gitread README and the test comment said
2.43 to 2.45, and that 2.46 and later read none.

AGENTS.md's replacement rule states the outcome for a runner outside
the registry (it adds the variable to its environment), and says that
refusing a push to refs/replace/* is not per-call handling.

Change-Id: Ica52e8db9d55ff0bb6d5fff02c1f0ef62a601df3
Signed-off-by: Thomas Kosiewski <tk@coder.com>
…TTR_SOURCE=HEAD

The #781 verifier's gap: TestReachableLFSOIDs_IgnoresReplacementRefs
replaces only a commit, so GIT_NO_REPLACE_OBJECTS on rev-list alone
passed it. TestReachableLFSOIDs_IgnoresReplacedBlobs replaces the
pointer blob by text, which cat-file --batch would read, and by an empty
blob, which cat-file --batch-check would report as size 0. Leaving the
variable off any one of the three children fails a test.

The noTreeAttrsEnv comment and the gitread README also list
GIT_ATTR_SOURCE=HEAD in the server's environment among the setups whose
answers change.

Change-Id: I6028bee8bbc2b43353e09cae07fadbdc53a77969
Signed-off-by: Thomas Kosiewski <tk@coder.com>
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 2aa70817fb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: 2aa70817fb

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@ThomasK33
ThomasK33 added this pull request to the merge queue Nov 8, 2026
Merged via the queue into main with commit 5326d70 Nov 8, 2026
10 of 11 checks passed
@ThomasK33
ThomasK33 deleted the blame-grep-no-tree-attributes branch November 8, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant