git.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Michael Lohmann <git@lohmann.sh>
To: ben.knoble@gmail.com
Cc: git@lohmann.sh, git@vger.kernel.org
Subject: [PATCH v2 4/5] setup: allow temporary bypass of `ensure_safe_repository()` checks
Date: Mon, 13 Oct 2025 23:46:07 +0200	[thread overview]
Message-ID: <20251013214608.33581-5-git@lohmann.sh> (raw)
In-Reply-To: <20251013214608.33581-1-git@lohmann.sh>

So far, the only option to allow executing git in what it considers to
be an "unsafe context" is to set this repository as "safe.directory". If
a user only wants to temporarily execute one command, they would need to
set the path as safe, execute the command and then remove the path
again. Forgetting to do the latter would make the user vulnerable if
this repo was changed afterwards in a malicious way.

Allow temporarily bypassing `ensure_safe_repository()` checks with a new
flag "--allow-unsafe" or environment variable "GIT_ALLOW_UNSAFE".

Signed-off-by: Michael Lohmann <git@lohmann.sh>
---
 Documentation/git.adoc            | 13 +++++++++++++
 environment.h                     |  1 +
 git.c                             |  5 +++++
 setup.c                           | 13 +++++++++++--
 t/meson.build                     |  1 +
 t/t0036-allow-unsafe-directory.sh | 28 ++++++++++++++++++++++++++++
 6 files changed, 59 insertions(+), 2 deletions(-)
 create mode 100755 t/t0036-allow-unsafe-directory.sh

diff --git a/Documentation/git.adoc b/Documentation/git.adoc
index ce099e78b8..7df51c38f9 100644
--- a/Documentation/git.adoc
+++ b/Documentation/git.adoc
@@ -14,6 +14,7 @@ SYNOPSIS
     [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]
     [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]
     [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]
+    [--allow-unsafe]
     <command> [<args>]
 
 DESCRIPTION
@@ -231,6 +232,12 @@ If you just want to run git as if it was started in `<path>` then use
 	linkgit:gitattributes[5]. This is equivalent to setting the
 	`GIT_ATTR_SOURCE` environment variable.
 
+--allow-unsafe::
+	Temporarily trust the repository regardless of "safe.directory"
+	configuration or ownership, potentially resulting in arbitrary code
+	execution by hooks or configuration settings. Equivalent to setting
+	the environment variable `GIT_ALLOW_UNSAFE=1`.
+
 GIT COMMANDS
 ------------
 
@@ -493,6 +500,12 @@ These environment variables apply to 'all' core Git commands. Nb: it
 is worth noting that they may be used/overridden by SCMS sitting above
 Git so take care if using a foreign front-end.
 
+`GIT_ALLOW_UNSAFE`::
+	This Boolean environment variable can be set to true to skip the
+	safety checks of "safe.directory" configuration and if the user
+	owns the repository before potentially executing arbitrary code
+	from hooks or config.
+
 `GIT_INDEX_FILE`::
 	This environment variable specifies an alternate
 	index file. If not specified, the default of `$GIT_DIR/index`
diff --git a/environment.h b/environment.h
index 51898c99cd..ee9e1b9514 100644
--- a/environment.h
+++ b/environment.h
@@ -42,6 +42,7 @@
 #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS"
 #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR"
 #define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE"
+#define GIT_ALLOW_UNSAFE "GIT_ALLOW_UNSAFE"
 
 /*
  * Environment variable used to propagate the --no-advice global option to the
diff --git a/git.c b/git.c
index c5fad56813..a7581a6805 100644
--- a/git.c
+++ b/git.c
@@ -42,6 +42,7 @@ const char git_usage_string[] =
 	   "           [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n"
 	   "           [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n"
 	   "           [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n"
+	   "           [--allow-unsafe]\n"
 	   "           <command> [<args>]");
 
 const char git_more_info_string[] =
@@ -354,6 +355,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
 			setenv(GIT_ADVICE_ENVIRONMENT, "0", 1);
 			if (envchanged)
 				*envchanged = 1;
+		} else if (!strcmp(cmd, "--allow-unsafe")) {
+			setenv(GIT_ALLOW_UNSAFE, "1", 1);
+			if (envchanged)
+				*envchanged = 1;
 		} else {
 			fprintf(stderr, _("unknown option: %s\n"), cmd);
 			usage(git_usage_string);
diff --git a/setup.c b/setup.c
index 41a12a85ab..10975fd9a3 100644
--- a/setup.c
+++ b/setup.c
@@ -1307,6 +1307,9 @@ static int ensure_safe_repository(const char *gitfile,
 {
 	struct safe_directory_data data = { 0 };
 
+	if (git_env_bool("GIT_ALLOW_UNSAFE", 0))
+		return 1;
+
 	/*
 	 * normalize the data.path for comparison with normalized paths
 	 * that come from the configuration file.  The path is unsafe
@@ -1353,7 +1356,10 @@ void die_upon_assumed_unsafe_repo(const char *gitfile, const char *worktree,
 	      "%s"
 	      "To add an exception for this directory, call:\n"
 	      "\n"
-	      "\tgit config --global --add safe.directory %s"),
+	      "\tgit config --global --add safe.directory %s\n"
+	      "\n"
+	      "To temporarily bypass safety-checks, run 'git --allow-unsafe <command>'\n"
+	      "or set the environment variable 'GIT_ALLOW_UNSAFE=true'."),
 	    path, report.buf, quoted.buf);
 }
 
@@ -1797,7 +1803,10 @@ const char *setup_git_directory_gently(int *nongit_ok)
 			      "%s"
 			      "To add an exception for this directory, call:\n"
 			      "\n"
-			      "\tgit config --global --add safe.directory %s"),
+			      "\tgit config --global --add safe.directory %s\n"
+			      "\n"
+			      "To temporarily bypass safety-checks, run 'git --allow-unsafe <command>'\n"
+			      "or set the environment variable 'GIT_ALLOW_UNSAFE=true'."),
 			    dir.buf, report.buf, quoted.buf);
 		}
 		*nongit_ok = 1;
diff --git a/t/meson.build b/t/meson.build
index 11376b9e25..c55fb55784 100644
--- a/t/meson.build
+++ b/t/meson.build
@@ -100,6 +100,7 @@ integration_tests = [
   't0033-safe-directory.sh',
   't0034-root-safe-directory.sh',
   't0035-safe-bare-repository.sh',
+  't0036-allow-unsafe-directory.sh',
   't0040-parse-options.sh',
   't0041-usage.sh',
   't0050-filesystem.sh',
diff --git a/t/t0036-allow-unsafe-directory.sh b/t/t0036-allow-unsafe-directory.sh
new file mode 100755
index 0000000000..4b98e815ff
--- /dev/null
+++ b/t/t0036-allow-unsafe-directory.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+test_description='verify safe.directory checks'
+
+. ./test-lib.sh
+
+GIT_TEST_ASSUME_DIFFERENT_OWNER=1
+export GIT_TEST_ASSUME_DIFFERENT_OWNER
+
+expect_rejected_dir () {
+	test_must_fail git status 2>err &&
+	grep "dubious ownership" err
+}
+
+test_expect_success 'safe.directory is not set' '
+	expect_rejected_dir
+'
+
+test_expect_success '--allow-unsafe allows execution in unsafe directory' '
+	git --allow-unsafe status
+'
+
+test_expect_success 'GIT_ALLOW_UNSAFE bool allows unsafe directory' '
+	env GIT_ALLOW_UNSAFE=true \
+	    git status
+'
+
+test_done
-- 
2.50.1 (Apple Git-155)


  parent reply	other threads:[~2025-10-13 21:46 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-13  9:41 [PATCH 0/5] Allow enforcing safe.directory Michael Lohmann
2025-10-13  9:41 ` [PATCH 1/5] setup: rename `ensure_safe_repository()` for clarity Michael Lohmann
2025-10-13  9:41 ` [PATCH 2/5] setup: rename `die_upon_assumed_unsafe_repo()` to align with check Michael Lohmann
2025-10-14 20:16   ` Junio C Hamano
2025-10-13  9:41 ` [PATCH 3/5] setup: refactor `ensure_safe_repository()` testing priorities Michael Lohmann
2025-10-14 20:32   ` Junio C Hamano
2025-10-13  9:41 ` [PATCH 4/5] setup: allow temporary bypass of `ensure_safe_repository()` checks Michael Lohmann
2025-10-13  9:41 ` [PATCH 5/5] setup: allow not marking self owned repos as safe in `ensure_safe_repository()` Michael Lohmann
2025-10-13 11:59   ` D. Ben Knoble
2025-10-13 21:46     ` [PATCH v2 0/5] Apply comments of D. Ben Knoble Michael Lohmann
2025-10-13 21:46       ` [PATCH v2 1/5] setup: rename `ensure_safe_repository()` for clarity Michael Lohmann
2025-10-13 21:46       ` [PATCH v2 2/5] setup: rename `die_upon_assumed_unsafe_repo()` to align with check Michael Lohmann
2025-10-13 21:46       ` [PATCH v2 3/5] setup: refactor `ensure_safe_repository()` testing priorities Michael Lohmann
2025-10-13 21:46       ` Michael Lohmann [this message]
2025-10-13 21:46       ` [PATCH v2 5/5] setup: allow not marking self owned repos as safe in `ensure_safe_repository()` Michael Lohmann
2025-10-16  5:33 ` [PATCH v3 0/5] Allow skipping ownership of repo in safety consideration Michael Lohmann
2025-10-16  5:33   ` [PATCH v3 1/5] setup: rename `ensure_safe_repository()` for clarity Michael Lohmann
2025-10-16  5:33   ` [PATCH v3 2/5] setup: rename `die_upon_unsafe_repo()` to align with check Michael Lohmann
2025-10-16  5:33   ` [PATCH v3 3/5] setup: refactor `ensure_safe_repository()` testing priorities Michael Lohmann
2025-10-16  5:33   ` [PATCH v3 4/5] setup: allow temporary bypass of `ensure_safe_repository()` checks Michael Lohmann
2025-10-16 19:26     ` Junio C Hamano
2025-10-16  5:33   ` [PATCH v3 5/5] setup: allow not marking self owned repos as safe in `ensure_safe_repository()` Michael Lohmann
2025-10-16 19:33     ` Junio C Hamano
2025-10-16 19:58     ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251013214608.33581-5-git@lohmann.sh \
    --to=git@lohmann.sh \
    --cc=ben.knoble@gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).