Skip to content

Commit bef02d3

Browse files
committed
fs_path: introduce str_is_valid
Provide a mechanism for users to limit the number of characters that are examined; `git_fs_path_str_is_valid` and friends will only examine up to `str->size` bytes. `git_fs_path_is_valid` delegates to these new functions by passing `SIZE_MAX` (instead of doing a `strlen`), which is a sentinel value meaning "look for a NUL terminator".
1 parent 63e36c5 commit bef02d3

File tree

3 files changed

+81
-19
lines changed

3 files changed

+81
-19
lines changed

src/fs_path.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,16 +1634,17 @@ static bool validate_component(
16341634
return true;
16351635
}
16361636

1637-
bool git_fs_path_is_valid_ext(
1638-
const char *path,
1637+
bool git_fs_path_is_valid_str_ext(
1638+
const git_str *path,
16391639
unsigned int flags,
16401640
bool (*validate_char_cb)(char ch, void *payload),
16411641
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
16421642
void *payload)
16431643
{
16441644
const char *start, *c;
1645+
size_t len = 0;
16451646

1646-
for (start = c = path; *c; c++) {
1647+
for (start = c = path->ptr; *c && len < path->size; c++, len++) {
16471648
if (!validate_char(*c, flags))
16481649
return false;
16491650

@@ -1663,6 +1664,15 @@ bool git_fs_path_is_valid_ext(
16631664
start = c + 1;
16641665
}
16651666

1667+
/*
1668+
* We want to support paths specified as either `const char *`
1669+
* or `git_str *`; we pass size as `SIZE_MAX` when we use a
1670+
* `const char *` to avoid a `strlen`. Ensure that we didn't
1671+
* have a NUL in the buffer if there was a non-SIZE_MAX length.
1672+
*/
1673+
if (path->size != SIZE_MAX && len != path->size)
1674+
return false;
1675+
16661676
if (!validate_component(start, (c - start), flags))
16671677
return false;
16681678

@@ -1673,11 +1683,6 @@ bool git_fs_path_is_valid_ext(
16731683
return true;
16741684
}
16751685

1676-
bool git_fs_path_is_valid(const char *path, unsigned int flags)
1677-
{
1678-
return git_fs_path_is_valid_ext(path, flags, NULL, NULL, NULL);
1679-
}
1680-
16811686
#ifdef GIT_WIN32
16821687
GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
16831688
{

src/fs_path.h

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -620,26 +620,56 @@ extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url
620620
GIT_FS_PATH_REJECT_TRAVERSAL
621621
#endif
622622

623-
/**
624-
* Validate a filesystem path. This ensures that the given path is legal
625-
* and does not contain any "unsafe" components like path traversal ('.'
626-
* or '..'), characters that are inappropriate for lesser filesystems
627-
* (trailing ' ' or ':' characters), or filenames ("component names")
628-
* that are not supported ('AUX', 'COM1").
629-
*/
630-
extern bool git_fs_path_is_valid(const char *path, unsigned int flags);
631-
632623
/**
633624
* Validate a filesystem path; with custom callbacks per-character and
634625
* per-path component.
635626
*/
636-
extern bool git_fs_path_is_valid_ext(
637-
const char *path,
627+
extern bool git_fs_path_is_valid_str_ext(
628+
const git_str *path,
638629
unsigned int flags,
639630
bool (*validate_char_cb)(char ch, void *payload),
640631
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
641632
void *payload);
642633

634+
GIT_INLINE(bool) git_fs_path_is_valid_ext(
635+
const char *path,
636+
unsigned int flags,
637+
bool (*validate_char_cb)(char ch, void *payload),
638+
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
639+
void *payload)
640+
{
641+
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
642+
return git_fs_path_is_valid_str_ext(
643+
&str,
644+
flags,
645+
validate_char_cb,
646+
validate_component_cb,
647+
payload);
648+
}
649+
650+
/**
651+
* Validate a filesystem path. This ensures that the given path is legal
652+
* and does not contain any "unsafe" components like path traversal ('.'
653+
* or '..'), characters that are inappropriate for lesser filesystems
654+
* (trailing ' ' or ':' characters), or filenames ("component names")
655+
* that are not supported ('AUX', 'COM1").
656+
*/
657+
GIT_INLINE(bool) git_fs_path_is_valid(
658+
const char *path,
659+
unsigned int flags)
660+
{
661+
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
662+
return git_fs_path_is_valid_str_ext(&str, flags, NULL, NULL, NULL);
663+
}
664+
665+
/** Validate a filesystem path in a `git_str`. */
666+
GIT_INLINE(bool) git_fs_path_is_valid_str(
667+
const git_str *path,
668+
unsigned int flags)
669+
{
670+
return git_fs_path_is_valid_str_ext(path, flags, NULL, NULL, NULL);
671+
}
672+
643673
/**
644674
* Validate an on-disk path, taking into account that it will have a
645675
* suffix appended (eg, `.lock`).

tests/path/core.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ void test_path_core__isvalid_standard(void)
6464
cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar/.file", 0));
6565
}
6666

67+
/* Ensure that `is_valid_str` only reads str->size bytes */
68+
void test_path_core__isvalid_standard_str(void)
69+
{
70+
git_str str = GIT_STR_INIT_CONST("foo/bar//zap", 0);
71+
72+
str.size = 0;
73+
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
74+
75+
str.size = 3;
76+
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
77+
78+
str.size = 4;
79+
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
80+
81+
str.size = 5;
82+
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
83+
84+
str.size = 7;
85+
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
86+
87+
str.size = 8;
88+
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
89+
90+
str.size = strlen(str.ptr);
91+
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
92+
}
93+
6794
void test_path_core__isvalid_empty_dir_component(void)
6895
{
6996
cl_assert_equal_b(false, git_fs_path_is_valid("foo//bar", 0));

0 commit comments

Comments
 (0)