Commit bef02d3e by Edward Thomson

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".
parent 63e36c53
......@@ -1634,16 +1634,17 @@ static bool validate_component(
return true;
}
bool git_fs_path_is_valid_ext(
const char *path,
bool git_fs_path_is_valid_str_ext(
const git_str *path,
unsigned int flags,
bool (*validate_char_cb)(char ch, void *payload),
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
void *payload)
{
const char *start, *c;
size_t len = 0;
for (start = c = path; *c; c++) {
for (start = c = path->ptr; *c && len < path->size; c++, len++) {
if (!validate_char(*c, flags))
return false;
......@@ -1663,6 +1664,15 @@ bool git_fs_path_is_valid_ext(
start = c + 1;
}
/*
* We want to support paths specified as either `const char *`
* or `git_str *`; we pass size as `SIZE_MAX` when we use a
* `const char *` to avoid a `strlen`. Ensure that we didn't
* have a NUL in the buffer if there was a non-SIZE_MAX length.
*/
if (path->size != SIZE_MAX && len != path->size)
return false;
if (!validate_component(start, (c - start), flags))
return false;
......@@ -1673,11 +1683,6 @@ bool git_fs_path_is_valid_ext(
return true;
}
bool git_fs_path_is_valid(const char *path, unsigned int flags)
{
return git_fs_path_is_valid_ext(path, flags, NULL, NULL, NULL);
}
#ifdef GIT_WIN32
GIT_INLINE(bool) should_validate_longpaths(git_repository *repo)
{
......
......@@ -621,25 +621,55 @@ extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url
#endif
/**
* Validate a filesystem path. This ensures that the given path is legal
* and does not contain any "unsafe" components like path traversal ('.'
* or '..'), characters that are inappropriate for lesser filesystems
* (trailing ' ' or ':' characters), or filenames ("component names")
* that are not supported ('AUX', 'COM1").
*/
extern bool git_fs_path_is_valid(const char *path, unsigned int flags);
/**
* Validate a filesystem path; with custom callbacks per-character and
* per-path component.
*/
extern bool git_fs_path_is_valid_ext(
const char *path,
extern bool git_fs_path_is_valid_str_ext(
const git_str *path,
unsigned int flags,
bool (*validate_char_cb)(char ch, void *payload),
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
void *payload);
GIT_INLINE(bool) git_fs_path_is_valid_ext(
const char *path,
unsigned int flags,
bool (*validate_char_cb)(char ch, void *payload),
bool (*validate_component_cb)(const char *component, size_t len, void *payload),
void *payload)
{
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
return git_fs_path_is_valid_str_ext(
&str,
flags,
validate_char_cb,
validate_component_cb,
payload);
}
/**
* Validate a filesystem path. This ensures that the given path is legal
* and does not contain any "unsafe" components like path traversal ('.'
* or '..'), characters that are inappropriate for lesser filesystems
* (trailing ' ' or ':' characters), or filenames ("component names")
* that are not supported ('AUX', 'COM1").
*/
GIT_INLINE(bool) git_fs_path_is_valid(
const char *path,
unsigned int flags)
{
const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
return git_fs_path_is_valid_str_ext(&str, flags, NULL, NULL, NULL);
}
/** Validate a filesystem path in a `git_str`. */
GIT_INLINE(bool) git_fs_path_is_valid_str(
const git_str *path,
unsigned int flags)
{
return git_fs_path_is_valid_str_ext(path, flags, NULL, NULL, NULL);
}
/**
* Validate an on-disk path, taking into account that it will have a
* suffix appended (eg, `.lock`).
......
......@@ -64,6 +64,33 @@ void test_path_core__isvalid_standard(void)
cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar/.file", 0));
}
/* Ensure that `is_valid_str` only reads str->size bytes */
void test_path_core__isvalid_standard_str(void)
{
git_str str = GIT_STR_INIT_CONST("foo/bar//zap", 0);
str.size = 0;
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
str.size = 3;
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
str.size = 4;
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
str.size = 5;
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
str.size = 7;
cl_assert_equal_b(true, git_fs_path_is_valid_str(&str, 0));
str.size = 8;
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
str.size = strlen(str.ptr);
cl_assert_equal_b(false, git_fs_path_is_valid_str(&str, 0));
}
void test_path_core__isvalid_empty_dir_component(void)
{
cl_assert_equal_b(false, git_fs_path_is_valid("foo//bar", 0));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment