Skip to content
24 changes: 24 additions & 0 deletions Documentation/BreakingChanges.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,30 @@ would be significant, we may decide to defer this change to a subsequent minor
release. This evaluation will also take into account our own experience with
how painful it is to keep Rust an optional component.

* The default value of `safe.bareRepository` will change from `all` to
`explicit`. It is all too easy for an attacker to trick a user into cloning a
repository that contains an embedded bare repository with malicious hooks
configured. If the user enters that subdirectory and runs any Git command, Git
discovers the bare repository and the hooks fire. 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 was introduced in 8959555cee (setup_git_directory():
add an owner check for the top-level directory, 2022-03-02) with a default of
`all` to preserve backwards compatibility.
+
Changing the default to `explicit` means that Git will refuse to work with bare
repositories that are discovered implicitly by walking up the directory tree.
Bare repositories specified explicitly via the `--git-dir` command-line option
or the `GIT_DIR` environment variable continue to work regardless of this
setting. Repositories that look like a `.git` directory, a worktree, or a
submodule directory are also unaffected.
+
Users who rely on implicit discovery of bare repositories can restore the
previous behavior by setting `safe.bareRepository=all` in their global or
system configuration.

=== Removals

* Support for grafting commits has long been superseded by git-replace(1).
Expand Down
10 changes: 8 additions & 2 deletions Documentation/config/safe.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,23 @@ safe.bareRepository::
Specifies which bare repositories Git will work with. The currently
supported values are:
+
* `all`: Git works with all bare repositories. This is the default.
* `all`: Git works with all bare repositories. This is the default in
Git 2.x.
* `explicit`: Git only works with bare repositories specified via
the top-level `--git-dir` command-line option, or the `GIT_DIR`
environment variable (see linkgit:git[1]).
environment variable (see linkgit:git[1]). This will be the default
in Git 3.0.
+
If you do not use bare repositories in your workflow, then it may be
beneficial to set `safe.bareRepository` to `explicit` in your global
config. This will protect you from attacks that involve cloning a
repository that contains a bare repository and running a Git command
within that directory.
+
If you use bare repositories regularly and want to preserve the current
behavior after upgrading to Git 3.0, set `safe.bareRepository` to `all`
in your global or system config.
+
This config setting is only respected in protected configuration (see
<<SCOPES>>). This prevents untrusted repositories from tampering with
this value.
Expand Down
4 changes: 4 additions & 0 deletions setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,11 @@ static int allowed_bare_repo_cb(const char *key, const char *value,

static enum allowed_bare_repo get_allowed_bare_repo(void)
{
#ifdef WITH_BREAKING_CHANGES
enum allowed_bare_repo result = ALLOWED_BARE_REPO_EXPLICIT;
#else
enum allowed_bare_repo result = ALLOWED_BARE_REPO_ALL;
#endif
git_protected_config(allowed_bare_repo_cb, &result);
return result;
}
Expand Down
10 changes: 8 additions & 2 deletions t/t0035-safe-bare-repository.sh
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ test_expect_success 'setup an embedded bare repo, secondary worktree and submodu
test_path_is_dir outer-repo/.git/modules/subn
'

test_expect_success 'safe.bareRepository unset' '
test_expect_success !WITH_BREAKING_CHANGES 'safe.bareRepository unset' '
test_unconfig --global safe.bareRepository &&
expect_accepted_implicit -C outer-repo/bare-repo
'

test_expect_success WITH_BREAKING_CHANGES 'safe.bareRepository unset (defaults to explicit)' '
test_unconfig --global safe.bareRepository &&
expect_rejected -C outer-repo/bare-repo
'

test_expect_success 'safe.bareRepository=all' '
test_config_global safe.bareRepository all &&
expect_accepted_implicit -C outer-repo/bare-repo
Expand All @@ -63,7 +68,8 @@ test_expect_success 'safe.bareRepository in the repository' '
# safe.bareRepository must not be "explicit", otherwise
# git config fails with "fatal: not in a git directory" (like
# safe.directory)
test_config -C outer-repo/bare-repo safe.bareRepository all &&
test_when_finished "git config --file outer-repo/bare-repo/config --unset safe.bareRepository" &&
git config --file outer-repo/bare-repo/config safe.bareRepository all &&
test_config_global safe.bareRepository explicit &&
expect_rejected -C outer-repo/bare-repo
'
Expand Down
7 changes: 7 additions & 0 deletions t/t1300-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-terminal.sh

# test-lib.sh may have added global config (e.g. safe.bareRepository)
# that would appear in "git config --list" output and break tests
# that expect exact config contents.
test_expect_success 'remove global config from test-lib.sh' '
test_might_fail git config --global --unset-all safe.bareRepository
'

for mode in legacy subcommands
do

Expand Down
4 changes: 2 additions & 2 deletions t/t1305-config-include.sh
Original file line number Diff line number Diff line change
Expand Up @@ -350,9 +350,9 @@ test_expect_success 'conditional include, onbranch, implicit /** for /' '

test_expect_success 'include cycles are detected' '
git init --bare cycle &&
git -C cycle config include.path cycle &&
git -C cycle --git-dir=. config include.path cycle &&
git config -f cycle/cycle include.path config &&
test_must_fail git -C cycle config --get-all test.value 2>stderr &&
test_must_fail git -C cycle --git-dir=. config --get-all test.value 2>stderr &&
grep "exceeded maximum include depth" stderr
'

Expand Down
4 changes: 4 additions & 0 deletions t/t3000-ls-files-others.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,27 @@ test_expect_success 'setup: expected output' '

test_expect_success 'ls-files --others' '
git ls-files --others >output &&
test_filter_gitconfig output &&
test_cmp expected1 output
'

test_expect_success 'ls-files --others --directory' '
git ls-files --others --directory >output &&
test_filter_gitconfig output &&
test_cmp expected2 output
'

test_expect_success '--no-empty-directory hides empty directory' '
git ls-files --others --directory --no-empty-directory >output &&
test_filter_gitconfig output &&
test_cmp expected3 output
'

test_expect_success 'ls-files --others handles non-submodule .git' '
mkdir not-a-submodule &&
echo foo >not-a-submodule/.git &&
git ls-files -o >output &&
test_filter_gitconfig output &&
test_cmp expected1 output
'

Expand Down
3 changes: 3 additions & 0 deletions t/t3001-ls-files-others-exclude.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
test_filter_gitconfig output &&
test_cmp expect output
'

Expand All @@ -84,6 +85,7 @@ test_expect_success 'git ls-files --others with \r\n line endings.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
test_filter_gitconfig output &&
test_cmp expect output
'

Expand All @@ -99,6 +101,7 @@ test_expect_success 'git ls-files --others with various exclude options.' '
--exclude-per-directory=.gitignore \
--exclude-from=.git/ignore \
>output &&
test_filter_gitconfig output &&
test_cmp expect output
'

Expand Down
2 changes: 2 additions & 0 deletions t/t3002-ls-files-dashpath.sh
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ test_expect_success 'setup' '
test_expect_success 'git ls-files without path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others >output &&
test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
Expand Down Expand Up @@ -63,6 +64,7 @@ test_expect_success 'git ls-files with path restriction with -- --.' '
test_expect_success 'git ls-files with no path restriction.' '
test_when_finished "rm -f expect" &&
git ls-files --others -- >output &&
test_filter_gitconfig output &&
cat >expect <<-\EOF &&
--
-foo
Expand Down
1 change: 1 addition & 0 deletions t/t3009-ls-files-others-nonsubmodule.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ test_expect_success 'setup: directories' '

test_expect_success 'ls-files --others handles untracked git repositories' '
git ls-files -o >output &&
test_filter_gitconfig output &&
cat >expect <<-EOF &&
nonrepo-untracked-file/untracked
output
Expand Down
3 changes: 2 additions & 1 deletion t/t3011-common-prefixes-and-directory-traversal.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ test_expect_success 'setup' '
'

test_expect_success 'git ls-files -o shows the right entries' '
cat <<-EOF >expect &&
cat >expect <<-EOF &&
.gitignore
actual
an_ignored_dir/ignored
Expand All @@ -39,6 +39,7 @@ test_expect_success 'git ls-files -o shows the right entries' '
untracked_repo/
EOF
git ls-files -o >actual &&
test_filter_gitconfig actual &&
test_cmp expect actual
'

Expand Down
4 changes: 3 additions & 1 deletion t/t5601-clone.sh
Original file line number Diff line number Diff line change
Expand Up @@ -813,7 +813,9 @@ test_expect_success 'clone with includeIf' '
test_when_finished "rm -rf repo \"$HTTPD_DOCUMENT_ROOT_PATH/repo.git\"" &&
git clone --bare --no-local src "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&

test_when_finished "rm \"$HOME\"/.gitconfig" &&
test_when_finished "cp \"$HOME\"/.gitconfig.bak \
\"$HOME\"/.gitconfig 2>/dev/null || rm -f \"$HOME\"/.gitconfig" &&
cp "$HOME"/.gitconfig "$HOME"/.gitconfig.bak 2>/dev/null &&
cat >"$HOME"/.gitconfig <<-EOF &&
[includeIf "onbranch:something"]
path = /does/not/exist.inc
Expand Down
3 changes: 1 addition & 2 deletions t/t7060-wtstatus.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME

test_expect_success setup '
git config --global advice.statusuoption false &&
echo "/.gitconfig" >>.git/info/exclude &&
test_commit A &&
test_commit B oneside added &&
git checkout A^0 &&
Expand Down Expand Up @@ -221,7 +222,6 @@ test_expect_success 'status --branch with detached HEAD' '
git status --branch --porcelain >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
?? .gitconfig
?? actual
?? expect
?? expected
Expand All @@ -237,7 +237,6 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
git status --branch --porcelain=v1 >actual &&
cat >expected <<-EOF &&
## HEAD (no branch)
?? .gitconfig
?? actual
?? expect
?? expected
Expand Down
Loading
Loading