Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
4 changes: 2 additions & 2 deletions t/annotate-tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ test_expect_success 'blame with --contents' '
test_expect_success 'blame with --contents in a bare repo' '
git clone --bare . bare-contents.git &&
(
cd bare-contents.git &&
cd bare-contents.git && GIT_DIR=. && export GIT_DIR &&
echo "1A quick brown fox jumps over the" >contents &&
check_count --contents=contents A 1
)
Expand All @@ -101,7 +101,7 @@ test_expect_success 'blame with --contents changed' '
test_expect_success 'blame in a bare repo without starting commit' '
git clone --bare . bare.git &&
(
cd bare.git &&
cd bare.git && GIT_DIR=. && export GIT_DIR &&
check_count A 2
)
'
Expand Down
8 changes: 7 additions & 1 deletion t/helper/test-tool.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
#include "test-tool-utils.h"
#include "trace2.h"
#include "parse-options.h"
#include "environment.h"

static const char * const test_tool_usage[] = {
"test-tool [-C <directory>] <command [<arguments>...]]",
"test-tool [-C <directory>] [--git-dir=<path>] <command [<arguments>...]]",
NULL
};

Expand Down Expand Up @@ -107,9 +108,12 @@ static NORETURN void die_usage(void)
int cmd_main(int argc, const char **argv)
{
const char *working_directory = NULL;
const char *git_dir = NULL;
struct option options[] = {
OPT_STRING('C', NULL, &working_directory, "directory",
"change the working directory"),
OPT_STRING(0, "git-dir", &git_dir, "path",
"set the path to the repository"),
OPT_END()
};

Expand All @@ -123,6 +127,8 @@ int cmd_main(int argc, const char **argv)

if (working_directory && chdir(working_directory) < 0)
die("Could not cd to '%s'", working_directory);
if (git_dir)
setenv(GIT_DIR_ENVIRONMENT, git_dir, 1);

for (size_t i = 0; i < ARRAY_SIZE(cmds); i++) {
if (!strcmp(cmds[i].name, argv[1])) {
Expand Down
2 changes: 1 addition & 1 deletion t/lib-bitmap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ basic_bitmap_tests () {
rm -fr partial-clone.git &&
git clone --no-local --bare --filter=blob:none . partial-clone.git &&
(
cd partial-clone.git &&
cd partial-clone.git && GIT_DIR=. && export GIT_DIR &&
pack=$(echo objects/pack/*.pack) &&
git verify-pack -v "$pack" >have &&
awk "/blob/ { print \$1 }" <have >blobs &&
Expand Down
32 changes: 27 additions & 5 deletions t/lib-commit-graph.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,23 +22,41 @@ graph_git_two_modes() {
# NOTE: it is a bug to call this function with <directory> containing
# any characters in $IFS.
graph_git_behavior() {
BARE=
if test "$1" = "--bare"
then
BARE=t
shift
fi
MSG=$1
DIR=$2
BRANCH=$3
COMPARE=$4
test_expect_success "check normal git operations: $MSG" '
graph_git_two_modes "${DIR:+-C $DIR} log --oneline $BRANCH" &&
graph_git_two_modes "${DIR:+-C $DIR} log --topo-order $BRANCH" &&
graph_git_two_modes "${DIR:+-C $DIR} log --graph $COMPARE..$BRANCH" &&
graph_git_two_modes "${DIR:+-C $DIR} branch -vv" &&
graph_git_two_modes "${DIR:+-C $DIR} merge-base -a $BRANCH $COMPARE"
if test -n "$BARE"
then
DIR_ARGS="${DIR:+--git-dir=$DIR}"
else
DIR_ARGS="${DIR:+-C $DIR}"
fi &&
graph_git_two_modes "$DIR_ARGS log --oneline $BRANCH" &&
graph_git_two_modes "$DIR_ARGS log --topo-order $BRANCH" &&
graph_git_two_modes "$DIR_ARGS log --graph $COMPARE..$BRANCH" &&
graph_git_two_modes "$DIR_ARGS branch -vv" &&
graph_git_two_modes "$DIR_ARGS merge-base -a $BRANCH $COMPARE"
'
}

graph_read_expect() {
OPTIONAL=""
NUM_CHUNKS=3
DIR="."
BARE=
if test "$1" = "--bare"
then
BARE=t
shift
fi
if test "$1" = -C
then
shift
Expand Down Expand Up @@ -68,6 +86,10 @@ graph_read_expect() {
EOF
(
cd "$DIR" &&
if test -n "$BARE"
then
GIT_DIR=. && export GIT_DIR
fi &&
test-tool read-graph >output &&
test_cmp expect output
)
Expand Down
4 changes: 2 additions & 2 deletions t/lib-diff-alternative.sh
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,14 @@ EOF
'

test_expect_success "diff from attributes with bare repo with source" '
git -C bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
git --git-dir=bare.git --attr-source=branchA -c diff.driver.algorithm=myers \
-c diff.driverA.algorithm=$STRATEGY \
diff HEAD:file1 HEAD:file2 >output &&
test_cmp expect output
'

test_expect_success "diff from attributes with bare repo with invalid source" '
test_must_fail git -C bare.git --attr-source=invalid-branch diff \
test_must_fail git --git-dir=bare.git --attr-source=invalid-branch diff \
HEAD:file1 HEAD:file2
'

Expand Down
32 changes: 16 additions & 16 deletions t/lib-proto-disable.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test_allow_var () {

test_expect_success "fetch $desc (enabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_ALLOW_PROTOCOL=$proto &&
export GIT_ALLOW_PROTOCOL &&
git fetch
Expand All @@ -26,7 +26,7 @@ test_allow_var () {

test_expect_success "push $desc (enabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_ALLOW_PROTOCOL=$proto &&
export GIT_ALLOW_PROTOCOL &&
git push origin HEAD:pushed
Expand All @@ -35,7 +35,7 @@ test_allow_var () {

test_expect_success "push $desc (disabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_ALLOW_PROTOCOL=none &&
export GIT_ALLOW_PROTOCOL &&
test_must_fail git push origin HEAD:pushed
Expand All @@ -44,7 +44,7 @@ test_allow_var () {

test_expect_success "fetch $desc (disabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_ALLOW_PROTOCOL=none &&
export GIT_ALLOW_PROTOCOL &&
test_must_fail git fetch
Expand Down Expand Up @@ -83,19 +83,19 @@ test_config () {
'

test_expect_success "fetch $desc (enabled)" '
git -C tmp.git -c protocol.$proto.allow=always fetch
git --git-dir=tmp.git -c protocol.$proto.allow=always fetch
'

test_expect_success "push $desc (enabled)" '
git -C tmp.git -c protocol.$proto.allow=always push origin HEAD:pushed
git --git-dir=tmp.git -c protocol.$proto.allow=always push origin HEAD:pushed
'

test_expect_success "push $desc (disabled)" '
test_must_fail git -C tmp.git -c protocol.$proto.allow=never push origin HEAD:pushed
test_must_fail git --git-dir=tmp.git -c protocol.$proto.allow=never push origin HEAD:pushed
'

test_expect_success "fetch $desc (disabled)" '
test_must_fail git -C tmp.git -c protocol.$proto.allow=never fetch
test_must_fail git --git-dir=tmp.git -c protocol.$proto.allow=never fetch
'

test_expect_success "clone $desc (disabled)" '
Expand All @@ -110,16 +110,16 @@ test_config () {
'

test_expect_success "fetch $desc (enabled)" '
git -C tmp.git -c protocol.$proto.allow=user fetch
git --git-dir=tmp.git -c protocol.$proto.allow=user fetch
'

test_expect_success "push $desc (enabled)" '
git -C tmp.git -c protocol.$proto.allow=user push origin HEAD:pushed
git --git-dir=tmp.git -c protocol.$proto.allow=user push origin HEAD:pushed
'

test_expect_success "push $desc (disabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_PROTOCOL_FROM_USER=0 &&
export GIT_PROTOCOL_FROM_USER &&
test_must_fail git -c protocol.$proto.allow=user push origin HEAD:pushed
Expand All @@ -128,7 +128,7 @@ test_config () {

test_expect_success "fetch $desc (disabled)" '
(
cd tmp.git &&
cd tmp.git && GIT_DIR=. && export GIT_DIR &&
GIT_PROTOCOL_FROM_USER=0 &&
export GIT_PROTOCOL_FROM_USER &&
test_must_fail git -c protocol.$proto.allow=user fetch
Expand All @@ -153,22 +153,22 @@ test_config () {

test_expect_success "fetch $desc (enabled)" '
test_config_global protocol.allow always &&
git -C tmp.git fetch
git --git-dir=tmp.git fetch
'

test_expect_success "push $desc (enabled)" '
test_config_global protocol.allow always &&
git -C tmp.git push origin HEAD:pushed
git --git-dir=tmp.git push origin HEAD:pushed
'

test_expect_success "push $desc (disabled)" '
test_config_global protocol.allow never &&
test_must_fail git -C tmp.git push origin HEAD:pushed
test_must_fail git --git-dir=tmp.git push origin HEAD:pushed
'

test_expect_success "fetch $desc (disabled)" '
test_config_global protocol.allow never &&
test_must_fail git -C tmp.git fetch
test_must_fail git --git-dir=tmp.git fetch
'

test_expect_success "clone $desc (disabled)" '
Expand Down
7 changes: 4 additions & 3 deletions t/t0001-init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ check_config () {
return 1
fi

bare=$(cd "$1" && git config --bool core.bare)
worktree=$(cd "$1" && git config core.worktree) ||
bare=$(git --git-dir="$1" config --bool core.bare)
worktree=$(git --git-dir="$1" config core.worktree) ||
worktree=unset

test "$bare" = "$2" && test "$worktree" = "$3" || {
Expand Down Expand Up @@ -77,6 +77,7 @@ test_expect_success 'plain nested through aliased command' '
'

test_expect_success 'plain nested in bare through aliased command' '
test_config_global safe.bareRepository all &&
(
git init --bare bare-ancestor-aliased.git &&
cd bare-ancestor-aliased.git &&
Expand Down Expand Up @@ -346,7 +347,7 @@ test_expect_success 'bare & --separate-git-dir incompatible within worktree' '
test_when_finished "rm -rf bare.git linkwt seprepo" &&
test_commit gumby &&
git clone --bare . bare.git &&
git -C bare.git worktree add --detach ../linkwt &&
git --git-dir=bare.git worktree add --detach linkwt &&
test_must_fail git -C linkwt init --separate-git-dir seprepo 2>err &&
test_grep "incompatible" err
'
Expand Down
Loading
Loading