mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-11 12:09:07 +00:00
checkout: disallow bad paths on HFS
HFS filesystems ignore some characters like U+200C. When these characters are included in a path, they will be ignored for the purposes of comparison with other paths. Thus, if you have a ".git" folder, a folder of ".git<U+200C>" will also match. Protect our ".git" folder by ensuring that ".git<U+200C>" and friends do not match it.
This commit is contained in:
parent
ee5da720e5
commit
11d67b754d
93
src/path.c
93
src/path.c
@ -1282,6 +1282,95 @@ GIT_INLINE(bool) verify_dospath(
|
||||
component[last] != ':');
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) verify_dotgit_hfs(const char *component, size_t len)
|
||||
{
|
||||
const unsigned char *c;
|
||||
int git = 0, ign = 0;
|
||||
unsigned char one, two;
|
||||
|
||||
while (len) {
|
||||
switch (*(c = (const unsigned char *)component++)) {
|
||||
case '.':
|
||||
if (ign || git++ != 0)
|
||||
return true;
|
||||
break;
|
||||
case 'g':
|
||||
case 'G':
|
||||
if (ign || git++ != 1)
|
||||
return true;
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
if (ign || git++ != 2)
|
||||
return true;
|
||||
break;
|
||||
case 't':
|
||||
case 'T':
|
||||
if (ign || git++ != 3)
|
||||
return true;
|
||||
break;
|
||||
|
||||
case 0xe2:
|
||||
case 0xef:
|
||||
if (ign++ != 0)
|
||||
return true;
|
||||
one = *c;
|
||||
break;
|
||||
|
||||
case 0x80:
|
||||
case 0x81:
|
||||
if (ign++ != 1 || one != 0xe2)
|
||||
return true;
|
||||
two = *c;
|
||||
break;
|
||||
|
||||
case 0xbb:
|
||||
if (ign++ != 1 || one != 0xef)
|
||||
return true;
|
||||
two = *c;
|
||||
break;
|
||||
|
||||
case 0x8c:
|
||||
case 0x8d:
|
||||
case 0x8e:
|
||||
case 0x8f:
|
||||
if (ign != 2 || two != 0x80)
|
||||
return true;
|
||||
ign = 0;
|
||||
break;
|
||||
|
||||
case 0xaa:
|
||||
case 0xab:
|
||||
case 0xac:
|
||||
case 0xad:
|
||||
case 0xae:
|
||||
if (ign != 2 || (two != 0x80 && two != 0x81))
|
||||
return true;
|
||||
ign = 0;
|
||||
break;
|
||||
|
||||
case 0xaf:
|
||||
if (ign != 2 || two != 0x81)
|
||||
return true;
|
||||
ign = 0;
|
||||
break;
|
||||
|
||||
case 0xbf:
|
||||
if (ign != 2 || two != 0xbb)
|
||||
return true;
|
||||
ign = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
len--;
|
||||
}
|
||||
|
||||
return (ign || git != 4);
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags)
|
||||
{
|
||||
if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\')
|
||||
@ -1362,6 +1451,10 @@ static bool verify_component(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (flags & GIT_PATH_REJECT_DOT_GIT_HFS &&
|
||||
!verify_dotgit_hfs(component, len))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -472,6 +472,7 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or
|
||||
#define GIT_PATH_REJECT_DOS_GIT_SHORTNAME (1 << 6)
|
||||
#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
|
||||
#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
|
||||
#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
# define GIT_PATH_REJECT_DEFAULTS \
|
||||
@ -483,6 +484,10 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or
|
||||
GIT_PATH_REJECT_DOS_GIT_SHORTNAME | \
|
||||
GIT_PATH_REJECT_DOS_PATHS | \
|
||||
GIT_PATH_REJECT_NT_CHARS
|
||||
#elif __APPLE__
|
||||
# define GIT_PATH_REJECT_DEFAULTS \
|
||||
GIT_PATH_REJECT_TRAVERSAL | \
|
||||
GIT_PATH_REJECT_DOT_GIT_HFS
|
||||
#else
|
||||
# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
|
||||
#endif
|
||||
|
@ -249,3 +249,45 @@ void test_checkout_nasty__dot_git_colon_stuff(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Trees that contains entries with a tree ".git" that contain
|
||||
* byte sequences:
|
||||
* { 0xe2, 0x80, 0x8c }
|
||||
* { 0xe2, 0x80, 0x8d }
|
||||
* { 0xe2, 0x80, 0x8e }
|
||||
* { 0xe2, 0x80, 0x8f }
|
||||
* { 0xe2, 0x80, 0xaa }
|
||||
* { 0xe2, 0x80, 0xab }
|
||||
* { 0xe2, 0x80, 0xac }
|
||||
* { 0xe2, 0x80, 0xad }
|
||||
* { 0xe2, 0x81, 0xae }
|
||||
* { 0xe2, 0x81, 0xaa }
|
||||
* { 0xe2, 0x81, 0xab }
|
||||
* { 0xe2, 0x81, 0xac }
|
||||
* { 0xe2, 0x81, 0xad }
|
||||
* { 0xe2, 0x81, 0xae }
|
||||
* { 0xe2, 0x81, 0xaf }
|
||||
* { 0xef, 0xbb, 0xbf }
|
||||
* Because these map to characters that HFS filesystems "ignore". Thus
|
||||
* ".git<U+200C>" will map to ".git".
|
||||
*/
|
||||
void test_checkout_nasty__dot_git_hfs_ignorable(void)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
|
||||
test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
|
||||
#endif
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ void test_path_core__isvalid_dos_paths_withnum(void)
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", GIT_PATH_REJECT_DOS_PATHS));
|
||||
}
|
||||
|
||||
void test_core_path__isvalid_nt_chars(void)
|
||||
void test_path_core__isvalid_nt_chars(void)
|
||||
{
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\001foo", 0));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\037bar", 0));
|
||||
@ -245,3 +245,32 @@ void test_core_path__isvalid_nt_chars(void)
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf?bar", GIT_PATH_REJECT_NT_CHARS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf*bar", GIT_PATH_REJECT_NT_CHARS));
|
||||
}
|
||||
|
||||
void test_path_core__isvalid_dotgit_with_hfs_ignorables(void)
|
||||
{
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".g\xe2\x80\x8eIt", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".\xe2\x80\x8fgIt", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xaa.gIt", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xab.\xe2\x80\xacG\xe2\x80\xadI\xe2\x80\xaet", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xab.\xe2\x80\xaaG\xe2\x81\xabI\xe2\x80\xact", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xad.\xe2\x80\xaeG\xef\xbb\xbfIT", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".g", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, " .git", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "..git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT.", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2\x80It", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".\xe2gIt", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "\xe2\x80\xaa.gi", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2i\x80T\x8e", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", GIT_PATH_REJECT_DOT_GIT_HFS));
|
||||
}
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
Normal file
Binary file not shown.
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
Normal file
BIN
tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user