Skip to content

Commit 7d43f86

Browse files
committed
Merge branch 'gc/bare-repo-discovery' into seen
Introduce a discovery.barerepository configuration variable that allows users to forbid discovery of bare repositories. * gc/bare-repo-discovery: SQUASH??? move new test to t0035 setup.c: learn discovery.bareRepository=cwd setup.c: make bare repo discovery optional
2 parents 82cb3df + c7f1fad commit 7d43f86

File tree

3 files changed

+178
-6
lines changed

3 files changed

+178
-6
lines changed

Documentation/config/discovery.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
discovery.bare::
2+
Specifies what kinds of directories Git can recognize as a bare
3+
repository when looking for the repository (aka repository
4+
discovery). This has no effect if repository discovery is not
5+
performed e.g. the path to the repository is set via `--git-dir`
6+
(see linkgit:git[1]).
7+
+
8+
This config setting is only respected when specified in a system or global
9+
config, not when it is specified in a repository config or via the command
10+
line option `-c discovery.bare=<value>`.
11+
+
12+
The currently supported values are `always` (Git always recognizes bare
13+
repositories), `cwd` (Git only recognizes bare repositories if they are the
14+
current working directory) and `never` (Git never recognizes bare
15+
repositories). This defaults to `always`, but this default is likely to
16+
change.
17+
+
18+
If your workflow does not rely on bare repositories, it is recommended that
19+
you set this value to `never`. This makes repository discovery easier to
20+
reason about and prevents certain types of security and non-security
21+
problems, such as:
22+
23+
* `git clone`-ing a repository containing a malicious bare repository
24+
inside it.
25+
* Git recognizing a directory that isn't meant to be a bare repository,
26+
but happens to look like one.

setup.c

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@
1010
static int inside_git_dir = -1;
1111
static int inside_work_tree = -1;
1212
static int work_tree_config_is_bogus;
13+
enum discovery_bare_config {
14+
DISCOVERY_BARE_UNKNOWN = -1,
15+
DISCOVERY_BARE_NEVER = 0,
16+
DISCOVERY_BARE_ALWAYS,
17+
DISCOVERY_BARE_CWD,
18+
};
19+
static enum discovery_bare_config discovery_bare_config =
20+
DISCOVERY_BARE_UNKNOWN;
1321

1422
static struct startup_info the_startup_info;
1523
struct startup_info *startup_info = &the_startup_info;
@@ -1142,6 +1150,60 @@ static int ensure_valid_ownership(const char *path)
11421150
return data.is_safe;
11431151
}
11441152

1153+
static int discovery_bare_cb(const char *key, const char *value, void *d)
1154+
{
1155+
if (strcmp(key, "discovery.bare"))
1156+
return 0;
1157+
1158+
if (!strcmp(value, "never")) {
1159+
discovery_bare_config = DISCOVERY_BARE_NEVER;
1160+
return 0;
1161+
}
1162+
if (!strcmp(value, "always")) {
1163+
discovery_bare_config = DISCOVERY_BARE_ALWAYS;
1164+
return 0;
1165+
}
1166+
if (!strcmp(value, "cwd")) {
1167+
discovery_bare_config = DISCOVERY_BARE_CWD;
1168+
return 0;
1169+
}
1170+
return -1;
1171+
}
1172+
1173+
static int check_bare_repo_allowed(const char *cwd, const char *path)
1174+
{
1175+
if (discovery_bare_config == DISCOVERY_BARE_UNKNOWN) {
1176+
read_very_early_config(discovery_bare_cb, NULL);
1177+
/* We didn't find a value; use the default. */
1178+
if (discovery_bare_config == DISCOVERY_BARE_UNKNOWN)
1179+
discovery_bare_config = DISCOVERY_BARE_ALWAYS;
1180+
}
1181+
switch (discovery_bare_config) {
1182+
case DISCOVERY_BARE_NEVER:
1183+
return 0;
1184+
case DISCOVERY_BARE_ALWAYS:
1185+
return 1;
1186+
case DISCOVERY_BARE_CWD:
1187+
return !strcmp(cwd, path);
1188+
default:
1189+
BUG("invalid discovery_bare_config %d", discovery_bare_config);
1190+
}
1191+
}
1192+
1193+
static const char *discovery_bare_config_to_string(void)
1194+
{
1195+
switch (discovery_bare_config) {
1196+
case DISCOVERY_BARE_NEVER:
1197+
return "never";
1198+
case DISCOVERY_BARE_ALWAYS:
1199+
return "always";
1200+
case DISCOVERY_BARE_CWD:
1201+
return "cwd";
1202+
default:
1203+
BUG("invalid discovery_bare_config %d", discovery_bare_config);
1204+
}
1205+
}
1206+
11451207
enum discovery_result {
11461208
GIT_DIR_NONE = 0,
11471209
GIT_DIR_EXPLICIT,
@@ -1151,7 +1213,8 @@ enum discovery_result {
11511213
GIT_DIR_HIT_CEILING = -1,
11521214
GIT_DIR_HIT_MOUNT_POINT = -2,
11531215
GIT_DIR_INVALID_GITFILE = -3,
1154-
GIT_DIR_INVALID_OWNERSHIP = -4
1216+
GIT_DIR_INVALID_OWNERSHIP = -4,
1217+
GIT_DIR_DISALLOWED_BARE = -5
11551218
};
11561219

11571220
/*
@@ -1167,7 +1230,8 @@ enum discovery_result {
11671230
* the discovered .git/ directory, if any. If `gitdir` is not absolute, it
11681231
* is relative to `dir` (i.e. *not* necessarily the cwd).
11691232
*/
1170-
static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
1233+
static enum discovery_result setup_git_directory_gently_1(struct strbuf *cwd,
1234+
struct strbuf *dir,
11711235
struct strbuf *gitdir,
11721236
int die_on_error)
11731237
{
@@ -1248,6 +1312,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12481312
}
12491313

12501314
if (is_git_directory(dir->buf)) {
1315+
if (!check_bare_repo_allowed(cwd->buf, dir->buf))
1316+
return GIT_DIR_DISALLOWED_BARE;
12511317
if (!ensure_valid_ownership(dir->buf))
12521318
return GIT_DIR_INVALID_OWNERSHIP;
12531319
strbuf_addstr(gitdir, ".");
@@ -1272,16 +1338,18 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
12721338
int discover_git_directory(struct strbuf *commondir,
12731339
struct strbuf *gitdir)
12741340
{
1275-
struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
1341+
struct strbuf cwd = STRBUF_INIT, dir = STRBUF_INIT, err = STRBUF_INIT;
12761342
size_t gitdir_offset = gitdir->len, cwd_len;
12771343
size_t commondir_offset = commondir->len;
12781344
struct repository_format candidate = REPOSITORY_FORMAT_INIT;
12791345

1280-
if (strbuf_getcwd(&dir))
1346+
if (strbuf_getcwd(&cwd))
12811347
return -1;
1348+
strbuf_addbuf(&dir, &cwd);
12821349

12831350
cwd_len = dir.len;
1284-
if (setup_git_directory_gently_1(&dir, gitdir, 0) <= 0) {
1351+
if (setup_git_directory_gently_1(&cwd, &dir, gitdir, 0) <= 0) {
1352+
strbuf_release(&cwd);
12851353
strbuf_release(&dir);
12861354
return -1;
12871355
}
@@ -1304,6 +1372,7 @@ int discover_git_directory(struct strbuf *commondir,
13041372
strbuf_reset(&dir);
13051373
strbuf_addf(&dir, "%s/config", commondir->buf + commondir_offset);
13061374
read_repository_format(&candidate, dir.buf);
1375+
strbuf_release(&cwd);
13071376
strbuf_release(&dir);
13081377

13091378
if (verify_repository_format(&candidate, &err) < 0) {
@@ -1353,7 +1422,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
13531422
die_errno(_("Unable to read current working directory"));
13541423
strbuf_addbuf(&dir, &cwd);
13551424

1356-
switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
1425+
switch (setup_git_directory_gently_1(&cwd, &dir, &gitdir, 1)) {
13571426
case GIT_DIR_EXPLICIT:
13581427
prefix = setup_explicit_git_dir(gitdir.buf, &cwd, &repo_fmt, nongit_ok);
13591428
break;
@@ -1394,6 +1463,14 @@ const char *setup_git_directory_gently(int *nongit_ok)
13941463
}
13951464
*nongit_ok = 1;
13961465
break;
1466+
case GIT_DIR_DISALLOWED_BARE:
1467+
if (!nongit_ok) {
1468+
die(_("cannot use bare repository '%s' (discovery.bare is '%s')"),
1469+
dir.buf,
1470+
discovery_bare_config_to_string());
1471+
}
1472+
*nongit_ok = 1;
1473+
break;
13971474
case GIT_DIR_NONE:
13981475
/*
13991476
* As a safeguard against setup_git_directory_gently_1 returning

t/t0035-discovery-bare.sh

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
#!/bin/sh
2+
3+
test_description='verify discovery.bare checks'
4+
5+
. ./test-lib.sh
6+
7+
pwd="$(pwd)"
8+
9+
expect_allowed () {
10+
git rev-parse --absolute-git-dir >actual &&
11+
echo "$pwd/outer-repo/bare-repo" >expected &&
12+
test_cmp expected actual
13+
}
14+
15+
expect_rejected () {
16+
test_must_fail git rev-parse --absolute-git-dir 2>err &&
17+
grep "discovery.bare" err
18+
}
19+
20+
test_expect_success 'setup bare repo in worktree' '
21+
git init outer-repo &&
22+
git init --bare outer-repo/bare-repo
23+
'
24+
25+
test_expect_success 'discovery.bare unset' '
26+
(
27+
cd outer-repo/bare-repo &&
28+
expect_allowed &&
29+
cd refs/ &&
30+
expect_allowed
31+
)
32+
'
33+
34+
test_expect_success 'discovery.bare=always' '
35+
git config --global discovery.bare always &&
36+
(
37+
cd outer-repo/bare-repo &&
38+
expect_allowed &&
39+
cd refs/ &&
40+
expect_allowed
41+
)
42+
'
43+
44+
test_expect_success 'discovery.bare=never' '
45+
git config --global discovery.bare never &&
46+
(
47+
cd outer-repo/bare-repo &&
48+
expect_rejected &&
49+
cd refs/ &&
50+
expect_rejected
51+
) &&
52+
(
53+
GIT_DIR=outer-repo/bare-repo &&
54+
export GIT_DIR &&
55+
expect_allowed
56+
)
57+
'
58+
59+
test_expect_success 'discovery.bare=cwd' '
60+
git config --global discovery.bare cwd &&
61+
(
62+
cd outer-repo/bare-repo &&
63+
expect_allowed &&
64+
cd refs/ &&
65+
expect_rejected
66+
)
67+
'
68+
69+
test_done

0 commit comments

Comments
 (0)