mirror of
https://git.proxmox.com/git/libgit2
synced 2025-08-13 06:39:43 +00:00
index: allow read of index w/ illegal entries
Allow `git_index_read` to handle reading existing indexes with illegal entries. Allow the low-level `git_index_add` to add properly formed `git_index_entry`s even if they contain paths that would be illegal for the current filesystem (eg, `AUX`). Continue to disallow `git_index_add_bypath` from adding entries that are illegal universally illegal (eg, `.git`, `foo/../bar`).
This commit is contained in:
parent
4fea9cffbd
commit
318b825e76
@ -1226,7 +1226,7 @@ static int checkout_verify_paths(
|
|||||||
int action,
|
int action,
|
||||||
git_diff_delta *delta)
|
git_diff_delta *delta)
|
||||||
{
|
{
|
||||||
unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT;
|
unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
|
||||||
|
|
||||||
if (action & CHECKOUT_ACTION__REMOVE) {
|
if (action & CHECKOUT_ACTION__REMOVE) {
|
||||||
if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
|
if (!git_path_isvalid(repo, delta->old_file.path, flags)) {
|
||||||
|
32
src/index.c
32
src/index.c
@ -891,17 +891,31 @@ static void index_entry_adjust_namemask(
|
|||||||
entry->flags |= GIT_IDXENTRY_NAMEMASK;
|
entry->flags |= GIT_IDXENTRY_NAMEMASK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When `from_workdir` is true, we will validate the paths to avoid placing
|
||||||
|
* paths that are invalid for the working directory on the current filesystem
|
||||||
|
* (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This
|
||||||
|
* function will *always* prevent `.git` and directory traversal `../` from
|
||||||
|
* being added to the index.
|
||||||
|
*/
|
||||||
static int index_entry_create(
|
static int index_entry_create(
|
||||||
git_index_entry **out,
|
git_index_entry **out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *path)
|
const char *path,
|
||||||
|
bool from_workdir)
|
||||||
{
|
{
|
||||||
size_t pathlen = strlen(path), alloclen;
|
size_t pathlen = strlen(path), alloclen;
|
||||||
struct entry_internal *entry;
|
struct entry_internal *entry;
|
||||||
|
unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS;
|
||||||
|
|
||||||
if (!git_path_isvalid(repo, path,
|
/* always reject placing `.git` in the index and directory traversal.
|
||||||
GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) {
|
* when requested, disallow platform-specific filenames and upgrade to
|
||||||
giterr_set(GITERR_INDEX, "Invalid path: '%s'", path);
|
* the platform-specific `.git` tests (eg, `git~1`, etc).
|
||||||
|
*/
|
||||||
|
if (from_workdir)
|
||||||
|
path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS;
|
||||||
|
|
||||||
|
if (!git_path_isvalid(repo, path, path_valid_flags)) {
|
||||||
|
giterr_set(GITERR_INDEX, "invalid path: '%s'", path);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -933,7 +947,7 @@ static int index_entry_init(
|
|||||||
"Could not initialize index entry. "
|
"Could not initialize index entry. "
|
||||||
"Index is not backed up by an existing repository.");
|
"Index is not backed up by an existing repository.");
|
||||||
|
|
||||||
if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0)
|
if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, true) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* write the blob to disk and get the oid and stat info */
|
/* write the blob to disk and get the oid and stat info */
|
||||||
@ -1013,7 +1027,7 @@ static int index_entry_dup(
|
|||||||
git_index *index,
|
git_index *index,
|
||||||
const git_index_entry *src)
|
const git_index_entry *src)
|
||||||
{
|
{
|
||||||
if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0)
|
if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
index_entry_cpy(*out, src);
|
index_entry_cpy(*out, src);
|
||||||
@ -1035,7 +1049,7 @@ static int index_entry_dup_nocache(
|
|||||||
git_index *index,
|
git_index *index,
|
||||||
const git_index_entry *src)
|
const git_index_entry *src)
|
||||||
{
|
{
|
||||||
if (index_entry_create(out, INDEX_OWNER(index), src->path) < 0)
|
if (index_entry_create(out, INDEX_OWNER(index), src->path, false) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
index_entry_cpy_nocache(*out, src);
|
index_entry_cpy_nocache(*out, src);
|
||||||
@ -1447,7 +1461,7 @@ static int add_repo_as_submodule(git_index_entry **out, git_index *index, const
|
|||||||
struct stat st;
|
struct stat st;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
if (index_entry_create(&entry, INDEX_OWNER(index), path) < 0)
|
if (index_entry_create(&entry, INDEX_OWNER(index), path, true) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0)
|
if ((error = git_buf_joinpath(&abspath, git_repository_workdir(repo), path)) < 0)
|
||||||
@ -2884,7 +2898,7 @@ static int read_tree_cb(
|
|||||||
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
|
if (git_buf_joinpath(&path, root, tentry->filename) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0)
|
if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, false) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
entry->mode = tentry->attr;
|
entry->mode = tentry->attr;
|
||||||
|
@ -1630,9 +1630,12 @@ static bool verify_component(
|
|||||||
!verify_dotgit_ntfs(repo, component, len))
|
!verify_dotgit_ntfs(repo, component, len))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* don't bother rerunning the `.git` test if we ran the HFS or NTFS
|
||||||
|
* specific tests, they would have already rejected `.git`.
|
||||||
|
*/
|
||||||
if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
|
if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
|
||||||
(flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
|
(flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
|
||||||
(flags & GIT_PATH_REJECT_DOT_GIT) &&
|
(flags & GIT_PATH_REJECT_DOT_GIT_LITERAL) &&
|
||||||
len == 4 &&
|
len == 4 &&
|
||||||
component[0] == '.' &&
|
component[0] == '.' &&
|
||||||
(component[1] == 'g' || component[1] == 'G') &&
|
(component[1] == 'g' || component[1] == 'G') &&
|
||||||
@ -1649,6 +1652,8 @@ GIT_INLINE(unsigned int) dotgit_flags(
|
|||||||
{
|
{
|
||||||
int protectHFS = 0, protectNTFS = 0;
|
int protectHFS = 0, protectNTFS = 0;
|
||||||
|
|
||||||
|
flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
protectHFS = 1;
|
protectHFS = 1;
|
||||||
#endif
|
#endif
|
||||||
|
18
src/path.h
18
src/path.h
@ -564,15 +564,16 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or
|
|||||||
#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
|
#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6)
|
||||||
#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
|
#define GIT_PATH_REJECT_DOS_PATHS (1 << 7)
|
||||||
#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
|
#define GIT_PATH_REJECT_NT_CHARS (1 << 8)
|
||||||
#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9)
|
#define GIT_PATH_REJECT_DOT_GIT_LITERAL (1 << 9)
|
||||||
#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10)
|
#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 10)
|
||||||
|
#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 11)
|
||||||
|
|
||||||
/* Default path safety for writing files to disk: since we use the
|
/* Default path safety for writing files to disk: since we use the
|
||||||
* Win32 "File Namespace" APIs ("\\?\") we need to protect from
|
* Win32 "File Namespace" APIs ("\\?\") we need to protect from
|
||||||
* paths that the normal Win32 APIs would not write.
|
* paths that the normal Win32 APIs would not write.
|
||||||
*/
|
*/
|
||||||
#ifdef GIT_WIN32
|
#ifdef GIT_WIN32
|
||||||
# define GIT_PATH_REJECT_DEFAULTS \
|
# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \
|
||||||
GIT_PATH_REJECT_TRAVERSAL | \
|
GIT_PATH_REJECT_TRAVERSAL | \
|
||||||
GIT_PATH_REJECT_BACKSLASH | \
|
GIT_PATH_REJECT_BACKSLASH | \
|
||||||
GIT_PATH_REJECT_TRAILING_DOT | \
|
GIT_PATH_REJECT_TRAILING_DOT | \
|
||||||
@ -581,9 +582,18 @@ extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or
|
|||||||
GIT_PATH_REJECT_DOS_PATHS | \
|
GIT_PATH_REJECT_DOS_PATHS | \
|
||||||
GIT_PATH_REJECT_NT_CHARS
|
GIT_PATH_REJECT_NT_CHARS
|
||||||
#else
|
#else
|
||||||
# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL
|
# define GIT_PATH_REJECT_FILESYSTEM_DEFAULTS \
|
||||||
|
GIT_PATH_REJECT_TRAVERSAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Paths that should never be written into the working directory. */
|
||||||
|
#define GIT_PATH_REJECT_WORKDIR_DEFAULTS \
|
||||||
|
GIT_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT
|
||||||
|
|
||||||
|
/* Paths that should never be written to the index. */
|
||||||
|
#define GIT_PATH_REJECT_INDEX_DEFAULTS \
|
||||||
|
GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine whether a path is a valid git path or not - this must not contain
|
* Determine whether a path is a valid git path or not - this must not contain
|
||||||
* a '.' or '..' component, or a component that is ".git" (in any case).
|
* a '.' or '..' component, or a component that is ".git" (in any case).
|
||||||
|
@ -717,7 +717,7 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
|
|||||||
|
|
||||||
assert(file && backend && name);
|
assert(file && backend && name);
|
||||||
|
|
||||||
if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) {
|
if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
|
||||||
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
|
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name);
|
||||||
return GIT_EINVALIDSPEC;
|
return GIT_EINVALIDSPEC;
|
||||||
}
|
}
|
||||||
@ -1672,7 +1672,7 @@ static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char
|
|||||||
|
|
||||||
repo = backend->repo;
|
repo = backend->repo;
|
||||||
|
|
||||||
if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) {
|
if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
|
||||||
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
|
giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname);
|
||||||
return GIT_EINVALIDSPEC;
|
return GIT_EINVALIDSPEC;
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,12 @@ void test_path_core__isvalid_dot_git(void)
|
|||||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0));
|
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0));
|
||||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0));
|
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0));
|
||||||
|
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT));
|
cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT_LITERAL));
|
||||||
|
|
||||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0));
|
cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0));
|
||||||
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0));
|
cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0));
|
||||||
|
Loading…
Reference in New Issue
Block a user