]> git.ipfire.org Git - thirdparty/git.git/commitdiff
safe.directory: allow "lead/ing/path/*" match
authorJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2024 16:02:16 +0000 (09:02 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 29 May 2024 19:06:27 +0000 (12:06 -0700)
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>
Documentation/config/safe.txt
setup.c
t/t0033-safe-directory.sh

index 577df40223a095b999b38a41df6c3a1cd1252de2..2d45c98b12d9519ac92e82fa0545045f372c8d19 100644 (file)
@@ -44,7 +44,8 @@ string `*`. This will allow all repositories to be treated as if their
 directory was listed in the `safe.directory` list. If `safe.directory=*`
 is set in system config and you want to re-enable this protection, then
 initialize your list with an empty value before listing the repositories
-that you deem safe.
+that you deem safe.  Giving a directory with `/*` appended to it will
+allow access to all repositories under the named directory.
 +
 As explained, Git only allows you to access repositories owned by
 yourself, i.e. the user who is running Git, by default.  When Git
diff --git a/setup.c b/setup.c
index f4b32f76e3d86b46dbd7713195592906b73b9571..4c5de0960bf11b887e4e7b573f505b9eb3964f79 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -1176,13 +1176,21 @@ static int safe_directory_cb(const char *key, const char *value,
        } else if (!strcmp(value, "*")) {
                data->is_safe = 1;
        } else {
-               const char *interpolated = NULL;
-
-               if (!git_config_pathname(&interpolated, key, value) &&
-                   !fspathcmp(data->path, interpolated ? interpolated : value))
-                       data->is_safe = 1;
-
-               free((char *)interpolated);
+               const char *allowed = NULL;
+
+               if (!git_config_pathname(&allowed, key, value)) {
+                       if (!allowed)
+                               allowed = value;
+                       if (ends_with(allowed, "/*")) {
+                               size_t len = strlen(allowed);
+                               if (!fspathncmp(allowed, data->path, len - 1))
+                                       data->is_safe = 1;
+                       } else if (!fspathcmp(data->path, allowed)) {
+                               data->is_safe = 1;
+                       }
+               }
+               if (allowed != value)
+                       free((char *)allowed);
        }
 
        return 0;
index dc3496897abc96f488b43bfcbd1dd54258e75876..5f0b19e0572d1bb0d29e7294a616668dc1732a24 100755 (executable)
@@ -71,7 +71,22 @@ test_expect_success 'safe.directory=*, but is reset' '
        expect_rejected_dir
 '
 
+test_expect_success 'safe.directory with matching glob' '
+       git config --global --unset-all safe.directory &&
+       p=$(pwd) &&
+       git config --global safe.directory "${p%/*}/*" &&
+       git status
+'
+
+test_expect_success 'safe.directory with unmatching glob' '
+       git config --global --unset-all safe.directory &&
+       p=$(pwd) &&
+       git config --global safe.directory "${p%/*}no/*" &&
+       expect_rejected_dir
+'
+
 test_expect_success 'safe.directory in included file' '
+       git config --global --unset-all safe.directory &&
        cat >gitconfig-include <<-EOF &&
        [safe]
                directory = "$(pwd)"