Sync with 2.39.4
* maint-2.39: (38 commits) Git 2.39.4 fsck: warn about symlink pointing inside a gitdir core.hooksPath: add some protection while cloning init.templateDir: consider this config setting protected clone: prevent hooks from running during a clone Add a helper function to compare file contents init: refactor the template directory discovery into its own function find_hook(): refactor the `STRIP_EXTENSION` logic clone: when symbolic links collide with directories, keep the latter entry: report more colliding paths t5510: verify that D/F confusion cannot lead to an RCE submodule: require the submodule path to contain directories only clone_submodule: avoid using `access()` on directories submodules: submodule paths must not contain symlinks clone: prevent clashing git dirs when cloning submodule in parallel t7423: add tests for symlinked submodule directories has_dir_name(): do not get confused by characters < '/' docs: document security issues around untrusted .git dirs upload-pack: disable lazy-fetching by default fetch/clone: detect dubious ownership of local repositories ...
This commit is contained in:
89
submodule.c
89
submodule.c
@ -1005,6 +1005,9 @@ static int submodule_has_commits(struct repository *r,
|
||||
.super_oid = super_oid
|
||||
};
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
oid_array_for_each_unique(commits, check_has_commit, &has_commit);
|
||||
|
||||
if (has_commit.result) {
|
||||
@ -1127,6 +1130,9 @@ static int push_submodule(const char *path,
|
||||
const struct string_list *push_options,
|
||||
int dry_run)
|
||||
{
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
if (for_each_remote_ref_submodule(path, has_remote, NULL) > 0) {
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
strvec_push(&cp.args, "push");
|
||||
@ -1176,6 +1182,9 @@ static void submodule_push_check(const char *path, const char *head,
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
int i;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
strvec_push(&cp.args, "submodule--helper");
|
||||
strvec_push(&cp.args, "push-check");
|
||||
strvec_push(&cp.args, head);
|
||||
@ -1507,6 +1516,9 @@ static struct fetch_task *fetch_task_create(struct submodule_parallel_fetch *spf
|
||||
struct fetch_task *task = xmalloc(sizeof(*task));
|
||||
memset(task, 0, sizeof(*task));
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
task->sub = submodule_from_path(spf->r, treeish_name, path);
|
||||
|
||||
if (!task->sub) {
|
||||
@ -1879,6 +1891,9 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
|
||||
const char *git_dir;
|
||||
int ignore_cp_exit_code = 0;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
strbuf_addf(&buf, "%s/.git", path);
|
||||
git_dir = read_gitfile(buf.buf);
|
||||
if (!git_dir)
|
||||
@ -1955,6 +1970,9 @@ int submodule_uses_gitfile(const char *path)
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
const char *git_dir;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
strbuf_addf(&buf, "%s/.git", path);
|
||||
git_dir = read_gitfile(buf.buf);
|
||||
if (!git_dir) {
|
||||
@ -1994,6 +2012,9 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
int ret = 0;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
if (!file_exists(path) || is_empty_dir(path))
|
||||
return 0;
|
||||
|
||||
@ -2044,6 +2065,9 @@ void submodule_unset_core_worktree(const struct submodule *sub)
|
||||
{
|
||||
struct strbuf config_path = STRBUF_INIT;
|
||||
|
||||
if (validate_submodule_path(sub->path) < 0)
|
||||
exit(128);
|
||||
|
||||
submodule_name_to_gitdir(&config_path, the_repository, sub->name);
|
||||
strbuf_addstr(&config_path, "/config");
|
||||
|
||||
@ -2058,6 +2082,9 @@ static int submodule_has_dirty_index(const struct submodule *sub)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
if (validate_submodule_path(sub->path) < 0)
|
||||
exit(128);
|
||||
|
||||
prepare_submodule_repo_env(&cp.env);
|
||||
|
||||
cp.git_cmd = 1;
|
||||
@ -2075,6 +2102,10 @@ static int submodule_has_dirty_index(const struct submodule *sub)
|
||||
static void submodule_reset_index(const char *path, const char *super_prefix)
|
||||
{
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
prepare_submodule_repo_env(&cp.env);
|
||||
|
||||
cp.git_cmd = 1;
|
||||
@ -2138,10 +2169,27 @@ int submodule_move_head(const char *path, const char *super_prefix,
|
||||
if (!submodule_uses_gitfile(path))
|
||||
absorb_git_dir_into_superproject(path,
|
||||
super_prefix);
|
||||
else {
|
||||
char *dotgit = xstrfmt("%s/.git", path);
|
||||
char *git_dir = xstrdup(read_gitfile(dotgit));
|
||||
|
||||
free(dotgit);
|
||||
if (validate_submodule_git_dir(git_dir,
|
||||
sub->name) < 0)
|
||||
die(_("refusing to create/use '%s' in "
|
||||
"another submodule's git dir"),
|
||||
git_dir);
|
||||
free(git_dir);
|
||||
}
|
||||
} else {
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
submodule_name_to_gitdir(&gitdir, the_repository,
|
||||
sub->name);
|
||||
if (validate_submodule_git_dir(gitdir.buf,
|
||||
sub->name) < 0)
|
||||
die(_("refusing to create/use '%s' in another "
|
||||
"submodule's git dir"),
|
||||
gitdir.buf);
|
||||
connect_work_tree_and_git_dir(path, gitdir.buf, 0);
|
||||
strbuf_release(&gitdir);
|
||||
|
||||
@ -2262,6 +2310,34 @@ int validate_submodule_git_dir(char *git_dir, const char *submodule_name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int validate_submodule_path(const char *path)
|
||||
{
|
||||
char *p = xstrdup(path);
|
||||
struct stat st;
|
||||
int i, ret = 0;
|
||||
char sep;
|
||||
|
||||
for (i = 0; !ret && p[i]; i++) {
|
||||
if (!is_dir_sep(p[i]))
|
||||
continue;
|
||||
|
||||
sep = p[i];
|
||||
p[i] = '\0';
|
||||
/* allow missing components, but no symlinks */
|
||||
ret = lstat(p, &st) || !S_ISLNK(st.st_mode) ? 0 : -1;
|
||||
p[i] = sep;
|
||||
if (ret)
|
||||
error(_("expected '%.*s' in submodule path '%s' not to "
|
||||
"be a symbolic link"), i, p, p);
|
||||
}
|
||||
if (!lstat(p, &st) && S_ISLNK(st.st_mode))
|
||||
ret = error(_("expected submodule path '%s' not to be a "
|
||||
"symbolic link"), p);
|
||||
free(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Embeds a single submodules git directory into the superprojects git dir,
|
||||
* non recursively.
|
||||
@ -2273,6 +2349,9 @@ static void relocate_single_git_dir_into_superproject(const char *path,
|
||||
struct strbuf new_gitdir = STRBUF_INIT;
|
||||
const struct submodule *sub;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
if (submodule_uses_worktrees(path))
|
||||
die(_("relocate_gitdir for submodule '%s' with "
|
||||
"more than one worktree not supported"), path);
|
||||
@ -2314,6 +2393,9 @@ static void absorb_git_dir_into_superproject_recurse(const char *path,
|
||||
|
||||
struct child_process cp = CHILD_PROCESS_INIT;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
cp.dir = path;
|
||||
cp.git_cmd = 1;
|
||||
cp.no_stdin = 1;
|
||||
@ -2338,6 +2420,10 @@ void absorb_git_dir_into_superproject(const char *path,
|
||||
int err_code;
|
||||
const char *sub_git_dir;
|
||||
struct strbuf gitdir = STRBUF_INIT;
|
||||
|
||||
if (validate_submodule_path(path) < 0)
|
||||
exit(128);
|
||||
|
||||
strbuf_addf(&gitdir, "%s/.git", path);
|
||||
sub_git_dir = resolve_gitdir_gently(gitdir.buf, &err_code);
|
||||
|
||||
@ -2480,6 +2566,9 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
|
||||
const char *git_dir;
|
||||
int ret = 0;
|
||||
|
||||
if (validate_submodule_path(submodule) < 0)
|
||||
exit(128);
|
||||
|
||||
strbuf_reset(buf);
|
||||
strbuf_addstr(buf, submodule);
|
||||
strbuf_complete(buf, '/');
|
||||
|
Reference in New Issue
Block a user