clone: when symbolic links collide with directories, keep the latter

When recursively cloning a repository with submodules, we must ensure
that the submodules paths do not suddenly contain symbolic links that
would let Git write into unintended locations. We just plugged that
vulnerability, but let's add some more defense-in-depth.

Since we can only keep one item on disk if multiple index entries' paths
collide, we may just as well avoid keeping a symbolic link (because that
would allow attack vectors where Git follows those links by mistake).

Technically, we handle more situations than cloning submodules into
paths that were (partially) replaced by symbolic links. This provides
defense-in-depth in case someone finds a case-folding confusion
vulnerability in the future that does not even involve submodules.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin
2024-03-28 10:55:07 +01:00
parent 850c3a220e
commit 31572dc420
3 changed files with 31 additions and 2 deletions

View File

@ -633,6 +633,21 @@ test_expect_success CASE_INSENSITIVE_FS 'colliding file detection' '
test_i18ngrep "the following paths have collided" icasefs/warning
'
test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
'colliding symlink/directory keeps directory' '
git init icasefs-colliding-symlink &&
(
cd icasefs-colliding-symlink &&
a=$(printf a | git hash-object -w --stdin) &&
printf "100644 %s 0\tA/dir/b\n120000 %s 0\ta\n" $a $a >idx &&
git update-index --index-info <idx &&
test_tick &&
git commit -m initial
) &&
git clone icasefs-colliding-symlink icasefs-colliding-symlink-clone &&
test_file_not_empty icasefs-colliding-symlink-clone/A/dir/b
'
test_expect_success 'clone with GIT_DEFAULT_HASH' '
(
sane_unset GIT_DEFAULT_HASH &&

View File

@ -1222,8 +1222,8 @@ test_expect_success CASE_INSENSITIVE_FS,SYMLINKS \
) &&
test_path_is_missing "$tell_tale_path" &&
test_must_fail git clone --recursive captain hooked 2>err &&
grep "directory not empty" err &&
git clone --recursive captain hooked 2>err &&
! grep HOOK-RUN err &&
test_path_is_missing "$tell_tale_path"
'