Skip to content

Commit 30b7c4b

Browse files
committed
setup: notice more types of implicit bare repositories
Setting the safe.bareRepository configuration variable to explicit stops git from using a bare repository, unless the repository is explicitly specified, either by the "--git-dir=<path>" command line option, or by exporting $GIT_DIR environment variable. This may be a reasonable measure to safeguard users from accidentally straying into a bare repository in unexpected places, but often gets in the way of users who need valid accesses to the repository. Earlier, 45bb9162 (setup: allow cwd=.git w/ bareRepository=explicit, 2024-01-20) loosened the rule such that being inside the ".git" directory of a non-bare repository does not really count as accessing a "bare" repository. The reason why such a loosening is needed is because often hooks and third-party tools run from within $GIT_DIR while working with a non-bare repository. More importantly, the reason why this is safe is because a directory whose contents look like that of a "bare" repository cannot be a bare repository that came embedded within a checkout of a malicious project, as long as its directory name is ".git", because ".git" is not a name allowed for a directory in payload. There are at least two other cases where tools have to work in a bare-repository looking directory that is not an embedded bare repository, and accesses to them are still not allowed by the recent change. - A secondary worktree (whose name is $name) has its $GIT_DIR inside "worktrees/$name/" subdirectory of the $GIT_DIR of the primary worktree of the same repository. - A submodule worktree (whose name is $name) has its $GIT_DIR inside "modules/$name/" subdirectory of the $GIT_DIR of its superproject. As long as the primary worktree or the superproject in these cases are not bare, the pathname of these "looks like bare but not really" directories will have "/.git/worktrees/" and "/.git/modules/" as a substring in its leading part, and we can take advantage of the same security guarantee allow git to work from these places. Extend the earlier "in a directory called '.git' we are OK" logic used for the primary worktree to also cover the secondary worktree's and non-embedded submodule's $GIT_DIR, by moving the logic to a helper function "is_implicit_bare_repo()". We deliberately exclude secondary worktrees and submodules of a bare repository, as these are exactly what safe.bareRepository=explicit setting is designed to forbid accesses to without an explicit GIT_DIR/--git-dir=<path> Helped-by: Kyle Lippincott <[email protected]> Helped-by: Kyle Meyer <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 45bb916 commit 30b7c4b

File tree

2 files changed

+49
-5
lines changed

2 files changed

+49
-5
lines changed

setup.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1231,6 +1231,32 @@ static const char *allowed_bare_repo_to_string(
12311231
return NULL;
12321232
}
12331233

1234+
static int is_implicit_bare_repo(const char *path)
1235+
{
1236+
/*
1237+
* what we found is a ".git" directory at the root of
1238+
* the working tree.
1239+
*/
1240+
if (ends_with_path_components(path, ".git"))
1241+
return 1;
1242+
1243+
/*
1244+
* we are inside $GIT_DIR of a secondary worktree of a
1245+
* non-bare repository.
1246+
*/
1247+
if (strstr(path, "/.git/worktrees/"))
1248+
return 1;
1249+
1250+
/*
1251+
* we are inside $GIT_DIR of a worktree of a non-embedded
1252+
* submodule, whose superproject is not a bare repository.
1253+
*/
1254+
if (strstr(path, "/.git/modules/"))
1255+
return 1;
1256+
1257+
return 0;
1258+
}
1259+
12341260
/*
12351261
* We cannot decide in this function whether we are in the work tree or
12361262
* not, since the config can only be read _after_ this function was called.
@@ -1360,7 +1386,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
13601386
if (is_git_directory(dir->buf)) {
13611387
trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf);
13621388
if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT &&
1363-
!ends_with_path_components(dir->buf, ".git"))
1389+
!is_implicit_bare_repo(dir->buf))
13641390
return GIT_DIR_DISALLOWED_BARE;
13651391
if (!ensure_valid_ownership(NULL, NULL, dir->buf, report))
13661392
return GIT_DIR_INVALID_OWNERSHIP;

t/t0035-safe-bare-repository.sh

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,20 @@ expect_rejected () {
2929
grep -F "implicit-bare-repository:$pwd" "$pwd/trace.perf"
3030
}
3131

32-
test_expect_success 'setup bare repo in worktree' '
32+
test_expect_success 'setup an embedded bare repo, secondary worktree and submodule' '
3333
git init outer-repo &&
34-
git init --bare outer-repo/bare-repo
34+
git init --bare --initial-branch=main outer-repo/bare-repo &&
35+
git -C outer-repo worktree add ../outer-secondary &&
36+
test_path_is_dir outer-secondary &&
37+
(
38+
cd outer-repo &&
39+
test_commit A &&
40+
git push bare-repo +HEAD:refs/heads/main &&
41+
git -c protocol.file.allow=always \
42+
submodule add --name subn -- ./bare-repo subd
43+
) &&
44+
test_path_is_dir outer-repo/.git/worktrees/outer-secondary &&
45+
test_path_is_dir outer-repo/.git/modules/subn
3546
'
3647

3748
test_expect_success 'safe.bareRepository unset' '
@@ -53,8 +64,7 @@ test_expect_success 'safe.bareRepository in the repository' '
5364
# safe.bareRepository must not be "explicit", otherwise
5465
# git config fails with "fatal: not in a git directory" (like
5566
# safe.directory)
56-
test_config -C outer-repo/bare-repo safe.bareRepository \
57-
all &&
67+
test_config -C outer-repo/bare-repo safe.bareRepository all &&
5868
test_config_global safe.bareRepository explicit &&
5969
expect_rejected -C outer-repo/bare-repo
6070
'
@@ -86,4 +96,12 @@ test_expect_success 'no trace when "bare repository" is a subdir of .git' '
8696
expect_accepted_implicit -C outer-repo/.git/objects
8797
'
8898

99+
test_expect_success 'no trace in $GIT_DIR of secondary worktree' '
100+
expect_accepted_implicit -C outer-repo/.git/worktrees/outer-secondary
101+
'
102+
103+
test_expect_success 'no trace in $GIT_DIR of a submodule' '
104+
expect_accepted_implicit -C outer-repo/.git/modules/subn
105+
'
106+
89107
test_done

0 commit comments

Comments
 (0)