Skip to content

Commit 54b6c08

Browse files
committed
safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES
When an attacker can convince a user to clone a crafted repository that contains an embedded bare repository with malicious hooks, any Git command the user runs after entering that subdirectory will discover the bare repository and execute the hooks. The user does not even need to run a Git command explicitly: many shell prompts run `git status` in the background to display branch and dirty state information, and `git status` in turn may invoke the fsmonitor hook if so configured, making the user vulnerable the moment they `cd` into the directory. The safe.bareRepository configuration variable (introduced in 8959555 (setup_git_directory(): add an owner check for the top-level directory, 2022-03-02)) already provides protection against this attack vector by allowing users to set it to "explicit", but the default remained "all" for backwards compatibility. Since Git 3.0 is the natural point to change defaults to safer values, flip the default from "all" to "explicit" when built with WITH_BREAKING_CHANGES. This means Git will refuse to work with bare repositories that are discovered implicitly by walking up the directory tree. Bare repositories specified via --git-dir or GIT_DIR continue to work, and directories that look like .git, worktrees, or submodule directories are unaffected (the existing is_implicit_bare_repo() whitelist handles those cases). Users who rely on implicit bare repository discovery can restore the previous behavior by setting safe.bareRepository=all in their global or system configuration. The test for the "safe.bareRepository in the repository" scenario needed a more involved fix: it writes a safe.bareRepository=all entry into the bare repository's own config to verify that repo-local config does not override the protected (global) setting. Previously, test_config -C was used to write that entry, but its cleanup runs git -C <bare-repo> config --unset, which itself fails when the default is "explicit" and the global config has already been cleaned up. Switching to direct git config --file access avoids going through repository discovery entirely. Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
1 parent 6e8d538 commit 54b6c08

File tree

4 files changed

+44
-4
lines changed

4 files changed

+44
-4
lines changed

Documentation/BreakingChanges.adoc

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ would be significant, we may decide to defer this change to a subsequent minor
216216
release. This evaluation will also take into account our own experience with
217217
how painful it is to keep Rust an optional component.
218218

219+
* The default value of `safe.bareRepository` will change from `all` to
220+
`explicit`. It is all too easy for an attacker to trick a user into cloning a
221+
repository that contains an embedded bare repository with malicious hooks
222+
configured. If the user enters that subdirectory and runs any Git command, Git
223+
discovers the bare repository and the hooks fire. The user does not even need
224+
to run a Git command explicitly: many shell prompts run `git status` in the
225+
background to display branch and dirty state information, and `git status` in
226+
turn may invoke the fsmonitor hook if so configured, making the user
227+
vulnerable the moment they `cd` into the directory. The `safe.bareRepository`
228+
configuration variable was introduced in 8959555cee (setup_git_directory():
229+
add an owner check for the top-level directory, 2022-03-02) with a default of
230+
`all` to preserve backwards compatibility.
231+
+
232+
Changing the default to `explicit` means that Git will refuse to work with bare
233+
repositories that are discovered implicitly by walking up the directory tree.
234+
Bare repositories specified explicitly via the `--git-dir` command-line option
235+
or the `GIT_DIR` environment variable continue to work regardless of this
236+
setting. Repositories that look like a `.git` directory, a worktree, or a
237+
submodule directory are also unaffected.
238+
+
239+
Users who rely on implicit discovery of bare repositories can restore the
240+
previous behavior by setting `safe.bareRepository=all` in their global or
241+
system configuration.
242+
219243
=== Removals
220244

221245
* Support for grafting commits has long been superseded by git-replace(1).

Documentation/config/safe.adoc

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,23 @@ safe.bareRepository::
22
Specifies which bare repositories Git will work with. The currently
33
supported values are:
44
+
5-
* `all`: Git works with all bare repositories. This is the default.
5+
* `all`: Git works with all bare repositories. This is the default in
6+
Git 2.x.
67
* `explicit`: Git only works with bare repositories specified via
78
the top-level `--git-dir` command-line option, or the `GIT_DIR`
8-
environment variable (see linkgit:git[1]).
9+
environment variable (see linkgit:git[1]). This will be the default
10+
in Git 3.0.
911
+
1012
If you do not use bare repositories in your workflow, then it may be
1113
beneficial to set `safe.bareRepository` to `explicit` in your global
1214
config. This will protect you from attacks that involve cloning a
1315
repository that contains a bare repository and running a Git command
1416
within that directory.
1517
+
18+
If you use bare repositories regularly and want to preserve the current
19+
behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
20+
in your global or system config.
21+
+
1622
This config setting is only respected in protected configuration (see
1723
<<SCOPES>>). This prevents untrusted repositories from tampering with
1824
this value.

setup.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,7 +1485,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,
14851485

14861486
static enum allowed_bare_repo get_allowed_bare_repo(void)
14871487
{
1488+
#ifdef WITH_BREAKING_CHANGES
1489+
enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
1490+
#else
14881491
enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
1492+
#endif
14891493
git_protected_config(allowed_bare_repo_cb, &result);
14901494
return result;
14911495
}

t/t0035-safe-bare-repository.sh

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,16 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
4444
test_path_is_dir outer-repo/.git/modules/subn
4545
'
4646

47-
test_expect_success 'safe.bareRepository unset' '
47+
test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
4848
test_unconfig --global safe.bareRepository &&
4949
expect_accepted_implicit -C outer-repo/bare-repo
5050
'
5151

52+
test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
53+
test_unconfig --global safe.bareRepository &&
54+
expect_rejected -C outer-repo/bare-repo
55+
'
56+
5257
test_expect_success 'safe.bareRepository=all' '
5358
test_config_global safe.bareRepository all &&
5459
expect_accepted_implicit -C outer-repo/bare-repo
@@ -63,7 +68,8 @@ test_expect_success 'safe.bareRepository in the repository' '
6368
# safe.bareRepository must not be "explicit", otherwise
6469
# git config fails with "fatal: not in a git directory" (like
6570
# safe.directory)
66-
test_config -C outer-repo/bare-repo safe.bareRepository all &&
71+
test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
72+
git config --file outer-repo/bare-repo/config safe.bareRepository all &&
6773
test_config_global safe.bareRepository explicit &&
6874
expect_rejected -C outer-repo/bare-repo
6975
'

0 commit comments

Comments
 (0)