Skip to content

Commit cf8f6ce

Browse files
ttaylorrgitster
authored andcommitted
clone: delay picking a transport until after get_repo_path()
In the previous commit, t5619 demonstrates an issue where two calls to `get_repo_path()` could trick Git into using its local clone mechanism in conjunction with a non-local transport. That sequence is: - the starting state is that the local path https:/example.com/foo is a symlink that points to ../../../.git/modules/foo. So it's dangling. - get_repo_path() sees that no such path exists (because it's dangling), and thus we do not canonicalize it into an absolute path - because we're using --separate-git-dir, we create .git/modules/foo. Now our symlink is no longer dangling! - we pass the url to transport_get(), which sees it as an https URL. - we call get_repo_path() again, on the url. This second call was introduced by f38aa83 (use local cloning if insteadOf makes a local URL, 2014-07-17). The idea is that we want to pull the url fresh from the remote.c API, because it will apply any aliases. And of course now it sees that there is a local file, which is a mismatch with the transport we already selected. The issue in the above sequence is calling `transport_get()` before deciding whether or not the repository is indeed local, and not passing in an absolute path if it is local. This is reminiscent of a similar bug report in [1], where it was suggested to perform the `insteadOf` lookup earlier. Taking that approach may not be as straightforward, since the intent is to store the original URL in the config, but to actually fetch from the insteadOf one, so conflating the two early on is a non-starter. Note: we pass the path returned by `get_repo_path(remote->url[0])`, which should be the same as `repo_name` (aside from any `insteadOf` rewrites). We *could* pass `absolute_pathdup()` of the same argument, which 86521ac (Bring local clone's origin URL in line with that of a remote clone, 2008-09-01) indicates may differ depending on the presence of ".git/" for a non-bare repo. That matters for forming relative submodule paths, but doesn't matter for the second call, since we're just feeding it to the transport code, which is fine either way. [1]: https://lore.kernel.org/git/CAMoD=Bi41mB3QRn3JdZL-FGHs4w3C2jGpnJB-CqSndO7FMtfzA@mail.gmail.com/ Signed-off-by: Jeff King <[email protected]> Signed-off-by: Taylor Blau <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 58325b9 commit cf8f6ce

File tree

2 files changed

+15
-8
lines changed

2 files changed

+15
-8
lines changed

builtin/clone.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,10 +1201,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
12011201
refspec_appendf(&remote->fetch, "+%s*:%s*", src_ref_prefix,
12021202
branch_top.buf);
12031203

1204-
transport = transport_get(remote, remote->url[0]);
1205-
transport_set_verbosity(transport, option_verbosity, option_progress);
1206-
transport->family = family;
1207-
12081204
path = get_repo_path(remote->url[0], &is_bundle);
12091205
is_local = option_local != 0 && path && !is_bundle;
12101206
if (is_local) {
@@ -1224,6 +1220,10 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
12241220
}
12251221
if (option_local > 0 && !is_local)
12261222
warning(_("--local is ignored"));
1223+
1224+
transport = transport_get(remote, path ? path : remote->url[0]);
1225+
transport_set_verbosity(transport, option_verbosity, option_progress);
1226+
transport->family = family;
12271227
transport->cloning = 1;
12281228

12291229
transport_set_option(transport, TRANS_OPT_KEEP, "yes");

t/t5619-clone-local-ambiguous-transport.sh

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,18 @@ test_expect_success 'setup' '
5353
git -C "$REPO" update-server-info
5454
'
5555

56-
test_expect_failure 'ambiguous transport does not lead to arbitrary file-inclusion' '
56+
test_expect_success 'ambiguous transport does not lead to arbitrary file-inclusion' '
5757
git clone malicious clone &&
58-
git -C clone submodule update --init &&
59-
60-
test_path_is_missing clone/.git/modules/sub/objects/secret
58+
test_must_fail git -C clone submodule update --init 2>err &&
59+
60+
test_path_is_missing clone/.git/modules/sub/objects/secret &&
61+
# We would actually expect "transport .file. not allowed" here,
62+
# but due to quirks of the URL detection in Git, we mis-parse
63+
# the absolute path as a bogus URL and die before that step.
64+
#
65+
# This works for now, and if we ever fix the URL detection, it
66+
# is OK to change this to detect the transport error.
67+
grep "protocol .* is not supported" err
6168
'
6269

6370
test_done

0 commit comments

Comments
 (0)