summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2021-11-01 10:57:28 -0400
committerEdward Thomson <ethomson@edwardthomson.com>2021-11-09 15:17:17 +0000
commitbef02d3e638ce8c95c9e63622b46d87a0f8ee2b2 (patch)
tree5982af2e8e7f3b2996dcc65edcd5d3f42bd04118
parent63e36c53caa8c3f1581471de64bbef3ca87f3921 (diff)
downloadlibgit2-bef02d3e638ce8c95c9e63622b46d87a0f8ee2b2.tar.gz
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".
-rw-r--r--src/fs_path.c21
-rw-r--r--src/fs_path.h52
-rw-r--r--tests/path/core.c27
3 files changed, 81 insertions, 19 deletions
diff --git a/src/fs_path.c b/src/fs_path.c
index ceba02d62..fa27a6e73 100644
--- a/src/fs_path.c
+++ b/src/fs_path.c
@@ -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)
{
diff --git a/src/fs_path.h b/src/fs_path.h
index 991e7cd83..2f4bc9f79 100644
--- a/src/fs_path.h
+++ b/src/fs_path.h
@@ -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`).
diff --git a/tests/path/core.c b/tests/path/core.c
index f48a76957..6fa0450ca 100644
--- a/tests/path/core.c
+++ b/tests/path/core.c
@@ -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));