safe.directory: allow "lead/ing/path/*" match
When safe.directory was introduced in v2.30.3 timeframe, 8959555c (setup_git_directory(): add an owner check for the top-level directory, 2022-03-02), it only allowed specific opt-out directories. Immediately after an embargoed release that included the change, 0f85c4a3 (setup: opt-out of check with safe.directory=*, 2022-04-13) was done as a response to loosen the check so that a single '*' can be used to say "I trust all repositories" for folks who host too many repositories to list individually. Let's further loosen the check to allow people to say "everything under this hierarchy is deemed safe" by specifying such a leading directory with "/*" appended to it. Signed-off-by: Junio C Hamano <gitster@pobox.com>
Junio C Hamano committed
May 29, 2024 at 09:02 UTC
313eec177ad010048b399d6fd14de871b517f7e3
3 files changed
+32
-8
Documentation/config/safe.txt
+2
-1
@@ -44,7 +44,8 @@ string `*`. This will allow all repositories to be treated as if their
44
directory was listed in the `safe.directory` list. If `safe.directory=*`
45
is set in system config and you want to re-enable this protection, then
46
initialize your list with an empty value before listing the repositories
47
-that you deem safe.
47
+that you deem safe. Giving a directory with `/*` appended to it will
48
+allow access to all repositories under the named directory.
49
+
50
As explained, Git only allows you to access repositories owned by
51
yourself, i.e. the user who is running Git, by default. When Git
setup.c
+15
-7
@@ -1176,13 +1176,21 @@ static int safe_directory_cb(const char *key, const char *value,
1176
} else if (!strcmp(value, "*")) {
1177
data->is_safe = 1;
1178
} else {
1179
- const char *interpolated = NULL;
1180
-
1181
- if (!git_config_pathname(&interpolated, key, value) &&
1182
- !fspathcmp(data->path, interpolated ? interpolated : value))
1183
- data->is_safe = 1;
1184
-
1185
- free((char *)interpolated);
1179
+ const char *allowed = NULL;
1180
+
1181
+ if (!git_config_pathname(&allowed, key, value)) {
1182
+ if (!allowed)
1183
+ allowed = value;
1184
+ if (ends_with(allowed, "/*")) {
1185
+ size_t len = strlen(allowed);
1186
+ if (!fspathncmp(allowed, data->path, len - 1))
1187
+ data->is_safe = 1;
1188
+ } else if (!fspathcmp(data->path, allowed)) {
1189
+ data->is_safe = 1;
1190
+ }
1191
+ }
1192
+ if (allowed != value)
1193
+ free((char *)allowed);
1194
}
1195
1196
return 0;
t/t0033-safe-directory.sh
+15
@@ -71,7 +71,22 @@ test_expect_success 'safe.directory=*, but is reset' '
71
expect_rejected_dir
72
'
73
74
+test_expect_success 'safe.directory with matching glob' '
75
+ git config --global --unset-all safe.directory &&
76
+ p=$(pwd) &&
77
+ git config --global safe.directory "${p%/*}/*" &&
78
+ git status
79
+'
80
+
81
+test_expect_success 'safe.directory with unmatching glob' '
82
+ git config --global --unset-all safe.directory &&
83
+ p=$(pwd) &&
84
+ git config --global safe.directory "${p%/*}no/*" &&
85
+ expect_rejected_dir
86
+'
87
+
88
test_expect_success 'safe.directory in included file' '
89
+ git config --global --unset-all safe.directory &&
90
cat >gitconfig-include <<-EOF &&
91
[safe]
92
directory = "$(pwd)"