d2c84dad1c
When creating a directory on Windows whose path ends in a space or a period (or chains thereof), the Win32 API "helpfully" trims those. For example, `mkdir("abc ");` will return success, but actually create a directory called `abc` instead. This stems back to the DOS days, when all file names had exactly 8 characters plus exactly 3 characters for the file extension, and the only way to have shorter names was by padding with spaces. Sadly, this "helpful" behavior is a bit inconsistent: after a successful `mkdir("abc ");`, a `mkdir("abc /def")` will actually _fail_ (because the directory `abc ` does not actually exist). Even if it would work, we now have a serious problem because a Git repository could contain directories `abc` and `abc `, and on Windows, they would be "merged" unintentionally. As these paths are illegal on Windows, anyway, let's disallow any accesses to such paths on that Operating System. For practical reasons, this behavior is still guarded by the config setting `core.protectNTFS`: it is possible (and at least two regression tests make use of it) to create commits without involving the worktree. In such a scenario, it is of course possible -- even on Windows -- to create such file names. Among other consequences, this patch disallows submodules' paths to end in spaces on Windows (which would formerly have confused Git enough to try to write into incorrect paths, anyway). While this patch does not fix a vulnerability on its own, it prevents an attack vector that was exploited in demonstrations of a number of recently-fixed security bugs. The regression test added to `t/t7417-submodule-path-url.sh` reflects that attack vector. Note that we have to adjust the test case "prevent git~1 squatting on Windows" in `t/t7415-submodule-names.sh` because of a very subtle issue. It tries to clone two submodules whose names differ only in a trailing period character, and as a consequence their git directories differ in the same way. Previously, when Git tried to clone the second submodule, it thought that the git directory already existed (because on Windows, when you create a directory with the name `b.` it actually creates `b`), but with this patch, the first submodule's clone will fail because of the illegal name of the git directory. Therefore, when cloning the second submodule, Git will take a different code path: a fresh clone (without an existing git directory). Both code paths fail to clone the second submodule, both because the the corresponding worktree directory exists and is not empty, but the error messages are worded differently. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
110 lines
2.9 KiB
Bash
Executable File
110 lines
2.9 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
test_description='check handling of .. in submodule names
|
|
|
|
Exercise the name-checking function on a variety of names, and then give a
|
|
real-world setup that confirms we catch this in practice.
|
|
'
|
|
. ./test-lib.sh
|
|
|
|
test_expect_success 'check names' '
|
|
cat >expect <<-\EOF &&
|
|
valid
|
|
valid/with/paths
|
|
EOF
|
|
|
|
git submodule--helper check-name >actual <<-\EOF &&
|
|
valid
|
|
valid/with/paths
|
|
|
|
../foo
|
|
/../foo
|
|
..\foo
|
|
\..\foo
|
|
foo/..
|
|
foo/../
|
|
foo\..
|
|
foo\..\
|
|
foo/../bar
|
|
EOF
|
|
|
|
test_cmp expect actual
|
|
'
|
|
|
|
test_expect_success 'create innocent subrepo' '
|
|
git init innocent &&
|
|
git -C innocent commit --allow-empty -m foo
|
|
'
|
|
|
|
test_expect_success 'submodule add refuses invalid names' '
|
|
test_must_fail \
|
|
git submodule add --name ../../modules/evil "$PWD/innocent" evil
|
|
'
|
|
|
|
test_expect_success 'add evil submodule' '
|
|
git submodule add "$PWD/innocent" evil &&
|
|
|
|
mkdir modules &&
|
|
cp -r .git/modules/evil modules &&
|
|
write_script modules/evil/hooks/post-checkout <<-\EOF &&
|
|
echo >&2 "RUNNING POST CHECKOUT"
|
|
EOF
|
|
|
|
git config -f .gitmodules submodule.evil.update checkout &&
|
|
git config -f .gitmodules --rename-section \
|
|
submodule.evil submodule.../../modules/evil &&
|
|
git add modules &&
|
|
git commit -am evil
|
|
'
|
|
|
|
# This step seems like it shouldn't be necessary, since the payload is
|
|
# contained entirely in the evil submodule. But due to the vagaries of the
|
|
# submodule code, checking out the evil module will fail unless ".git/modules"
|
|
# exists. Adding another submodule (with a name that sorts before "evil") is an
|
|
# easy way to make sure this is the case in the victim clone.
|
|
test_expect_success 'add other submodule' '
|
|
git submodule add "$PWD/innocent" another-module &&
|
|
git add another-module &&
|
|
git commit -am another
|
|
'
|
|
|
|
test_expect_success 'clone evil superproject' '
|
|
git clone --recurse-submodules . victim >output 2>&1 &&
|
|
! grep "RUNNING POST CHECKOUT" output
|
|
'
|
|
|
|
test_expect_success MINGW 'prevent git~1 squatting on Windows' '
|
|
git init squatting &&
|
|
(
|
|
cd squatting &&
|
|
mkdir a &&
|
|
touch a/..git &&
|
|
git add a/..git &&
|
|
test_tick &&
|
|
git commit -m initial &&
|
|
|
|
modules="$(test_write_lines \
|
|
"[submodule \"b.\"]" "url = ." "path = c" \
|
|
"[submodule \"b\"]" "url = ." "path = d\\\\a" |
|
|
git hash-object -w --stdin)" &&
|
|
rev="$(git rev-parse --verify HEAD)" &&
|
|
hash="$(echo x | git hash-object -w --stdin)" &&
|
|
git -c core.protectNTFS=false update-index --add \
|
|
--cacheinfo 100644,$modules,.gitmodules \
|
|
--cacheinfo 160000,$rev,c \
|
|
--cacheinfo 160000,$rev,d\\a \
|
|
--cacheinfo 100644,$hash,d./a/x \
|
|
--cacheinfo 100644,$hash,d./a/..git &&
|
|
test_tick &&
|
|
git -c core.protectNTFS=false commit -m "module" &&
|
|
test_must_fail git show HEAD: 2>err &&
|
|
test_i18ngrep backslash err
|
|
) &&
|
|
test_must_fail git -c core.protectNTFS=false \
|
|
clone --recurse-submodules squatting squatting-clone 2>err &&
|
|
test_i18ngrep -e "directory not empty" -e "not an empty directory" err &&
|
|
! grep gitdir squatting-clone/d/a/git~2
|
|
'
|
|
|
|
test_done
|