mirror of
https://github.com/git/git
synced 2024-10-02 14:45:21 +00:00
1204e1a824
When performing a local clone of a repository we end up either copying or hardlinking the source repository into the target repository. This is significantly more performant than if we were to use git-upload-pack(1) and git-fetch-pack(1) to create the new repository and preserves both disk space and compute time. Unfortunately though, performing such a local clone of a repository that is not owned by the current user is inherently unsafe: - It is possible that source files get swapped out underneath us while we are copying or hardlinking them. While we do perform some checks here to assert that we hardlinked the expected file, they cannot reliably thwart time-of-check-time-of-use (TOCTOU) style races. It is thus possible for an adversary to make us copy or hardlink unexpected files into the target directory. Ideally, we would address this by starting to use openat(3P), fstatat(3P) and friends. Due to platform compatibility with Windows we cannot easily do that though. Furthermore, the scope of these fixes would likely be quite broad and thus not fit for an embargoed security release. - Even if we handled TOCTOU-style races perfectly, hardlinking files owned by a different user into the target repository is not a good idea in general. It is possible for an adversary to rewrite those files to contain whatever data they want even after the clone has completed. Address these issues by completely refusing local clones of a repository that is not owned by the current user. This reuses our existing infra we have in place via `ensure_valid_ownership()` and thus allows a user to override the safety guard by adding the source repository path to the "safe.directory" configuration. This addresses CVE-2024-32020. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
108 lines
2.6 KiB
Bash
Executable file
108 lines
2.6 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
test_description='verify safe.directory checks'
|
|
|
|
TEST_PASSES_SANITIZE_LEAK=true
|
|
. ./test-lib.sh
|
|
|
|
GIT_TEST_ASSUME_DIFFERENT_OWNER=1
|
|
export GIT_TEST_ASSUME_DIFFERENT_OWNER
|
|
|
|
expect_rejected_dir () {
|
|
test_must_fail git status 2>err &&
|
|
grep "dubious ownership" err
|
|
}
|
|
|
|
test_expect_success 'safe.directory is not set' '
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'safe.directory on the command line' '
|
|
git -c safe.directory="$(pwd)" status
|
|
'
|
|
|
|
test_expect_success 'safe.directory in the environment' '
|
|
env GIT_CONFIG_COUNT=1 \
|
|
GIT_CONFIG_KEY_0="safe.directory" \
|
|
GIT_CONFIG_VALUE_0="$(pwd)" \
|
|
git status
|
|
'
|
|
|
|
test_expect_success 'safe.directory in GIT_CONFIG_PARAMETERS' '
|
|
env GIT_CONFIG_PARAMETERS="${SQ}safe.directory${SQ}=${SQ}$(pwd)${SQ}" \
|
|
git status
|
|
'
|
|
|
|
test_expect_success 'ignoring safe.directory in repo config' '
|
|
(
|
|
unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
|
|
git config safe.directory "$(pwd)"
|
|
) &&
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'safe.directory does not match' '
|
|
git config --global safe.directory bogus &&
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'path exist as different key' '
|
|
git config --global foo.bar "$(pwd)" &&
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'safe.directory matches' '
|
|
git config --global --add safe.directory "$(pwd)" &&
|
|
git status
|
|
'
|
|
|
|
test_expect_success 'safe.directory matches, but is reset' '
|
|
git config --global --add safe.directory "" &&
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'safe.directory=*' '
|
|
git config --global --add safe.directory "*" &&
|
|
git status
|
|
'
|
|
|
|
test_expect_success 'safe.directory=*, but is reset' '
|
|
git config --global --add safe.directory "" &&
|
|
expect_rejected_dir
|
|
'
|
|
|
|
test_expect_success 'safe.directory in included file' '
|
|
cat >gitconfig-include <<-EOF &&
|
|
[safe]
|
|
directory = "$(pwd)"
|
|
EOF
|
|
git config --global --add include.path "$(pwd)/gitconfig-include" &&
|
|
git status
|
|
'
|
|
|
|
test_expect_success 'local clone of unowned repo refused in unsafe directory' '
|
|
test_when_finished "rm -rf source" &&
|
|
git init source &&
|
|
(
|
|
sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
|
|
test_commit -C source initial
|
|
) &&
|
|
test_must_fail git clone --local source target &&
|
|
test_path_is_missing target
|
|
'
|
|
|
|
test_expect_success 'local clone of unowned repo accepted in safe directory' '
|
|
test_when_finished "rm -rf source" &&
|
|
git init source &&
|
|
(
|
|
sane_unset GIT_TEST_ASSUME_DIFFERENT_OWNER &&
|
|
test_commit -C source initial
|
|
) &&
|
|
test_must_fail git clone --local source target &&
|
|
git config --global --add safe.directory "$(pwd)/source/.git" &&
|
|
git clone --local source target &&
|
|
test_path_is_dir target
|
|
'
|
|
|
|
test_done
|