fast-import: disallow more path components

Instead of just disallowing '.' and '..', make use of verify_path() to ensure that fast-import will disallow anything we wouldn't allow into the index, such as anything under .git/, .gitmodules as a symlink, or a dos drive prefix on Windows. Since a few fast-export and fast-import tests that tried to stress-test the correct handling of quoting relied on filenames that fail is_valid_win32_path(), such as spaces or periods at the end of filenames or backslashes within the filename, turn off core.protectNTFS for those tests to ensure they keep passing. Helped-by: Jeff King <peff@peff.net> Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Nov 30, 2024 at 01:09 UTC da91a90c2f42f4b4e1fffa916a51e0e7ecb86ed9
3 files changed +91 -7
builtin/fast-import.c
+6 -2
@@ -13,6 +13,7 @@
13 #include "delta.h"
14 #include "pack.h"
15 #include "path.h"
16 +#include "read-cache-ll.h"
17 #include "refs.h"
18 #include "csum-file.h"
19 #include "quote.h"
@@ -1468,8 +1469,6 @@ static int tree_content_set(
1469 root->tree = t = grow_tree_content(t, t->entry_count);
1470 e = new_tree_entry();
1471 e->name = to_atom(p, n);
1471 - if (is_dot_or_dotdot(e->name->str_dat))
1472 - die("path %s contains invalid component", p);
1472 e->versions[0].mode = 0;
1473 oidclr(&e->versions[0].oid, the_repository->hash_algo);
1474 t->entries[t->entry_count++] = e;
@@ -2416,6 +2415,9 @@ static void file_change_m(const char *p, struct branch *b)
2415 tree_content_replace(&b->branch_tree, &oid, mode, NULL);
2416 return;
2417 }
2418 +
2419 + if (!verify_path(path.buf, mode))
2420 + die("invalid path '%s'", path.buf);
2421 tree_content_set(&b->branch_tree, path.buf, &oid, mode, NULL);
2422 }
2423
@@ -2453,6 +2455,8 @@ static void file_change_cr(const char *p, struct branch *b, int rename)
2455 leaf.tree);
2456 return;
2457 }
2458 + if (!verify_path(dest.buf, leaf.versions[1].mode))
2459 + die("invalid path '%s'", dest.buf);
2460 tree_content_set(&b->branch_tree, dest.buf,
2461 &leaf.versions[1].oid,
2462 leaf.versions[1].mode,
t/t9300-fast-import.sh
+84 -4
@@ -522,7 +522,7 @@ test_expect_success 'B: fail on invalid committer (5)' '
522 test_must_fail git fast-import <input
523 '
524
525 -test_expect_success 'B: fail on invalid file path' '
525 +test_expect_success 'B: fail on invalid file path of ..' '
526 cat >input <<-INPUT_END &&
527 blob
528 mark :1
@@ -542,6 +542,86 @@ test_expect_success 'B: fail on invalid file path' '
542 test_must_fail git fast-import <input
543 '
544
545 +test_expect_success 'B: fail on invalid file path of .' '
546 + cat >input <<-INPUT_END &&
547 + blob
548 + mark :1
549 + data <<EOF
550 + File contents
551 + EOF
552 +
553 + commit refs/heads/badpath
554 + committer Name <email> $GIT_COMMITTER_DATE
555 + data <<COMMIT
556 + Commit Message
557 + COMMIT
558 + M 100644 :1 ./invalid-path
559 + INPUT_END
560 +
561 + test_when_finished "git update-ref -d refs/heads/badpath" &&
562 + test_must_fail git fast-import <input
563 +'
564 +
565 +test_expect_success WINDOWS 'B: fail on invalid file path of C:' '
566 + cat >input <<-INPUT_END &&
567 + blob
568 + mark :1
569 + data <<EOF
570 + File contents
571 + EOF
572 +
573 + commit refs/heads/badpath
574 + committer Name <email> $GIT_COMMITTER_DATE
575 + data <<COMMIT
576 + Commit Message
577 + COMMIT
578 + M 100644 :1 C:/invalid-path
579 + INPUT_END
580 +
581 + test_when_finished "git update-ref -d refs/heads/badpath" &&
582 + test_must_fail git fast-import <input
583 +'
584 +
585 +test_expect_success 'B: fail on invalid file path of .git' '
586 + cat >input <<-INPUT_END &&
587 + blob
588 + mark :1
589 + data <<EOF
590 + File contents
591 + EOF
592 +
593 + commit refs/heads/badpath
594 + committer Name <email> $GIT_COMMITTER_DATE
595 + data <<COMMIT
596 + Commit Message
597 + COMMIT
598 + M 100644 :1 .git/invalid-path
599 + INPUT_END
600 +
601 + test_when_finished "git update-ref -d refs/heads/badpath" &&
602 + test_must_fail git fast-import <input
603 +'
604 +
605 +test_expect_success 'B: fail on invalid file path of .gitmodules' '
606 + cat >input <<-INPUT_END &&
607 + blob
608 + mark :1
609 + data <<EOF
610 + File contents
611 + EOF
612 +
613 + commit refs/heads/badpath
614 + committer Name <email> $GIT_COMMITTER_DATE
615 + data <<COMMIT
616 + Commit Message
617 + COMMIT
618 + M 120000 :1 .gitmodules
619 + INPUT_END
620 +
621 + test_when_finished "git update-ref -d refs/heads/badpath" &&
622 + test_must_fail git fast-import <input
623 +'
624 +
625 ###
626 ### series C
627 ###
@@ -966,7 +1046,7 @@ test_expect_success 'L: verify internal tree sorting' '
1046 :100644 100644 M ba
1047 EXPECT_END
1048
969 - git fast-import <input &&
1049 + git -c core.protectNTFS=false fast-import <input &&
1050 GIT_PRINT_SHA1_ELLIPSIS="yes" git diff-tree --abbrev --raw L^ L >output &&
1051 cut -d" " -f1,2,5 output >actual &&
1052 test_cmp expect actual
@@ -3117,7 +3197,7 @@ test_path_eol_success () {
3197 test_expect_success "S: paths at EOL with $test must work" '
3198 test_when_finished "git branch -D S-path-eol" &&
3199
3120 - git fast-import --export-marks=marks.out <<-EOF >out 2>err &&
3200 + git -c core.protectNTFS=false fast-import --export-marks=marks.out <<-EOF >out 2>err &&
3201 blob
3202 mark :401
3203 data <<BLOB
@@ -3226,7 +3306,7 @@ test_path_space_success () {
3306 test_expect_success "S: paths before space with $test must work" '
3307 test_when_finished "git branch -D S-path-space" &&
3308
3229 - git fast-import --export-marks=marks.out <<-EOF 2>err &&
3309 + git -c core.protectNTFS=false fast-import --export-marks=marks.out <<-EOF 2>err &&
3310 blob
3311 mark :401
3312 data <<BLOB
t/t9350-fast-export.sh
+1 -1
@@ -631,7 +631,7 @@ test_expect_success 'fast-export quotes pathnames' '
631 git rev-list HEAD >expect &&
632 git init result &&
633 cd result &&
634 - git fast-import <../export.out &&
634 + git -c core.protectNTFS=false fast-import <../export.out &&
635 git rev-list HEAD >actual &&
636 test_cmp ../expect actual
637 )