Repository navigation
fix(util): reject symbolic links that alias .gitmodules - #2278
Merged
Byron merged 2 commits intoOct 9, 2026
Merged
Conversation
`_validate_repo_path` mirrors Git's `verify_path` for tree and index entry paths, but it only ever saw the path. Git's check there is mode dependent: an entry that makes `.gitmodules` a symbolic link is refused, because the submodule configuration would then be read through the link, from outside the repository. That is why `git update-index --add --cacheinfo 120000,<sha>,.gitmodules` fails with `Invalid path`, `git read-tree` fails with `invalid path`, and `git fsck --strict` reports `gitmodulesSymlink`. GitPython accepted such an entry in both directions. `IndexFile.add` with a `BaseIndexEntry` of mode `120000` and path `.gitmodules` was stored, `write_tree` serialized the tree, and `IndexFile.commit` wrote a commit Git refuses to read back and a server with `transfer.fsckObjects` set rejects. Coming the other way, `read_cache` and `tree_entries_from_data` accepted the same entry out of an untrusted repository's index or tree. `_validate_repo_path` now takes the entry mode and, for a symbolic link, rejects every spelling Git recognizes: `.gitmodules` with trailing spaces or periods, the HFS form with ignorable code points removed, and the NTFS short names `gitmod~1` through `gitmod~4` and `gi7eba~1` through `gi7eba~9`. The mode is passed at the boundaries that have one: `write_cache`, `read_cache`, `write_tree_from_cache`, `_tree_entry_to_baseindexentry`, `IndexFile._preprocess_add_items`, `IndexFile.add`, `tree_to_stream`, `tree_entries_from_data` and `TreeModifier.add`. Paths reached without a mode, such as the directories walked by `IndexFile._iter_expand_paths`, keep their previous behavior, and a regular file named `.gitmodules` stays valid. Checked against `git update-index --add --cacheinfo` on git 2.52.0 for modes `100644`, `120000`, `160000` and `40000` over the alias corpus: no path is left that Git rejects and GitPython accepts. Like the existing `.git` rule the name is tested per component, so a link below a directory spelled like one of those aliases is refused as well, which Git happens to allow. Adds regression tests in `test/test_index.py` and `test/test_tree.py`; `mypy`, `basedpyright --warnings` and `ruff` are clean.
Byron
force-pushed
the
gitmodules-symlink-entries
branch
from
October 9, 2026 03:40
1b2615c to
c99f923
Compare
Byron
force-pushed
the
gitmodules-symlink-entries
branch
from
October 9, 2026 04:15
c99f923 to
9f56080
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
_validate_repo_pathmirrors git'sverify_pathfor tree and index entry paths but only ever saw the path, and git's rule there is mode dependent: an entry that makes.gitmodulesa symbolic link is refused, since the submodule configuration would then be read through the link, from outside the repository. soIndexFile.addaccepted aBaseIndexEntryof mode120000named.gitmodules,write_treeserialized the tree, andIndexFile.commitwrote a commit thatgit read-treerefuses withinvalid pathandgit fsck --strictreports asgitmodulesSymlink, whileread_cacheandtree_entries_from_dataaccepted the same entry coming the other way, out of an untrusted repository. the entry mode is now passed down to_validate_repo_pathat every boundary that has one, and for a link it rejects each spelling git recognizes:.gitmoduleswith trailing spaces or periods, the hfs form with ignorable code points removed, and the ntfs short namesgitmod~1throughgitmod~4andgi7eba~1throughgi7eba~9. checked againstgit update-index --add --cacheinfoon git 2.52.0 for modes 100644, 120000, 160000 and 40000 over the alias corpus, leaving no path that git rejects and gitpython accepts; the name is tested per component like the existing.gitrule, so a link below a directory spelled like one of those aliases is refused too.Note: this change and description were prepared by an AI agent acting for this account's owner.