From e0d201b61601e17e24ed00cc3d16e8e25ca68596 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 29 Jan 2015 12:41:22 -0800 Subject: apply: do not touch a file beyond a symbolic link Because Git tracks symbolic links as symbolic links, a path that has a symbolic link in its leading part (e.g. path/to/dir/file, where path/to/dir is a symbolic link to somewhere else, be it inside or outside the working tree) can never appear in a patch that validly applies, unless the same patch first removes the symbolic link to allow a directory to be created there. Detect and reject such a patch. Things to note: - Unfortunately, we cannot reuse the has_symlink_leading_path() from dir.c, as that is only about the working tree, but "git apply" can be told to apply the patch only to the index or to both the index and to the working tree. - We cannot directly use has_symlink_leading_path() even when we are applying only to the working tree, as an early patch of a valid input may remove a symbolic link path/to/dir and then a later patch of the input may create a path path/to/dir/file, but "git apply" first checks the input without touching either the index or the working tree. The leading symbolic link check must be done on the interim result we compute in-core (i.e. after the first patch, there is no path/to/dir symbolic link and it is perfectly valid to create path/to/dir/file). Similarly, when an input creates a symbolic link path/to/dir and then creates a file path/to/dir/file, we need to flag it as an error without actually creating path/to/dir symbolic link in the filesystem. Instead, for any patch in the input that leaves a path (i.e. a non deletion) in the result, we check all leading paths against the resulting tree that the patch would create by inspecting all the patches in the input and then the target of patch application (either the index or the working tree). This way, we catch a mischief or a mistake to add a symbolic link path/to/dir and a file path/to/dir/file at the same time, while allowing a valid patch that removes a symbolic link path/to/dir and then adds a file path/to/dir/file. Signed-off-by: Junio C Hamano --- t/t4122-apply-symlink-inside.sh | 87 +++++++++++++++++++++++++++++++++++++++++ t/t4139-apply-escape.sh | 8 ++-- 2 files changed, 91 insertions(+), 4 deletions(-) (limited to 't') diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh index 035c080581..1779c0a568 100755 --- a/t/t4122-apply-symlink-inside.sh +++ b/t/t4122-apply-symlink-inside.sh @@ -71,4 +71,91 @@ test_expect_success SYMLINKS 'do not read from beyond symbolic link' ' ' +test_expect_success SYMLINKS 'do not follow symbolic link (setup)' ' + + rm -rf arch/i386/dir arch/x86_64/dir && + git reset --hard && + ln -s ../i386/dir arch/x86_64/dir && + git add arch/x86_64/dir && + git diff HEAD >add_symlink.patch && + git reset --hard && + + mkdir arch/x86_64/dir && + >arch/x86_64/dir/file && + git add arch/x86_64/dir/file && + git diff HEAD >add_file.patch && + git diff -R HEAD >del_file.patch && + git reset --hard && + rm -fr arch/x86_64/dir && + + cat add_symlink.patch add_file.patch >patch && + cat add_symlink.patch del_file.patch >tricky_del && + + mkdir arch/i386/dir +' + +test_expect_success SYMLINKS 'do not follow symbolic link (same input)' ' + + # same input creates a confusing symbolic link + test_must_fail git apply patch 2>error-wt && + test_i18ngrep "beyond a symbolic link" error-wt && + test_path_is_missing arch/x86_64/dir && + test_path_is_missing arch/i386/dir/file && + + test_must_fail git apply --index patch 2>error-ix && + test_i18ngrep "beyond a symbolic link" error-ix && + test_path_is_missing arch/x86_64/dir && + test_path_is_missing arch/i386/dir/file && + test_must_fail git ls-files --error-unmatch arch/x86_64/dir && + test_must_fail git ls-files --error-unmatch arch/i386/dir && + + test_must_fail git apply --cached patch 2>error-ct && + test_i18ngrep "beyond a symbolic link" error-ct && + test_must_fail git ls-files --error-unmatch arch/x86_64/dir && + test_must_fail git ls-files --error-unmatch arch/i386/dir && + + >arch/i386/dir/file && + git add arch/i386/dir/file && + + test_must_fail git apply tricky_del && + test_path_is_file arch/i386/dir/file && + + test_must_fail git apply --index tricky_del && + test_path_is_file arch/i386/dir/file && + test_must_fail git ls-files --error-unmatch arch/x86_64/dir && + git ls-files --error-unmatch arch/i386/dir && + + test_must_fail git apply --cached tricky_del && + test_must_fail git ls-files --error-unmatch arch/x86_64/dir && + git ls-files --error-unmatch arch/i386/dir +' + +test_expect_success SYMLINKS 'do not follow symbolic link (existing)' ' + + # existing symbolic link + git reset --hard && + ln -s ../i386/dir arch/x86_64/dir && + git add arch/x86_64/dir && + + test_must_fail git apply add_file.patch 2>error-wt-add && + test_i18ngrep "beyond a symbolic link" error-wt-add && + test_path_is_missing arch/i386/dir/file && + + mkdir arch/i386/dir && + >arch/i386/dir/file && + test_must_fail git apply del_file.patch 2>error-wt-del && + test_i18ngrep "beyond a symbolic link" error-wt-del && + test_path_is_file arch/i386/dir/file && + rm arch/i386/dir/file && + + test_must_fail git apply --index add_file.patch 2>error-ix-add && + test_i18ngrep "beyond a symbolic link" error-ix-add && + test_path_is_missing arch/i386/dir/file && + test_must_fail git ls-files --error-unmatch arch/i386/dir && + + test_must_fail git apply --cached add_file.patch 2>error-ct-file && + test_i18ngrep "beyond a symbolic link" error-ct-file && + test_must_fail git ls-files --error-unmatch arch/i386/dir +' + test_done diff --git a/t/t4139-apply-escape.sh b/t/t4139-apply-escape.sh index 02b97b3c30..45b5660a47 100755 --- a/t/t4139-apply-escape.sh +++ b/t/t4139-apply-escape.sh @@ -98,7 +98,7 @@ test_expect_success 'cannot delete file containing .. (index)' ' test_path_is_file ../foo ' -test_expect_failure SYMLINKS 'symlink escape via ..' ' +test_expect_success SYMLINKS 'symlink escape via ..' ' { mkpatch_symlink tmp .. && mkpatch_add tmp/foo ../foo @@ -108,7 +108,7 @@ test_expect_failure SYMLINKS 'symlink escape via ..' ' test_path_is_missing ../foo ' -test_expect_failure SYMLINKS 'symlink escape via .. (index)' ' +test_expect_success SYMLINKS 'symlink escape via .. (index)' ' { mkpatch_symlink tmp .. && mkpatch_add tmp/foo ../foo @@ -118,7 +118,7 @@ test_expect_failure SYMLINKS 'symlink escape via .. (index)' ' test_path_is_missing ../foo ' -test_expect_failure SYMLINKS 'symlink escape via absolute path' ' +test_expect_success SYMLINKS 'symlink escape via absolute path' ' { mkpatch_symlink tmp "$(pwd)" && mkpatch_add tmp/foo ../foo @@ -128,7 +128,7 @@ test_expect_failure SYMLINKS 'symlink escape via absolute path' ' test_path_is_missing ../foo ' -test_expect_failure SYMLINKS 'symlink escape via absolute path (index)' ' +test_expect_success SYMLINKS 'symlink escape via absolute path (index)' ' { mkpatch_symlink tmp "$(pwd)" && mkpatch_add tmp/foo ../foo -- cgit v1.2.3