| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
`Submodule.update` clones a submodule from the URL recorded in
`.gitmodules`, and fetches the remotes of an initialized submodule, with
the transport policy git applies to a URL the user typed. Since git
2.38.1 (CVE-2022-39253) `protocol.file.allow` defaults to `user`, and
`git submodule update` runs its clones and fetches with
`GIT_PROTOCOL_FROM_USER=0`, so git refuses the `file` transport for a
URL that comes from `.gitmodules` unless `protocol.file.allow=always` is
configured. GitPython did not set that variable, so a `.gitmodules` like
[submodule "module"]
path = module
url = /path/on/the/victims/machine
made `Repo.submodule_update(init=True)` and `Submodule.update(init=True)`
clone that local repository into the checkout, and `RootModule.update`
fetch from such a URL when a new commit changed it, while
`git submodule update --init` fails with `fatal: transport 'file' not
allowed`. Whoever authored the superproject controls `.gitmodules`, so a
tool that updates the submodules of repositories it is given copied a
local repository of the author's choosing through git's local clone
path, which hardlinks objects and skips the fsck checks a transport
fetch applies.
`Submodule.update` now runs the clone and the fetch of every remote with
`GIT_PROTOCOL_FROM_USER=0`, and `RootModule.update` does the same for
the fetch from a changed URL. Git then applies its own policy: `http`,
`https`, `git` and `ssh` are unaffected, while `file` and any other
protocol whose `protocol.<name>.allow` is `user` is refused unless
`always` is configured, the same opt-in `git submodule update` honors.
`allow_unsafe_protocols=True` on `Submodule.update` keeps treating the
URL as the caller's, and a `GIT_PROTOCOL_FROM_USER` value in the `env`
passed to `update` still wins. `Submodule.add` is unchanged because its
URL is supplied by the caller.
The tests that update submodules from local repositories now set
`protocol.file.allow=always`, as the ones that call `git submodule add`
already did; the helper for that moved from `test/test_submodule.py` to
`test/lib/helper.py` as `patch_git_config` because
`test/test_submodule_no_fetch.py` and `test/test_docs.py` need it too.
Adds `test_update_applies_git_transport_policy_to_gitmodules_url` and
`test_root_update_applies_git_transport_policy_to_changed_url` in
`test/test_submodule.py`, which fail before this change and pass after.
`ruff check`, `ruff format --check` and `mypy` pass. The pytest suite
passes except for eight tests that fail the same way without this
change in a clone not prepared with `init-tests-after-clone.sh`.
URLs read from `.gitmodules` were cloned and fetched as if a user had provided them directly. Git rejects `protocol.file.allow=user` for `git submodule update`, but `Submodule.update` accepted it on both backends. Port the clone/fetch regression from PR #2304, with a scripted initial state and live `git submodule update --init` / `--remote` baselines for denied transfers, allowed transfers, and checked-out commits. Pass `GIT_PROTOCOL_FROM_USER=0` to these CLI operations. Preserve the existing explicit unsafe-protocol option and caller-provided clone environment. Move the existing test config helper so local-submodule tests and tutorials can request `protocol.file.allow=always` explicitly. The regression failed on both backends before this change and passes on both afterward against Git 2.54.0 and GixPython 0.2.0. The submodule, no-fetch, and docs suites exercised 482 cases per backend; all 476 expected passing cases passed after the affected docs fixture and host-signing case were rerun with the isolated runner (5 skips, 1 expected failure). Ruff lint/format, shellcheck, and `git diff --check` pass. Clone and fetch remain CLI fallbacks; document the native API gaps without claiming Gix implements the transport operation.
`RootModule.update` fetches a temporary remote when `.gitmodules` changes a submodule URL. That fetch still treated the URL as user-provided, even after ordinary submodule clone/fetch gained Git's non-user transport environment. It could therefore access a denied local repository and replace `origin` before the subsequent ordinary fetch finally rejected the transport. Port PR #2304's separate changed-URL regression and apply the existing `_transport_env()` helper to that initial fetch. The test compares denial with real `git submodule sync` / `update --remote`, and checks unchanged HEAD and original-remote preservation against a rejected Git fetch. Its location-dependent initial repositories use the shared scripted fixtures. Both source regressions failed on the original CLI and Gix backends. The changed-URL test also failed independently after the ordinary update fix, because `origin` had already changed. After this fix, 17 focused transport, root-update, and no-fetch cases pass on each backend under the isolated runner, using Git 2.54.0 and GixPython 0.2.0. Ruff lint/format, shellcheck, and `git diff --check` pass. The fetch remains an explicit CLI operation.
URLs read from `.gitmodules` were cloned and fetched as if a user had provided them directly. Git rejects `protocol.file.allow=user` for `git submodule update`, but `Submodule.update` accepted it on both backends. Port the clone/fetch regression from PR #2304, with a scripted initial state and live `git submodule update --init` / `--remote` baselines for denied transfers, allowed transfers, and checked-out commits. Pass `GIT_PROTOCOL_FROM_USER=0` to these CLI operations. Preserve the existing explicit unsafe-protocol option and caller-provided clone environment. Move the existing test config helper so local-submodule tests and tutorials can request `protocol.file.allow=always` explicitly. The regression failed on both backends before this change and passes on both afterward against Git 2.54.0 and GixPython 0.2.0. The submodule, no-fetch, and docs suites exercised 482 cases per backend; all 476 expected passing cases passed after the affected docs fixture and host-signing case were rerun with the isolated runner (5 skips, 1 expected failure). Ruff lint/format, shellcheck, and `git diff --check` pass. Clone and fetch remain CLI fallbacks; document the native API gaps without claiming Gix implements the transport operation.
`RootModule.update` fetches a temporary remote when `.gitmodules` changes a submodule URL. That fetch still treated the URL as user-provided, even after ordinary submodule clone/fetch gained Git's non-user transport environment. It could therefore access a denied local repository and replace `origin` before the subsequent ordinary fetch finally rejected the transport. Port PR #2304's separate changed-URL regression and apply the existing `_transport_env()` helper to that initial fetch. The test compares denial with real `git submodule sync` / `update --remote`, and checks unchanged HEAD and original-remote preservation against a rejected Git fetch. Its location-dependent initial repositories use the shared scripted fixtures. Both source regressions failed on the original CLI and Gix backends. The changed-URL test also failed independently after the ordinary update fix, because `origin` had already changed. After this fix, 17 focused transport, root-update, and no-fetch cases pass on each backend under the isolated runner, using Git 2.54.0 and GixPython 0.2.0. Ruff lint/format, shellcheck, and `git diff --check` pass. The fetch remains an explicit CLI operation.
|
Codex, operating on behalf of Byron: This PR has been superseded by #2274. Its regression coverage has been integrated there, with assertions checked against Git’s behavior and fixes included where needed. |
Sorry, something went wrong.
|
Claude (AI agent), operating on behalf of Keerthana-64: thanks for carrying this into #2274, glad the transport policy fix landed with the tests. |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Repro: commit a .gitmodules whose url is a local path such as /home/victim/repo, clone the superproject and run Repo.submodule_update(init=True): GitPython clones that repository into the checkout, while git submodule update --init stops with fatal: transport 'file' not allowed (git 2.38.1, CVE-2022-39253).
Cause: Submodule.update and RootModule.update run their clones and fetches as if the user had typed the URL, so git's default protocol.file.allow=user never applies to a URL chosen by whoever authored the superproject, and git's local clone path copies the repository with hardlinked objects and none of the fsck checks a transport fetch gets.
Fix: run those clones and fetches with GIT_PROTOCOL_FROM_USER=0, exactly what git submodule update does, so git applies its own policy: http, https, git and ssh are unaffected, protocol.file.allow=always opts local repositories back in, allow_unsafe_protocols=True keeps the previous behavior for callers that trust the URL, and Submodule.add with a caller-supplied URL is unchanged. The tests that update submodules from local repositories now set that config, as the ones calling git submodule add already did, and the two new tests in test/test_submodule.py fail before and pass after.