safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES#2072
Open
dscho wants to merge 11 commits intogitgitgadget:masterfrom
Open
safe.bareRepository: default to "explicit" with WITH_BREAKING_CHANGES#2072dscho wants to merge 11 commits intogitgitgadget:masterfrom
dscho wants to merge 11 commits intogitgitgadget:masterfrom
Conversation
It is all too easy for an attacker to embed a bare repository with
malicious hooks inside a cloned project. Even a seemingly harmless
shell prompt calling `git status` would then trigger those hooks via
fsmonitor. The `safe.bareRepository` setting, when set to `explicit`,
defends against this class of attack, and the test suite should be
ready for the day this becomes the default, most likely in Git v3.0.
The `test_hook` helper function (which gained `--git-dir` support in
a preceding commit) is used throughout the test suite to install hooks
into repositories. When the target is a bare repository, `test_hook
-C <bare>` fails under `safe.bareRepository=explicit` because the
`-C` flag triggers implicit bare repository discovery. Replace all
such calls with `test_hook --git-dir <bare>`.
Some diff hunks also contain adjacent `git -C` to `git --git-dir`
conversions for commands like `test_cmp_refs`, `git config`, or `git
update-ref` that happen to appear in the same hunk as the `test_hook`
change. These are the same kind of fix and would be difficult to
separate into individual hunks.
This commit was produced with the help of Claude Opus 4.6 acting as a
sophisticated keyboard. It was told to replace every `test_hook -C`
with `test_hook --git-dir` (and likewise for `git -C` and
`test_cmp_refs -C` in the same hunks). The dominant pattern can be
verified:
git show HEAD |
sed 's/test_hook --git-dir/test_hook -C/g' |
sed -ne '/^diff/,/@@/d' -e 's/^[-+]//p' |
uniq -u
Any residual output consists of the adjacent `git -C` to
`--git-dir` conversions mentioned above.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Even if the Git community ultimately decides not to change the default of `safe.bareRepository`, it is good test hygiene to have a test suite that would survive such a change unscathed. A preceding commit taught `test-tool` a `--git-dir` option (mirroring what `git` itself does: setting `GIT_DIR` in the environment). This commit puts it to use. Replace `test-tool -C <bare>` invocations with `test-tool --git-dir=<bare>` so that bare repositories are accessed explicitly rather than through implicit discovery. In t1460-refs-migrate.sh, this simplifies the `print_all_reflog_entries` helper considerably: the previous version needed an if/else branch to dispatch between `-C` and a `GIT_DIR=` prefix, whereas now both `test-tool` and `git` accept the same `--git-dir` flag and the helper can pass `$repo_flag` through uniformly. Some diff hunks also contain adjacent `git -C` to `git --git-dir` conversions that share the same hunk as the `test-tool` change. This commit was produced with the help of Claude Opus 4.6 acting as a sophisticated keyboard. It was told to replace `test-tool -C <bare>` with `test-tool --git-dir=<bare>` and to simplify the surrounding control flow where the new flag made branching unnecessary. Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
If the `safe.bareRepository` default were to change to `explicit` one
day (as is being discussed for Git v3.0), the test suite must not
silently break in places that configure bare repositories via
`test_config -C <bare>`. The `-C` flag triggers implicit discovery
which would then be rejected. A preceding commit taught `test_config`
and `test_unconfig` a `--git-dir` option so that bare repository
configuration can be set and cleaned up with the same familiar
one-liner used everywhere else. This commit converts all call sites.
Replace `test_config -C <bare>` with `test_config --git-dir <bare>`.
The cleanup registered via `test_when_finished` now runs `test_unconfig
--git-dir`, which correctly uses `--unset-all` and handles exit code 5
(nothing to unset), matching the robust behavior that `test_unconfig`
has always provided.
Some diff hunks also contain adjacent `git -C` to `git --git-dir`
conversions for commands that appear in the same test and therefore the
same hunk as the `test_config` change.
This commit was produced with the help of Claude Opus 4.6 acting as a
sophisticated keyboard. It was told to replace `test_config -C` with
`test_config --git-dir` across bare-repository test sites, and to
convert any hand-rolled `test_when_finished` + `git config --unset`
pairs back into the `test_config` abstraction. The dominant pattern can
be verified:
git show HEAD |
sed 's/test_config --git-dir/test_config -C/g' |
sed -ne '/^diff/,/@@/d' -e 's/^[-+]//p' |
uniq -u
Residual output consists of adjacent `git -C` to `--git-dir`
conversions and, in t1400, a restructured `test_when_finished` that
splits config cleanup from reflog deletion.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
When a test invokes `git -C <bare-repo>`, Git enters the directory and discovers the bare repository implicitly. If `safe.bareRepository` is set to `explicit`, this implicit discovery is refused and the test fails. Regardless of whether the default changes in Git v3.0 or later, it is the right thing to harden Git's own test suite so that it would still pass under the stricter setting: the tests should demonstrate the recommended practice, not rely on the lenient default. Replace `git -C <bare>` with `git --git-dir=<bare>` throughout the test suite wherever the target is a bare repository. The `--git-dir` flag tells Git exactly where the repository is, bypassing the discovery mechanism entirely. A handful of hunks additionally collapse subshells that used `cd <bare> && git ...` into a single `git --git-dir=<bare> ...` invocation, and several shared test helpers (lib-commit-graph.sh, lib-diff-alternative.sh, lib-proto-disable.sh, t5411/common-functions.sh) gain `--bare` or `--git-dir` parameters to pass through to `git` internally. Note that `--git-dir` does not change the working directory the way `-C` does: relative paths in arguments are resolved from the original directory, not from inside the repository. In this commit, no path adjustments were necessary (those are handled in a separate commit). This commit was produced with the help of Claude Opus 4.6 acting as a sophisticated keyboard. It was told to replace every `git -C <bare>` with `git --git-dir=<bare>` where the target is known to be a bare repository, and to adjust helper functions in shared test libraries accordingly. Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
A companion commit replaced `git -C <bare>` with `git --git-dir=<bare>` for simple cases. This commit handles the cases where that substitution also requires adjusting relative paths in command arguments. The `-C` flag changes the working directory before executing the command, so arguments like `../foo` are resolved relative to the repository. The `--git-dir` flag does not change the working directory, so the same path must be expressed relative to the original directory instead, e.g. `foo` rather than `../foo`, or `c/.git` rather than `../c/.git`. Each hunk in this commit is therefore specific to its test, making the changes not amenable to a single mechanical verification command. This commit was produced with the help of Claude Opus 4.6 acting as a sophisticated keyboard. It was told to replace `git -C <bare>` with `git --git-dir=<bare>` and to adjust every relative path that the `-C` flag would have resolved differently. The affected files are: t0001-init.sh, t0003-attributes.sh, t1091-sparse-checkout-builtin.sh, t2400-worktree-add.sh, t2402-worktree-list.sh, t3200-branch.sh, t5504-fetch-receive-strict.sh, t6020-bundle-misc.sh, t6500-gc.sh, t7412-submodule-absorbgitdirs.sh, and t9003-help-autocorrect.sh. Assisted-by: Claude Opus 4.6 Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Some tests need to `cd` into a bare repository rather than address it
from outside, typically because they create files there, inspect the
object store, or call helpers that assume they are already inside the
repository. For these, the `--git-dir` approach used by the companion
commits is not applicable: the test genuinely needs its working
directory to be inside the repository.
The solution is to set and export `GIT_DIR=.` immediately after
entering the bare repository. This tells Git explicitly where the
repository is and prevents the implicit discovery that
`safe.bareRepository=explicit` would reject. Should the default ever
change (Git v3.0 is the most likely candidate), these tests will
continue to work without modification.
A few cases deserve special mention. In t1400-update-ref.sh, the test
framework variable `$bare` points to the bare repository and the test
sets `GIT_DIR=$bare` with a `cd $bare` plus a later `cd -` and
`sane_unset GIT_DIR` for cleanup. In t2400-worktree-add.sh, some
subshells use the shorter inline form `GIT_DIR=. git worktree add ...`
where only a single command needs the override. In
t5411/common-functions.sh, the change affects the shared setup used by
the entire proc-receive test family. In lib-proto-disable.sh and
lib-bitmap.sh, the change is inside shared test library functions that
are sourced by multiple test scripts.
This commit was produced with the help of Claude Opus 4.6 acting as a
sophisticated keyboard. It was told to add `GIT_DIR=. && export
GIT_DIR &&` after every `cd <bare> &&` inside a subshell that runs Git
commands on a bare repository. The dominant pattern can be verified:
git show HEAD |
sed 's/ GIT_DIR=\. && export GIT_DIR &&//' |
sed -ne '/^diff/,/@@/d' -e 's/^[-+]//p' |
uniq -u
Residual output corresponds to structural adjustments (subshell
changes, inline `GIT_DIR=.` prefix form, cleanup via `sane_unset`) and
adjacent `git -C` to `--git-dir` conversions that share the same hunk.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Several tests enter the `.git` directory (or a bare repository)
directly via `cd .git` or `cd bare.git` and then run Git commands from
within. When `safe.bareRepository` is set to `explicit`, these commands
fail because Git considers the implicitly-discovered bare repository
unsafe. A future change, most likely timed for Git v3.0, intends to
make `explicit` the default.
The naive fix of setting `GIT_DIR=.` does not work here because it
changes worktree detection: Git would no longer see the parent as a
working tree, breaking tests that specifically verify "this operation
requires a work tree" (such as `git reset --hard` inside `.git`).
Instead, opt into the current default explicitly via the
`GIT_CONFIG_PARAMETERS` environment variable, following existing
precedent in t0033-safe-directory.sh. Each affected subshell exports
`GIT_CONFIG_PARAMETERS="'safe.bareRepository=all'"` so that the
bare-repository safety check passes while preserving the original
worktree semantics.
This commit was produced with the help of Claude Opus 4.6 acting as a
sophisticated keyboard. It was told to add
` && export GIT_CONFIG_PARAMETERS="${SQ}safe.bareRepository=all${SQ}"`
after every `cd .git` or `cd bare.git` line inside a subshell that runs
Git commands.
The purely mechanical nature of the change can be verified by stripping
the addition from the diff and confirming that no lines actually changed:
git show HEAD |
sed 's/ && export GIT_CONFIG_PARAMETERS=.*$//' |
sed -ne '/^diff/,/@@/d' -e 's/^[-+]//p' |
uniq -u
An empty output confirms that every hunk is the same mechanical
insertion.
Assisted-by: Claude Opus 4.6
Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
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>
54b6c08 to
a30a801
Compare
|
There are issues in commit 8365979:
|
|
There are issues in commit 9d4fafe:
|
|
There are issues in commit 9d973bb:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
In one of my projects, I work exclusively on bare repositories. During one of the tests, I noticed that
safe.bareRepositoryis not yet enabled by default. This strikes me as undesirable, and I deem Git v3.0 the most logical opportunity to change the default.Cc: Patrick Steinhardt ps@pks.im