summaryrefslogtreecommitdiffstats
path: root/hook.c
diff options
context:
space:
mode:
authorJohannes Schindelin <johannes.schindelin@gmx.de>2024-03-28 19:21:06 +0100
committerJohannes Schindelin <johannes.schindelin@gmx.de>2024-04-19 12:38:23 +0200
commit8db1e8743c0f1ed241f6a1b8bf55b6fef07d6751 (patch)
tree271bcbf04a20143fe57abd04110239e8ef08df7b /hook.c
parentAdd a helper function to compare file contents (diff)
downloadgit-8db1e8743c0f1ed241f6a1b8bf55b6fef07d6751.tar.xz
git-8db1e8743c0f1ed241f6a1b8bf55b6fef07d6751.zip
clone: prevent hooks from running during a clone
Critical security issues typically combine relatively common vulnerabilities such as case confusion in file paths with other weaknesses in order to raise the severity of the attack. One such weakness that has haunted the Git project in many a submodule-related CVE is that any hooks that are found are executed during a clone operation. Examples are the `post-checkout` and `fsmonitor` hooks. However, Git's design calls for hooks to be disabled by default, as only disabled example hooks are copied over from the templates in `<prefix>/share/git-core/templates/`. As a defense-in-depth measure, let's prevent those hooks from running. Obviously, administrators can choose to drop enabled hooks into the template directory, though, _and_ it is also possible to override `core.hooksPath`, in which case the new check needs to be disabled. Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Diffstat (limited to 'hook.c')
-rw-r--r--hook.c32
1 files changed, 32 insertions, 0 deletions
diff --git a/hook.c b/hook.c
index 22b274b60b..632b537b99 100644
--- a/hook.c
+++ b/hook.c
@@ -3,6 +3,30 @@
#include "run-command.h"
#include "config.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)
+ return 0;
+
+ ret = do_files_match(template_path.buf, path);
+
+ strbuf_release(&template_path);
+ return ret;
+}
+
const char *find_hook(const char *name)
{
static struct strbuf path = STRBUF_INIT;
@@ -38,6 +62,14 @@ 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))
+ die(_("active `%s` hook found during `git clone`:\n\t%s\n"
+ "For security reasons, this is disallowed by default.\n"
+ "If this is intentional and the hook should actually "
+ "be run, please\nrun the command again with "
+ "`GIT_CLONE_PROTECTION_ACTIVE=false`"),
+ name, path.buf);
return path.buf;
}