Skip to content

Commit 0044a35

Browse files
committed
clone: drop the protections where hooks aren't run
As part of the security bug-fix releases v2.39.4, ..., v2.45.1, I introduced logic to safeguard `git clone` from running hooks that were installed _during_ the clone operation. The rationale was that Git's CVE-2024-32002, CVE-2021-21300, CVE-2019-1354, CVE-2019-1353, CVE-2019-1352, and CVE-2019-1349 should have been low-severity vulnerabilities but were elevated to critical/high severity by the attack vector that allows a weakness where files inside `.git/` can be inadvertently written during a `git clone` to escalate to a Remote Code Execution attack by virtue of installing a malicious `post-checkout` hook that Git will then run at the end of the operation without giving the user a chance to see what code is executed. Unfortunately, Git LFS uses a similar strategy to install its own `post-checkout` hook during a `git clone`; In fact, Git LFS is installing four separate hooks while running the `smudge` filter. While this pattern is probably in want of being improved by introducing better support in Git for Git LFS and other tools wishing to register hooks to be run at various stages of Git's commands, let's undo the clone protections to unbreak Git LFS-enabled clones. This reverts commit 8db1e87 (clone: prevent hooks from running during a clone, 2024-03-28). Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent cd14042 commit 0044a35

File tree

3 files changed

+1
-96
lines changed

3 files changed

+1
-96
lines changed

builtin/clone.c

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -937,8 +937,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
937937
int err = 0, complete_refs_before_fetch = 1;
938938
int submodule_progress;
939939
int filter_submodules = 0;
940-
const char *template_dir;
941-
char *template_dir_dup = NULL;
942940

943941
struct transport_ls_refs_options transport_ls_refs_options =
944942
TRANSPORT_LS_REFS_OPTIONS_INIT;
@@ -958,13 +956,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
958956
usage_msg_opt(_("You must specify a repository to clone."),
959957
builtin_clone_usage, builtin_clone_options);
960958

961-
xsetenv("GIT_CLONE_PROTECTION_ACTIVE", "true", 0 /* allow user override */);
962-
template_dir = get_template_dir(option_template);
963-
if (*template_dir && !is_absolute_path(template_dir))
964-
template_dir = template_dir_dup =
965-
absolute_pathdup(template_dir);
966-
xsetenv("GIT_CLONE_TEMPLATE_DIR", template_dir, 1);
967-
968959
if (option_depth || option_since || option_not.nr)
969960
deepen = 1;
970961
if (option_single_branch == -1)
@@ -1112,7 +1103,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
11121103
}
11131104
}
11141105

1115-
init_db(git_dir, real_git_dir, template_dir, GIT_HASH_UNKNOWN, NULL,
1106+
init_db(git_dir, real_git_dir, option_template, GIT_HASH_UNKNOWN, NULL,
11161107
INIT_DB_QUIET);
11171108

11181109
if (real_git_dir) {
@@ -1430,7 +1421,6 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
14301421
free(unborn_head);
14311422
free(dir);
14321423
free(path);
1433-
free(template_dir_dup);
14341424
UNLEAK(repo);
14351425
junk_mode = JUNK_LEAVE_ALL;
14361426

hook.c

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,6 @@
33
#include "run-command.h"
44
#include "config.h"
55

6-
static int identical_to_template_hook(const char *name, const char *path)
7-
{
8-
const char *env = getenv("GIT_CLONE_TEMPLATE_DIR");
9-
const char *template_dir = get_template_dir(env && *env ? env : NULL);
10-
struct strbuf template_path = STRBUF_INIT;
11-
int found_template_hook, ret;
12-
13-
strbuf_addf(&template_path, "%s/hooks/%s", template_dir, name);
14-
found_template_hook = access(template_path.buf, X_OK) >= 0;
15-
#ifdef STRIP_EXTENSION
16-
if (!found_template_hook) {
17-
strbuf_addstr(&template_path, STRIP_EXTENSION);
18-
found_template_hook = access(template_path.buf, X_OK) >= 0;
19-
}
20-
#endif
21-
if (!found_template_hook) {
22-
strbuf_release(&template_path);
23-
return 0;
24-
}
25-
26-
ret = do_files_match(template_path.buf, path);
27-
28-
strbuf_release(&template_path);
29-
return ret;
30-
}
31-
326
const char *find_hook(const char *name)
337
{
348
static struct strbuf path = STRBUF_INIT;
@@ -64,14 +38,6 @@ const char *find_hook(const char *name)
6438
}
6539
return NULL;
6640
}
67-
if (!git_hooks_path && git_env_bool("GIT_CLONE_PROTECTION_ACTIVE", 0) &&
68-
!identical_to_template_hook(name, path.buf))
69-
die(_("active `%s` hook found during `git clone`:\n\t%s\n"
70-
"For security reasons, this is disallowed by default.\n"
71-
"If this is intentional and the hook should actually "
72-
"be run, please\nrun the command again with "
73-
"`GIT_CLONE_PROTECTION_ACTIVE=false`"),
74-
name, path.buf);
7541
return path.buf;
7642
}
7743

t/t5601-clone.sh

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -771,57 +771,6 @@ test_expect_success 'batch missing blob request does not inadvertently try to fe
771771
git clone --filter=blob:limit=0 "file://$(pwd)/server" client
772772
'
773773

774-
test_expect_success 'clone with init.templatedir runs hooks' '
775-
git init tmpl/hooks &&
776-
write_script tmpl/hooks/post-checkout <<-EOF &&
777-
echo HOOK-RUN >&2
778-
echo I was here >hook.run
779-
EOF
780-
git -C tmpl/hooks add . &&
781-
test_tick &&
782-
git -C tmpl/hooks commit -m post-checkout &&
783-
784-
test_when_finished "git config --global --unset init.templateDir || :" &&
785-
test_when_finished "git config --unset init.templateDir || :" &&
786-
(
787-
sane_unset GIT_TEMPLATE_DIR &&
788-
NO_SET_GIT_TEMPLATE_DIR=t &&
789-
export NO_SET_GIT_TEMPLATE_DIR &&
790-
791-
git -c core.hooksPath="$(pwd)/tmpl/hooks" \
792-
clone tmpl/hooks hook-run-hookspath 2>err &&
793-
! grep "active .* hook found" err &&
794-
test_path_is_file hook-run-hookspath/hook.run &&
795-
796-
git -c init.templateDir="$(pwd)/tmpl" \
797-
clone tmpl/hooks hook-run-config 2>err &&
798-
! grep "active .* hook found" err &&
799-
test_path_is_file hook-run-config/hook.run &&
800-
801-
git clone --template=tmpl tmpl/hooks hook-run-option 2>err &&
802-
! grep "active .* hook found" err &&
803-
test_path_is_file hook-run-option/hook.run &&
804-
805-
git config --global init.templateDir "$(pwd)/tmpl" &&
806-
git clone tmpl/hooks hook-run-global-config 2>err &&
807-
git config --global --unset init.templateDir &&
808-
! grep "active .* hook found" err &&
809-
test_path_is_file hook-run-global-config/hook.run &&
810-
811-
# clone ignores local `init.templateDir`; need to create
812-
# a new repository because we deleted `.git/` in the
813-
# `setup` test case above
814-
git init local-clone &&
815-
cd local-clone &&
816-
817-
git config init.templateDir "$(pwd)/../tmpl" &&
818-
git clone ../tmpl/hooks hook-run-local-config 2>err &&
819-
git config --unset init.templateDir &&
820-
! grep "active .* hook found" err &&
821-
test_path_is_missing hook-run-local-config/hook.run
822-
)
823-
'
824-
825774
. "$TEST_DIRECTORY"/lib-httpd.sh
826775
start_httpd
827776

0 commit comments

Comments
 (0)