diff --git a/builtin/init-db.c b/builtin/init-db.c index a101e7f94c119b..64357fdada4126 100644 --- a/builtin/init-db.c +++ b/builtin/init-db.c @@ -10,6 +10,8 @@ #include "exec-cmd.h" #include "parse-options.h" #include "worktree.h" +#include "run-command.h" +#include "hook.h" #ifdef NO_TRUSTABLE_FILEMODE #define TEST_FILEMODE 0 @@ -28,6 +30,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, size_t path_baselen = path->len; size_t template_baselen = template_path->len; struct dirent *de; + int is_hooks_dir = ends_with(template_path->buf, "/hooks/"); /* Note: if ".git/hooks" file exists in the repository being * re-initialized, /etc/core-git/templates/hooks/update would @@ -80,6 +83,10 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, strbuf_release(&lnk); } else if (S_ISREG(st_template.st_mode)) { + if (is_hooks_dir && + is_executable(template_path->buf)) + add_safe_hook(template_path->buf); + if (copy_file(path->buf, template_path->buf, st_template.st_mode)) die_errno(_("cannot copy '%s' to '%s'"), template_path->buf, path->buf); diff --git a/hook.c b/hook.c index f810ee133beb79..b69cc691bdfda1 100644 --- a/hook.c +++ b/hook.c @@ -4,32 +4,6 @@ #include "config.h" #include "strmap.h" -static int identical_to_template_hook(const char *name, const char *path) -{ - const char *env = getenv("GIT_CLONE_TEMPLATE_DIR"); - const char *template_dir = get_template_dir(env && *env ? env : NULL); - struct strbuf template_path = STRBUF_INIT; - int found_template_hook, ret; - - strbuf_addf(&template_path, "%s/hooks/%s", template_dir, name); - found_template_hook = access(template_path.buf, X_OK) >= 0; -#ifdef STRIP_EXTENSION - if (!found_template_hook) { - strbuf_addstr(&template_path, STRIP_EXTENSION); - found_template_hook = access(template_path.buf, X_OK) >= 0; - } -#endif - if (!found_template_hook) { - strbuf_release(&template_path); - return 0; - } - - ret = do_files_match(template_path.buf, path); - - strbuf_release(&template_path); - return ret; -} - static struct strset safe_hook_sha256s = STRSET_INIT; static int safe_hook_sha256s_initialized; @@ -60,6 +34,22 @@ static int get_sha256_of_file_contents(const char *path, char *sha256) return 0; } +void add_safe_hook(const char *path) +{ + char sha256[GIT_SHA256_HEXSZ + 1] = { '\0' }; + + if (!get_sha256_of_file_contents(path, sha256)) { + char *p; + + strset_add(&safe_hook_sha256s, sha256); + + /* support multi-process operations e.g. recursive clones */ + p = xstrfmt("safe.hook.sha256=%s", sha256); + git_config_push_parameter(p); + free(p); + } +} + static int safe_hook_cb(const char *key, const char *value, void *d) { struct strset *set = d; @@ -131,7 +121,6 @@ const char *find_hook(const char *name) return NULL; } if (!git_hooks_path && git_env_bool("GIT_CLONE_PROTECTION_ACTIVE", 0) && - !identical_to_template_hook(name, path.buf) && !is_hook_safe_during_clone(name, path.buf, sha256)) die(_("active `%s` hook found during `git clone`:\n\t%s\n" "For security reasons, this is disallowed by default.\n" diff --git a/hook.h b/hook.h index 4258b13da0d7c3..e2034ee8b233a9 100644 --- a/hook.h +++ b/hook.h @@ -82,4 +82,14 @@ int run_hooks(const char *hook_name); * hook. This function behaves like the old run_hook_le() API. */ int run_hooks_l(const char *hook_name, ...); + +/** + * Mark the contents of the provided path as safe to run during a clone + * operation. + * + * This function is mainly used when copying templates to mark the + * just-copied hooks as benign. + */ +void add_safe_hook(const char *path); + #endif diff --git a/setup.c b/setup.c index c3301f5ab824b5..7f7538c9bf7688 100644 --- a/setup.c +++ b/setup.c @@ -7,6 +7,7 @@ #include "promisor-remote.h" #include "quote.h" #include "exec-cmd.h" +#include "hook.h" static int inside_git_dir = -1; static int inside_work_tree = -1; diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh index 20deca0231b8bf..71eaa3d1e14068 100755 --- a/t/t5601-clone.sh +++ b/t/t5601-clone.sh @@ -819,6 +819,25 @@ test_expect_success 'clone with init.templatedir runs hooks' ' git config --unset init.templateDir && ! grep "active .* hook found" err && test_path_is_missing hook-run-local-config/hook.run + ) && + + test_config_global protocol.file.allow always && + git -C tmpl/hooks submodule add "$(pwd)/tmpl/hooks" sub && + test_tick && + git -C tmpl/hooks add .gitmodules sub && + git -C tmpl/hooks commit -m submodule && + + ( + sane_unset GIT_TEMPLATE_DIR && + NO_SET_GIT_TEMPLATE_DIR=t && + export NO_SET_GIT_TEMPLATE_DIR && + + git -c init.templateDir="$(pwd)/tmpl" \ + clone --recurse-submodules \ + tmpl/hooks hook-run-submodule 2>err && + ! grep "active .* hook found" err && + test_path_is_file hook-run-submodule/hook.run && + test_path_is_file hook-run-submodule/sub/hook.run ) '