| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
Also, sloppy review of the tests which are assumpted to not make things worse. <!-- agent --> Submodule.move() checked lexical containment but did not validate intermediate destination components before filesystem and repository updates. GHSA-gq48-pqfc-9p58 identifies the resulting checkout-path boundary violation. The new regression failed before the fix because move() returned successfully. Share the existing abspath component walk with move() and validate the normalized destination before any mutation, including configuration-only and module-only calls. Preserve the no-op early return and existing final-component symlink handling; abspath still rejects every symlink component. This addresses pre-existing links, not concurrent directory replacement races. The Git reference checkout at 1630431f326e15fcde608827b5ff38422528eb59 uses has_symlink_leading_path() in builtin/mv.c and tests rejection without index changes in t/t7001-mv.sh. The fix follows that intermediate-component rule while retaining GitPython leaf-link compatibility. Validation: the 30 new parameterized cases pass, covering relative and absolute destinations and link targets, internal and dangling links, all move flag combinations, unchanged repository state after rejection, ordinary and no-op moves, and leaf-link compatibility. The complete test/test_submodule.py suite passes: 75 passed, 3 skipped, 1 xfailed. Test-process commit.gpgsign=false avoids sandbox GPG failures. Ruff lint and format checks, mypy (45 source files), and git diff --check pass. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
A bit of a sloppy review, rubber-stamping the tests based on the assumption that they are validating it's conforming to Git, probably also while increasing coverage. <!-- agent --> Submodule.add() could clone through a checkout symlink after module_exists() swallowed the validation error. Metadata paths had a similar gap: locally planted symlinks under .git/modules could redirect cloning, reconnecting, renaming, updating, or removing a submodule. Some failures were detected only after changing configuration or moving or removing checkout directories. Reuse the checkout component check for metadata paths and validate checkout paths in the shared clone helper, including legacy embedded repositories. Check .gitfiles, submodule configuration files, and the actual repository path named by a gitfile, which can differ from .git/modules/<name>. Reject symlinked .gitmodules files as well. Preflight move and rename sources and destinations before mutation, including the implicit metadata rename when a default-named submodule moves. Keep module_exists()'s boolean contract and the existing supported replacement of a leaf symlink during a move. Add 56 regression cases covering checkout and metadata links, dangling links, redirected gitfiles, legacy clone layouts, and rejected operations preserving external targets, configuration, the index, and an empty move destination. The initial 36 cases reproduced failures before the fix. These checks reject existing symlinks; they do not prevent concurrent filesystem replacement between validation and use. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
There was a problem hiding this comment.
Two critical and two moderate findings remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overviewThis pull request hardens submodule operations against intermediate symlink traversal and adds regression coverage.
Changes:
| File | Summary and findings |
|---|---|
| test/test_submodule.py | Adds symlink-safety tests. Moderate findings: missing .gitmodules preflight in an initialized update() path (2 votes), and inconsistent dangling-final-symlink expectation (2 votes). |
| git/objects/submodule/base.py | Implements path validation. Critical finding: forced removal may continue after a symlink-related ValueError instead of rejecting before mutation (2 votes). |
| doc/source/changes.rst | Adds the 3.1.63 changelog entry. Critical finding: VERSION remains 3.1.62, breaking release validation and publication consistency (1 vote). |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Sorry, something went wrong.
There was a problem hiding this comment.
Unresolved findings can bypass validation or cause incorrect repository state changes.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review detailsgit/objects/submodule/base.py:1237
self._validated_name(self.name)
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:1103
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
test/test_submodule.py:168
with pytest.raises(OSError if kind == "dangling" else ValueError):
submodule.move("destination")
Sorry, something went wrong.
There was a problem hiding this comment.
Unresolved moderate findings affect update, forced removal, and dangling symlink handling.
Review detailsgit/objects/submodule/base.py:258
fp_module = cls._checked_abspath(repo.working_tree_dir, cls.k_modules_file)
git/objects/submodule/base.py:1239
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:1105
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
test/test_submodule.py:167
with pytest.raises(OSError if kind == "dangling" else ValueError):
test/test_submodule.py:234
if operation == "update":
sm.update()
Sorry, something went wrong.
A quick rubber-stamp, admittedly. V4 will probably review all tests and make it more proper, if there can be such a thing in python anyway. <!-- agent --> Both Windows failures came from os.renames() pruning a directory symlink above the source after the rename succeeded. Unlike POSIX, Windows rmdir() can remove a directory symlink even when its target is nonempty. Moving a checkout through a worktree alias therefore deleted the alias and broke configuration updates and rollback. Renaming metadata through a linked .git/modules directory deleted that link and broke config.lock creation. Route checkout moves, rollback, and metadata renames through one helper. It creates destination parents and renames the source, then prunes empty source parents only until it reaches a symlink or a directory it cannot remove. This keeps ordinary empty-directory cleanup while preserving parent links and their targets, including targets that become empty. Leaf symlinks continue to move as links rather than moving their targets. Exercise Windows directory-symlink removal semantics on POSIX in the existing compatibility tests, and use native behavior on Windows. Both reported failures reproduced locally before the fix. Strengthen assertions that worktree and metadata parent aliases survive, their targets remain directories, and leaf metadata symlinks move without moving their targets. A further Windows run exposed a separate sharing violation during the checkout move. Submodule.add() read HEAD through its temporary Repo but left that Repo's persistent cat-file processes open. Those processes can hold the checkout as their current directory and prevent its rename. Close the owned Repo with a context manager when reading HEAD, including on read failure, instead of waiting for garbage collection. Add a regression that observes the real cat-file processes started for the new checkout and requires them to have exited before add() returns. It failed before the fix and now passes, along with the immediate move. The remaining metadata failures also reproduce with Python 3.7's Windows path semantics: ntpath.realpath is an alias of abspath and does not resolve symlinks. Relative core.worktree values were calculated from the metadata alias instead of the repository directory Git actually opens. This broke add/reconnect HEAD reads and made moves and renames point at nonexistent worktrees. The SHA/dubious-ownership message was a secondary read failure. Use pathlib.Path.resolve(), which resolves Windows symlinks on Python 3.7, for both endpoints of gitfile/config rewrites and for metadata removal. Run metadata and worktree-alias tests with native and simulated Windows 3.7 realpath behavior. The simulation reproduced all eight reported failures plus a leaf-symlink removal failure before this change. Reference: https://github.com/python/cpython/blob/3.7/Lib/ntpath.py and https://github.com/python/cpython/blob/3.7/Lib/pathlib.py. Validation: 183 passed, 3 skipped, and 1 expected failure across the submodule and diff suites plus the commit-message hook success test on macOS. Ruff lint and formatting, mypy for the changed module, and git diff --check passed. Native Windows validation remains for CI. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
There was a problem hiding this comment.
Three moderate review findings remain unresolved.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review detailsgit/objects/submodule/base.py:1241
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
test/test_submodule.py:234
with pytest.raises(ValueError, match="contains a symbolic link"):
if operation == "update":
sm.update()
Sorry, something went wrong.
There was a problem hiding this comment.
Unresolved review findings remain in path validation, metadata cleanup, and regression coverage.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review detailsgit/objects/submodule/base.py:1241
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:1107
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
test/test_submodule.py:235
with pytest.raises(ValueError, match="contains a symbolic link"):
if operation == "update":
sm.update()
test/test_submodule.py:169
with pytest.raises(OSError if kind == "dangling" else ValueError):
submodule.move("destination")
Sorry, something went wrong.
rubber stamp <!-- agent --> The Python 3.7 Windows path simulation wrapped the entire os.path module in Mock. On Windows, all six simulated removal cases hit sharing violations that the default error handling converted into skipped tests. Use a SimpleNamespace copy of the path module and replace only realpath with abspath. Patch only the submodule module's osp binding so pathlib keeps its own resolver and other path operations remain ordinary calls. This retains Python 3.7 compatibility and allows the removal cases to run successfully without Windows permission-error suppression. Before removing a submodule, resolve and verify its metadata directory, then assert that removal deletes it as well as the checkout. Checking only the checkout could miss metadata left behind through a directory symlink. Validation: all 57 focused submodule compatibility and process-cleanup tests passed on Windows with Python 3.10 and HIDE_WINDOWS_KNOWN_ERRORS=0. Ruff 0.16.5 lint and formatting checks and git diff --check passed. Native Python 3.7 was unavailable; its realpath behavior is simulated. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
There was a problem hiding this comment.
Two moderate findings remain regarding dangling destination symlinks and an ineffective gitmodules update test case.
Review detailsgit/objects/submodule/base.py:1107
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
test/test_submodule.py:233
with pytest.raises(ValueError, match="contains a symbolic link"):
Sorry, something went wrong.
There was a problem hiding this comment.
Three unresolved moderate findings affect symlink validation and expected test behavior.
Review detailsgit/objects/submodule/base.py:1107
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
git/objects/submodule/base.py:1247
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:259
fp_module = cls._checked_abspath(repo.working_tree_dir, cls.k_modules_file)
Sorry, something went wrong.
<!-- agent --> Ubuntu CI reported FileNotFoundError for maintenance.lock while running test_timeout_funcs. The test performs normal pull/fetch calls before its forced timeouts, and Git can launch detached automatic maintenance from those operations. Maintenance can then race with the fixture's recursive removal of the temporary repository, removing a lock file after cleanup has enumerated it. Disable maintenance.auto in this test's temporary repository and set gc.auto to zero for older Git versions that use automatic garbage collection. This removes background housekeeping unrelated to the timeout assertions without weakening repository cleanup or changing library behavior. Use mock.patch.object for the global forced termination status so an assertion failure cannot leak the override into subsequent tests. Validation: Git Trace2 recorded three detached maintenance launches in the original test and none with the fix. The timeout test then passed 20 consecutive runs locally on macOS. Ruff lint and formatting checks and git diff --check passed. The original Ubuntu cleanup exception was not reproduced locally; tracing verified removal of the suspected race source. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
There was a problem hiding this comment.
Unresolved implementation and regression-test issues affect symlink safety and side-effect-free behavior.
Review detailsPreviously missed (1) — in code that hasn't changed since the last review.
test/test_submodule.py:322
git/objects/submodule/base.py:1107
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
git/objects/submodule/base.py:1247
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
test/test_submodule.py:235
sm.update()
test/test_submodule.py:241
sm.remove(force=True)
test/test_submodule.py:308
with git.Repo(alias) as parent:
added = Submodule.add(parent, "new", "new", sm.url)
test/test_submodule.py:220
path = root / (".gitmodules" if link_kind == "gitmodules" else "module")
Sorry, something went wrong.
There was a problem hiding this comment.
Moderate unresolved issues affect update, removal, and dangling-symlink handling.
Review detailsgit/objects/submodule/base.py:259
fp_module = cls._checked_abspath(repo.working_tree_dir, cls.k_modules_file)
git/objects/submodule/base.py:1259
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:1119
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
test/test_submodule.py:169
with pytest.raises(OSError if kind == "dangling" else ValueError):
submodule.move("destination")
test/test_submodule.py:235
with pytest.raises(ValueError, match="contains a symbolic link"):
if operation == "update":
sm.update()
Sorry, something went wrong.
rubber stamp <!-- agent --> Removing a submodule retains its metadata alias but deletes the target. Adding the same submodule again then passes a dangling directory symlink to git clone --separate-git-dir. Git for Windows fails while copying its template files through that alias. Both native-realpath and simulated Windows 3.7 remove-leaf cases reproduced this failure locally. When the metadata destination is a leaf symlink, pass its target to Git and leave the alias intact. Resolve relative targets against the link's parent, create missing target parents through the existing clone setup, and let Git create the repository directory itself. Precreating that directory is insufficient because Git rejects an existing separate git repository destination. Read the link explicitly because Python 3.7 on Windows cannot resolve a dangling link with Path.resolve(). Normalize the Windows namespace prefix returned by newer os.readlink implementations, including UNC targets, and use forward slashes before passing the path through Git's URL logic. The shared clone helper covers add() and initialization through update(). Add regression coverage for direct cloning through absolute and relative dangling metadata links with missing target parents. Verify that the link and its stored target are retained, metadata is created at the target, and the resulting checkout works under both realpath modes. Validation: 61 focused tests passed on Windows/Python 3.10 with HIDE_WINDOWS_KNOWN_ERRORS=0, including both reported failures. Ruff 0.16.5 lint and formatting checks and git diff --check passed. Native Python 3.7 and UNC network shares were not available for execution. A broader run also exposed sharing violations in sibling-reinitialization tests before cloning; a representative case also failed with the unchanged HEAD clone helper loaded in memory. That separate removal issue is not changed here. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
rubber stamp, just get this through CI OMG <!-- agent --> The dangling-metadata regression passed pathlib.Path objects to os.readlink() when capturing and checking the symlink target. Windows Python 3.7 requires a string argument, so all four parameter combinations failed with TypeError before exercising the clone fix. Convert the path to str at both calls. The production clone helper already passes a string and needs no change. Keep the target-preservation assertions and the absolute/relative and realpath-mode coverage intact. Validation: reproduced all four TypeErrors on Windows/Python 3.10 with an in-memory readlink wrapper enforcing the Python 3.7 string requirement. After the conversions, all four cases passed with the same wrapper and Windows permission-error suppression disabled. Ruff 0.16.5 lint and formatting checks and git diff --check passed. Native Python 3.7 was not available locally. Assisted-by: GPT 6.0 Co-authored-by: GPT 6.0 <codex@openai.com>
There was a problem hiding this comment.
Unresolved moderate findings affect dangling symlink handling, forced removal, and update validation.
Review detailsgit/objects/submodule/base.py:1119
module_checkout_abspath = self._checkout_abspath(module_checkout_path, allow_final_symlink=True)
git/objects/submodule/base.py:1259
if configuration:
self._checked_abspath(self.repo.working_tree_dir, self.k_modules_file)
git/objects/submodule/base.py:259
fp_module = cls._checked_abspath(repo.working_tree_dir, cls.k_modules_file)
test/test_submodule.py:235
with pytest.raises(ValueError, match="contains a symbolic link"):
if operation == "update":
sm.update()
test/test_submodule.py:169
with pytest.raises(OSError if kind == "dangling" else ValueError):
submodule.move("destination")
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Tasks
This section is for Byron only. Models continuing this PR must not add, remove, check, uncheck, rename, or reorder checkboxes here.
Everything below this line was generated by Codex GPT-6.
Created by Codex on behalf of Byron. Byron will review before this is ready to merge.
Submodule moves now validate intermediate destination components before changing the checkout, index, or configuration. The check shares the existing checkout-path validation and preserves ordinary moves, no-op moves, and final-component symlink behavior.
Advisory summary
GHSA-gq48-pqfc-9p58: medium severity; GitPython (pip), reported affected version = 3.1.62. No patched version or CVE is currently assigned. Reproducer details are omitted from this description.
Validation
This check addresses existing symlinks; concurrent directory replacement remains outside its scope.
Windows CI exposed open clone handles in the normal-move test fixture. Follow-up d7efa37d closes the fixture module repository before yielding it, using the existing Windows cleanup in Repo.close(). All 30 focused cases pass locally after this test-only adjustment.