Sync with 2.30.6

Signed-off-by: Taylor Blau <me@ttaylorr.com>
diff --git a/Documentation/RelNotes/2.30.6.txt b/Documentation/RelNotes/2.30.6.txt
new file mode 100644
index 0000000..d649071
--- /dev/null
+++ b/Documentation/RelNotes/2.30.6.txt
@@ -0,0 +1,60 @@
+Git v2.30.6 Release Notes
+=========================
+
+This release addresses the security issues CVE-2022-39253 and
+CVE-2022-39260.
+
+Fixes since v2.30.5
+-------------------
+
+ * CVE-2022-39253:
+   When relying on the `--local` clone optimization, Git dereferences
+   symbolic links in the source repository before creating hardlinks
+   (or copies) of the dereferenced link in the destination repository.
+   This can lead to surprising behavior where arbitrary files are
+   present in a repository's `$GIT_DIR` when cloning from a malicious
+   repository.
+
+   Git will no longer dereference symbolic links via the `--local`
+   clone mechanism, and will instead refuse to clone repositories that
+   have symbolic links present in the `$GIT_DIR/objects` directory.
+
+   Additionally, the value of `protocol.file.allow` is changed to be
+   "user" by default.
+
+ * CVE-2022-39260:
+   An overly-long command string given to `git shell` can result in
+   overflow in `split_cmdline()`, leading to arbitrary heap writes and
+   remote code execution when `git shell` is exposed and the directory
+   `$HOME/git-shell-commands` exists.
+
+   `git shell` is taught to refuse interactive commands that are
+   longer than 4MiB in size. `split_cmdline()` is hardened to reject
+   inputs larger than 2GiB.
+
+Credit for finding CVE-2022-39253 goes to Cory Snider of Mirantis. The
+fix was authored by Taylor Blau, with help from Johannes Schindelin.
+
+Credit for finding CVE-2022-39260 goes to Kevin Backhouse of GitHub.
+The fix was authored by Kevin Backhouse, Jeff King, and Taylor Blau.
+
+
+Jeff King (2):
+      shell: add basic tests
+      shell: limit size of interactive commands
+
+Kevin Backhouse (1):
+      alias.c: reject too-long cmdline strings in split_cmdline()
+
+Taylor Blau (11):
+      builtin/clone.c: disallow `--local` clones with symlinks
+      t/lib-submodule-update.sh: allow local submodules
+      t/t1NNN: allow local submodules
+      t/2NNNN: allow local submodules
+      t/t3NNN: allow local submodules
+      t/t4NNN: allow local submodules
+      t/t5NNN: allow local submodules
+      t/t6NNN: allow local submodules
+      t/t7NNN: allow local submodules
+      t/t9NNN: allow local submodules
+      transport: make `protocol.file.allow` be "user" by default
diff --git a/Documentation/config/protocol.txt b/Documentation/config/protocol.txt
index 756591d..79938913 100644
--- a/Documentation/config/protocol.txt
+++ b/Documentation/config/protocol.txt
@@ -1,10 +1,10 @@
 protocol.allow::
 	If set, provide a user defined default policy for all protocols which
 	don't explicitly have a policy (`protocol.<name>.allow`).  By default,
-	if unset, known-safe protocols (http, https, git, ssh, file) have a
+	if unset, known-safe protocols (http, https, git, ssh) have a
 	default policy of `always`, known-dangerous protocols (ext) have a
-	default policy of `never`, and all other protocols have a default
-	policy of `user`.  Supported policies:
+	default policy of `never`, and all other protocols (including file)
+	have a default policy of `user`.  Supported policies:
 +
 --
 
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 2126fe8..91a5ae7 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.31.4
+DEF_VER=v2.31.5
 
 LF='
 '
diff --git a/alias.c b/alias.c
index c471538..00abde0 100644
--- a/alias.c
+++ b/alias.c
@@ -46,14 +46,16 @@
 
 #define SPLIT_CMDLINE_BAD_ENDING 1
 #define SPLIT_CMDLINE_UNCLOSED_QUOTE 2
+#define SPLIT_CMDLINE_ARGC_OVERFLOW 3
 static const char *split_cmdline_errors[] = {
 	N_("cmdline ends with \\"),
-	N_("unclosed quote")
+	N_("unclosed quote"),
+	N_("too many arguments"),
 };
 
 int split_cmdline(char *cmdline, const char ***argv)
 {
-	int src, dst, count = 0, size = 16;
+	size_t src, dst, count = 0, size = 16;
 	char quoted = 0;
 
 	ALLOC_ARRAY(*argv, size);
@@ -96,6 +98,11 @@
 		return -SPLIT_CMDLINE_UNCLOSED_QUOTE;
 	}
 
+	if (count >= INT_MAX) {
+		FREE_AND_NULL(*argv);
+		return -SPLIT_CMDLINE_ARGC_OVERFLOW;
+	}
+
 	ALLOC_GROW(*argv, count + 1, size);
 	(*argv)[count] = NULL;
 
diff --git a/builtin/clone.c b/builtin/clone.c
index 51e844a..8c05d58 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -420,13 +420,11 @@
 	int src_len, dest_len;
 	struct dir_iterator *iter;
 	int iter_status;
-	unsigned int flags;
 	struct strbuf realpath = STRBUF_INIT;
 
 	mkdir_if_missing(dest->buf, 0777);
 
-	flags = DIR_ITERATOR_PEDANTIC | DIR_ITERATOR_FOLLOW_SYMLINKS;
-	iter = dir_iterator_begin(src->buf, flags);
+	iter = dir_iterator_begin(src->buf, DIR_ITERATOR_PEDANTIC);
 
 	if (!iter)
 		die_errno(_("failed to start iterator over '%s'"), src->buf);
@@ -442,6 +440,10 @@
 		strbuf_setlen(dest, dest_len);
 		strbuf_addstr(dest, iter->relative_path);
 
+		if (S_ISLNK(iter->st.st_mode))
+			die(_("symlink '%s' exists, refusing to clone with --local"),
+			    iter->relative_path);
+
 		if (S_ISDIR(iter->st.st_mode)) {
 			mkdir_if_missing(dest->buf, 0777);
 			continue;
diff --git a/shell.c b/shell.c
index cef7ffd..02cfd96 100644
--- a/shell.c
+++ b/shell.c
@@ -47,6 +47,8 @@
 		die("could not chdir to user's home directory");
 }
 
+#define MAX_INTERACTIVE_COMMAND (4*1024*1024)
+
 static void run_shell(void)
 {
 	int done = 0;
@@ -67,22 +69,46 @@
 	run_command_v_opt(help_argv, RUN_SILENT_EXEC_FAILURE);
 
 	do {
-		struct strbuf line = STRBUF_INIT;
 		const char *prog;
 		char *full_cmd;
 		char *rawargs;
+		size_t len;
 		char *split_args;
 		const char **argv;
 		int code;
 		int count;
 
 		fprintf(stderr, "git> ");
-		if (git_read_line_interactively(&line) == EOF) {
+
+		/*
+		 * Avoid using a strbuf or git_read_line_interactively() here.
+		 * We don't want to allocate arbitrary amounts of memory on
+		 * behalf of a possibly untrusted client, and we're subject to
+		 * OS limits on command length anyway.
+		 */
+		fflush(stdout);
+		rawargs = xmalloc(MAX_INTERACTIVE_COMMAND);
+		if (!fgets(rawargs, MAX_INTERACTIVE_COMMAND, stdin)) {
 			fprintf(stderr, "\n");
-			strbuf_release(&line);
+			free(rawargs);
 			break;
 		}
-		rawargs = strbuf_detach(&line, NULL);
+		len = strlen(rawargs);
+
+		/*
+		 * If we truncated due to our input buffer size, reject the
+		 * command. That's better than running bogus input, and
+		 * there's a good chance it's just malicious garbage anyway.
+		 */
+		if (len >= MAX_INTERACTIVE_COMMAND - 1)
+			die("invalid command format: input too long");
+
+		if (len > 0 && rawargs[len - 1] == '\n') {
+			if (--len > 0 && rawargs[len - 1] == '\r')
+				--len;
+			rawargs[len] = '\0';
+		}
+
 		split_args = xstrdup(rawargs);
 		count = split_cmdline(split_args, &argv);
 		if (count < 0) {
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index 4b714e9..cc5b58b 100644
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -196,6 +196,7 @@
 # the submodule repo if it doesn't exist and configures the most problematic
 # settings for diff.ignoreSubmodules.
 prolog () {
+	test_config_global protocol.file.allow always &&
 	(test -d submodule_update_repo || create_lib_submodule_repo) &&
 	test_config_global diff.ignoreSubmodules all &&
 	test_config diff.ignoreSubmodules all
diff --git a/t/t1091-sparse-checkout-builtin.sh b/t/t1091-sparse-checkout-builtin.sh
index fc64e9e..705efb5 100755
--- a/t/t1091-sparse-checkout-builtin.sh
+++ b/t/t1091-sparse-checkout-builtin.sh
@@ -452,7 +452,8 @@
 	(
 		cd super &&
 		mkdir modules &&
-		git submodule add ../repo modules/child &&
+		git -c protocol.file.allow=always \
+			submodule add ../repo modules/child &&
 		git add . &&
 		git commit -m "add submodule" &&
 		git sparse-checkout init --cone &&
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index deae916..60df435 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -221,7 +221,8 @@
 	test_commit -C super test_commit &&
 	test_create_repo sub &&
 	test_commit -C sub test_commit &&
-	git -C super submodule add ../sub dir/sub &&
+	git -c protocol.file.allow=always \
+		-C super submodule add ../sub dir/sub &&
 	echo $(pwd)/super >expect  &&
 	git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
 	test_cmp expect out &&
diff --git a/t/t2400-worktree-add.sh b/t/t2400-worktree-add.sh
index 96dfca1..399229e 100755
--- a/t/t2400-worktree-add.sh
+++ b/t/t2400-worktree-add.sh
@@ -600,6 +600,7 @@
 '
 
 test_expect_success '"add" with uninitialized submodule, with submodule.recurse unset' '
+	test_config_global protocol.file.allow always &&
 	test_create_repo submodule &&
 	test_commit -C submodule first &&
 	test_create_repo project &&
@@ -615,6 +616,7 @@
 '
 
 test_expect_success '"add" with initialized submodule, with submodule.recurse unset' '
+	test_config_global protocol.file.allow always &&
 	git -C project-clone submodule update --init &&
 	git -C project-clone worktree add ../project-4
 '
diff --git a/t/t2403-worktree-move.sh b/t/t2403-worktree-move.sh
index a4e1a17..e8246ee 100755
--- a/t/t2403-worktree-move.sh
+++ b/t/t2403-worktree-move.sh
@@ -138,7 +138,8 @@
 	(
 		cd withsub &&
 		test_commit initial &&
-		git submodule add "$PWD"/.git sub &&
+		git -c protocol.file.allow=always \
+			submodule add "$PWD"/.git sub &&
 		git commit -m withsub &&
 		git worktree add second HEAD &&
 		git worktree move second third
@@ -148,7 +149,7 @@
 test_expect_success 'not move a repo with initialized submodule' '
 	(
 		cd withsub &&
-		git -C third submodule update &&
+		git -c protocol.file.allow=always -C third submodule update &&
 		test_must_fail git worktree move third forth
 	)
 '
@@ -227,6 +228,7 @@
 '
 
 test_expect_success 'remove a repo with uninitialized submodule' '
+	test_config_global protocol.file.allow always &&
 	(
 		cd withsub &&
 		git worktree add to-remove HEAD &&
@@ -235,6 +237,7 @@
 '
 
 test_expect_success 'not remove a repo with initialized submodule' '
+	test_config_global protocol.file.allow always &&
 	(
 		cd withsub &&
 		git worktree add to-remove HEAD &&
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
index b172c26..11018f3 100755
--- a/t/t2405-worktree-submodule.sh
+++ b/t/t2405-worktree-submodule.sh
@@ -10,6 +10,7 @@
 base_path=$(pwd -P)
 
 test_expect_success 'setup: create origin repos'  '
+	git config --global protocol.file.allow always &&
 	git init origin/sub &&
 	test_commit -C origin/sub file1 &&
 	git init origin/main &&
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index cc4b102..d6e356e 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -282,6 +282,7 @@
 	git init repo1 &&
 	git init repo1/sub &&
 	test_commit -C repo1/sub x &&
+	test_config_global protocol.file.allow always &&
 	git -C repo1 submodule add ./sub &&
 	git -C repo1 commit -m "adding sub" &&
 
diff --git a/t/t3420-rebase-autostash.sh b/t/t3420-rebase-autostash.sh
index 43fcb68..693934e 100755
--- a/t/t3420-rebase-autostash.sh
+++ b/t/t3420-rebase-autostash.sh
@@ -310,7 +310,7 @@
 test_expect_success 'autostash with dirty submodules' '
 	test_when_finished "git reset --hard && git checkout main" &&
 	git checkout -b with-submodule &&
-	git submodule add ./ sub &&
+	git -c protocol.file.allow=always submodule add ./ sub &&
 	test_tick &&
 	git commit -m add-submodule &&
 	echo changed >sub/file0 &&
diff --git a/t/t3426-rebase-submodule.sh b/t/t3426-rebase-submodule.sh
index 0ad3a07..fb21f67 100755
--- a/t/t3426-rebase-submodule.sh
+++ b/t/t3426-rebase-submodule.sh
@@ -47,7 +47,8 @@
 	git init sub &&
 	git -C sub commit --allow-empty -m "Initial commit" &&
 	git init super &&
-	git -C super submodule add ../sub &&
+	git -c protocol.file.allow=always \
+		-C super submodule add ../sub &&
 	git -C super config submodule.sub.ignore dirty &&
 	>super/foo &&
 	git -C super add foo &&
diff --git a/t/t3512-cherry-pick-submodule.sh b/t/t3512-cherry-pick-submodule.sh
index 822f2d4..10995a4 100755
--- a/t/t3512-cherry-pick-submodule.sh
+++ b/t/t3512-cherry-pick-submodule.sh
@@ -13,6 +13,8 @@
 test_submodule_switch "cherry-pick"
 
 test_expect_success 'unrelated submodule/file conflict is ignored' '
+	test_config_global protocol.file.allow always &&
+
 	test_create_repo sub &&
 
 	touch sub/file &&
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index bb9ef35..a0eda54 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -336,7 +336,7 @@
 
 test_expect_success 'rm removes removed submodule from index and .gitmodules' '
 	git reset --hard &&
-	git submodule update &&
+	git -c protocol.file.allow=always submodule update &&
 	rm -rf submod &&
 	git rm submod &&
 	git status -s -uno --ignore-submodules=none >actual &&
@@ -642,6 +642,7 @@
 EOF
 
 test_expect_success 'setup subsubmodule' '
+	test_config_global protocol.file.allow always &&
 	git reset --hard &&
 	git submodule update &&
 	(
diff --git a/t/t3906-stash-submodule.sh b/t/t3906-stash-submodule.sh
index a52e53d..0f7348e 100755
--- a/t/t3906-stash-submodule.sh
+++ b/t/t3906-stash-submodule.sh
@@ -36,7 +36,7 @@
 	git init main &&
 	(
 		cd main &&
-		git submodule add ../sub &&
+		git -c protocol.file.allow=always submodule add ../sub &&
 		test_commit main_file
 	)
 }
diff --git a/t/t4059-diff-submodule-not-initialized.sh b/t/t4059-diff-submodule-not-initialized.sh
index 49bca7b..d489230 100755
--- a/t/t4059-diff-submodule-not-initialized.sh
+++ b/t/t4059-diff-submodule-not-initialized.sh
@@ -49,7 +49,7 @@
 '
 
 test_expect_success 'setup - git submodule add' '
-	git submodule add ./sm2 sm1 &&
+	git -c protocol.file.allow=always submodule add ./sm2 sm1 &&
 	commit_file sm1 .gitmodules &&
 	git diff-tree -p --no-commit-id --submodule=log HEAD -- sm1 >actual &&
 	cat >expected <<-EOF &&
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index dc7b242..a90add5 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -759,9 +759,9 @@
 '
 
 test_expect_success 'setup nested submodule' '
-	git submodule add -f ./sm2 &&
+	git -c protocol.file.allow=always submodule add -f ./sm2 &&
 	git commit -a -m "add sm2" &&
-	git -C sm2 submodule add ../sm2 nested &&
+	git -c protocol.file.allow=always -C sm2 submodule add ../sm2 nested &&
 	git -C sm2 commit -a -m "nested sub" &&
 	head10=$(git -C sm2 rev-parse --short --verify HEAD)
 '
diff --git a/t/t4067-diff-partial-clone.sh b/t/t4067-diff-partial-clone.sh
index 804f2a8..28f42a4 100755
--- a/t/t4067-diff-partial-clone.sh
+++ b/t/t4067-diff-partial-clone.sh
@@ -77,6 +77,7 @@
 
 test_expect_success 'when fetching missing objects, diff skips GITLINKs' '
 	test_when_finished "rm -rf sub server client trace" &&
+	test_config_global protocol.file.allow always &&
 
 	test_create_repo sub &&
 	test_commit -C sub first &&
diff --git a/t/t4208-log-magic-pathspec.sh b/t/t4208-log-magic-pathspec.sh
index 7f0c1dc..2e8f5ad 100755
--- a/t/t4208-log-magic-pathspec.sh
+++ b/t/t4208-log-magic-pathspec.sh
@@ -124,6 +124,7 @@
 
 test_expect_success 'tree_entry_interesting does not match past submodule boundaries' '
 	test_when_finished "rm -rf repo submodule" &&
+	test_config_global protocol.file.allow always &&
 	git init submodule &&
 	test_commit -C submodule initial &&
 	git init repo &&
diff --git a/t/t5510-fetch.sh b/t/t5510-fetch.sh
index e83b2a6..1147ac9 100755
--- a/t/t5510-fetch.sh
+++ b/t/t5510-fetch.sh
@@ -782,6 +782,7 @@
 '
 
 test_expect_success 'fetch.writeCommitGraph with submodules' '
+	test_config_global protocol.file.allow always &&
 	git clone dups super &&
 	(
 		cd super &&
diff --git a/t/t5526-fetch-submodules.sh b/t/t5526-fetch-submodules.sh
index ed11569..bca6795 100755
--- a/t/t5526-fetch-submodules.sh
+++ b/t/t5526-fetch-submodules.sh
@@ -38,6 +38,7 @@
 }
 
 test_expect_success setup '
+	git config --global protocol.file.allow always &&
 	mkdir deepsubmodule &&
 	(
 		cd deepsubmodule &&
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index 58c7add..210302b 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -116,6 +116,7 @@
 	test_commit -C parent one &&
 	git -C parent push --mirror up &&
 
+	test_config_global protocol.file.allow always &&
 	git -C parent submodule add ../upstream workbench &&
 	git -C parent/workbench remote add up ../../upstream &&
 	git -C parent commit -m "add submodule" &&
diff --git a/t/t5572-pull-submodule.sh b/t/t5572-pull-submodule.sh
index 29537f4..2269b55 100755
--- a/t/t5572-pull-submodule.sh
+++ b/t/t5572-pull-submodule.sh
@@ -46,6 +46,10 @@
 KNOWN_FAILURE_NOFF_MERGE_ATTEMPTS_TO_MERGE_REMOVED_SUBMODULE_FILES=1
 test_submodule_switch_func "git_pull_noff"
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'pull --recurse-submodule setup' '
 	test_create_repo child &&
 	test_commit -C child bar &&
diff --git a/t/t5601-clone.sh b/t/t5601-clone.sh
index e7e6c08..4d733ba 100755
--- a/t/t5601-clone.sh
+++ b/t/t5601-clone.sh
@@ -741,6 +741,7 @@
 	echo aa >server/a &&
 	echo bb >server/b &&
 	# Also add a gitlink pointing to an arbitrary repository
+	test_config_global protocol.file.allow always &&
 	git -C server submodule add "$(pwd)/repo_for_submodule" c &&
 	git -C server add a b c &&
 	git -C server commit -m x &&
diff --git a/t/t5604-clone-reference.sh b/t/t5604-clone-reference.sh
index e845d62..6fb7a6e 100755
--- a/t/t5604-clone-reference.sh
+++ b/t/t5604-clone-reference.sh
@@ -303,8 +303,6 @@
 		ln -s ../an-object $obj &&
 
 		cd ../ &&
-		find . -type f | sort >../../../T.objects-files.raw &&
-		find . -type l | sort >../../../T.objects-symlinks.raw &&
 		echo unknown_content >unknown_file
 	) &&
 	git -C T fsck &&
@@ -313,19 +311,27 @@
 
 
 test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at objects/' '
-	for option in --local --no-hardlinks --shared --dissociate
+	# None of these options work when cloning locally, since T has
+	# symlinks in its `$GIT_DIR/objects` directory
+	for option in --local --no-hardlinks --dissociate
 	do
-		git clone $option T T$option || return 1 &&
-		git -C T$option fsck || return 1 &&
-		git -C T$option rev-list --all --objects >T$option.objects &&
-		test_cmp T.objects T$option.objects &&
-		(
-			cd T$option/.git/objects &&
-			find . -type f | sort >../../../T$option.objects-files.raw &&
-			find . -type l | sort >../../../T$option.objects-symlinks.raw
-		)
+		test_must_fail git clone $option T T$option 2>err || return 1 &&
+		test_i18ngrep "symlink.*exists" err || return 1
 	done &&
 
+	# But `--shared` clones should still work, even when specifying
+	# a local path *and* that repository has symlinks present in its
+	# `$GIT_DIR/objects` directory.
+	git clone --shared T T--shared &&
+	git -C T--shared fsck &&
+	git -C T--shared rev-list --all --objects >T--shared.objects &&
+	test_cmp T.objects T--shared.objects &&
+	(
+		cd T--shared/.git/objects &&
+		find . -type f | sort >../../../T--shared.objects-files.raw &&
+		find . -type l | sort >../../../T--shared.objects-symlinks.raw
+	) &&
+
 	for raw in $(ls T*.raw)
 	do
 		sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
@@ -333,26 +339,6 @@
 		sort $raw.de-sha-1 >$raw.de-sha || return 1
 	done &&
 
-	cat >expected-files <<-EOF &&
-	./Y/Z
-	./Y/Z
-	./Y/Z
-	./a-loose-dir/Z
-	./an-object
-	./info/packs
-	./pack/pack-Z.idx
-	./pack/pack-Z.pack
-	./packs/pack-Z.idx
-	./packs/pack-Z.pack
-	./unknown_file
-	EOF
-
-	for option in --local --no-hardlinks --dissociate
-	do
-		test_cmp expected-files T$option.objects-files.raw.de-sha || return 1 &&
-		test_must_be_empty T$option.objects-symlinks.raw.de-sha || return 1
-	done &&
-
 	echo ./info/alternates >expected-files &&
 	test_cmp expected-files T--shared.objects-files.raw &&
 	test_must_be_empty T--shared.objects-symlinks.raw
diff --git a/t/t5614-clone-submodules-shallow.sh b/t/t5614-clone-submodules-shallow.sh
index 5504b51..0c85ef8 100755
--- a/t/t5614-clone-submodules-shallow.sh
+++ b/t/t5614-clone-submodules-shallow.sh
@@ -24,6 +24,7 @@
 
 test_expect_success 'nonshallow clone implies nonshallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --recurse-submodules "file://$pwd/." super_clone &&
 	git -C super_clone log --oneline >lines &&
 	test_line_count = 3 lines &&
@@ -33,6 +34,7 @@
 
 test_expect_success 'shallow clone with shallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
 	git -C super_clone log --oneline >lines &&
 	test_line_count = 2 lines &&
@@ -42,6 +44,7 @@
 
 test_expect_success 'shallow clone does not imply shallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
 	git -C super_clone log --oneline >lines &&
 	test_line_count = 2 lines &&
@@ -51,6 +54,7 @@
 
 test_expect_success 'shallow clone with non shallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
 	git -C super_clone log --oneline >lines &&
 	test_line_count = 2 lines &&
@@ -60,6 +64,7 @@
 
 test_expect_success 'non shallow clone with shallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
 	git -C super_clone log --oneline >lines &&
 	test_line_count = 3 lines &&
@@ -69,6 +74,7 @@
 
 test_expect_success 'clone follows shallow recommendation' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git config -f .gitmodules submodule.sub.shallow true &&
 	git add .gitmodules &&
 	git commit -m "recommend shallow for sub" &&
@@ -87,6 +93,7 @@
 
 test_expect_success 'get unshallow recommended shallow submodule' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git clone --no-local "file://$pwd/." super_clone &&
 	(
 		cd super_clone &&
@@ -103,6 +110,7 @@
 
 test_expect_success 'clone follows non shallow recommendation' '
 	test_when_finished "rm -rf super_clone" &&
+	test_config_global protocol.file.allow always &&
 	git config -f .gitmodules submodule.sub.shallow false &&
 	git add .gitmodules &&
 	git commit -m "recommend non shallow for sub" &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index 5cb4153..c1cc003 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -174,6 +174,8 @@
 	test_config -C src_with_sub uploadpack.allowfilter 1 &&
 	test_config -C src_with_sub uploadpack.allowanysha1inwant 1 &&
 
+	test_config_global protocol.file.allow always &&
+
 	git -C src_with_sub submodule add "file://$(pwd)/submodule" mysub &&
 	git -C src_with_sub commit -m "commit with submodule" &&
 
diff --git a/t/t5617-clone-submodules-remote.sh b/t/t5617-clone-submodules-remote.sh
index e2dbb4e..5691f27 100755
--- a/t/t5617-clone-submodules-remote.sh
+++ b/t/t5617-clone-submodules-remote.sh
@@ -10,6 +10,7 @@
 pwd=$(pwd)
 
 test_expect_success 'setup' '
+	git config --global protocol.file.allow always &&
 	git checkout -b main &&
 	test_commit commit1 &&
 	mkdir sub &&
diff --git a/t/t6008-rev-list-submodule.sh b/t/t6008-rev-list-submodule.sh
index 3153a0d..a0a070b 100755
--- a/t/t6008-rev-list-submodule.sh
+++ b/t/t6008-rev-list-submodule.sh
@@ -26,7 +26,7 @@
 
 	: > super-file &&
 	git add super-file &&
-	git submodule add "$(pwd)" sub &&
+	git -c protocol.file.allow=always submodule add "$(pwd)" sub &&
 	git symbolic-ref HEAD refs/heads/super &&
 	test_tick &&
 	git commit -m super-initial &&
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 0f1cb49..7a8c9e3 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -9,7 +9,7 @@
 	: >pretzel/a &&
 	git -C pretzel add a &&
 	git -C pretzel commit -m "add a file" -- a &&
-	git submodule add ./pretzel sub &&
+	git -c protocol.file.allow=always submodule add ./pretzel sub &&
 	git commit -a -m "add submodule" &&
 	git submodule deinit --all
 '
diff --git a/t/t7001-mv.sh b/t/t7001-mv.sh
index 25bb9bb..ef505ae 100755
--- a/t/t7001-mv.sh
+++ b/t/t7001-mv.sh
@@ -304,6 +304,7 @@
 rm -f moved symlink
 
 test_expect_success 'setup submodule' '
+	test_config_global protocol.file.allow always &&
 	git commit -m initial &&
 	git reset --hard &&
 	git submodule add ./. sub &&
@@ -489,6 +490,7 @@
 '
 
 test_expect_success 'moving nested submodules' '
+	test_config_global protocol.file.allow always &&
 	git commit -am "cleanup commit" &&
 	mkdir sub_nested_nested &&
 	(
diff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh
index 4613882..95a9b47 100755
--- a/t/t7064-wtstatus-pv2.sh
+++ b/t/t7064-wtstatus-pv2.sh
@@ -465,6 +465,7 @@
 	git checkout initial-branch &&
 	git clone . sub_repo &&
 	git clone . super_repo &&
+	test_config_global protocol.file.allow always &&
 	(	cd super_repo &&
 		git submodule add ../sub_repo sub1 &&
 
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index a74816c..ff0a0f1 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -480,6 +480,7 @@
 		git init &&
 		test_commit msg hello.world
 	) &&
+	test_config_global protocol.file.allow always &&
 	git submodule add ./repo/.git sub1 &&
 	git commit -m "sub1" &&
 	git branch before_sub2 &&
diff --git a/t/t7400-submodule-basic.sh b/t/t7400-submodule-basic.sh
index a924fdb..eca1e47 100755
--- a/t/t7400-submodule-basic.sh
+++ b/t/t7400-submodule-basic.sh
@@ -14,6 +14,10 @@
 
 . ./test-lib.sh
 
+test_expect_success 'setup - enable local submodules' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule deinit works on empty repository' '
 	git submodule deinit --all
 '
diff --git a/t/t7403-submodule-sync.sh b/t/t7403-submodule-sync.sh
index 7d2ac33..ea92ef5 100755
--- a/t/t7403-submodule-sync.sh
+++ b/t/t7403-submodule-sync.sh
@@ -14,6 +14,8 @@
 . ./test-lib.sh
 
 test_expect_success setup '
+	git config --global protocol.file.allow always &&
+
 	echo file >file &&
 	git add file &&
 	test_tick &&
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index ff3ba54..7f83735 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -25,6 +25,7 @@
 
 
 test_expect_success 'setup a submodule tree' '
+	git config --global protocol.file.allow always &&
 	echo file > file &&
 	git add file &&
 	test_tick &&
diff --git a/t/t7407-submodule-foreach.sh b/t/t7407-submodule-foreach.sh
index e2f110b..59bd150 100755
--- a/t/t7407-submodule-foreach.sh
+++ b/t/t7407-submodule-foreach.sh
@@ -16,6 +16,7 @@
 
 
 test_expect_success 'setup a submodule tree' '
+	git config --global protocol.file.allow always &&
 	echo file > file &&
 	git add file &&
 	test_tick &&
diff --git a/t/t7408-submodule-reference.sh b/t/t7408-submodule-reference.sh
index a3892f4..02feb85 100755
--- a/t/t7408-submodule-reference.sh
+++ b/t/t7408-submodule-reference.sh
@@ -17,6 +17,10 @@
 	test_cmp expect actual
 }
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'preparing first repository' '
 	test_create_repo A &&
 	(
diff --git a/t/t7409-submodule-detached-work-tree.sh b/t/t7409-submodule-detached-work-tree.sh
index e17ac81..374ed48 100755
--- a/t/t7409-submodule-detached-work-tree.sh
+++ b/t/t7409-submodule-detached-work-tree.sh
@@ -15,6 +15,10 @@
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule on detached working tree' '
 	git init --bare remote &&
 	test_create_repo bundle1 &&
diff --git a/t/t7411-submodule-config.sh b/t/t7411-submodule-config.sh
index ad28e93..c583c4e 100755
--- a/t/t7411-submodule-config.sh
+++ b/t/t7411-submodule-config.sh
@@ -12,6 +12,9 @@
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
 test_expect_success 'submodule config cache setup' '
 	mkdir submodule &&
 	(cd submodule &&
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
index c8e7e98..c8b5ac2 100755
--- a/t/t7413-submodule-is-active.sh
+++ b/t/t7413-submodule-is-active.sh
@@ -9,6 +9,7 @@
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+	git config --global protocol.file.allow always &&
 	git init sub &&
 	test_commit -C sub initial &&
 	git init super &&
diff --git a/t/t7414-submodule-mistakes.sh b/t/t7414-submodule-mistakes.sh
index f2e7df5..cf95603 100755
--- a/t/t7414-submodule-mistakes.sh
+++ b/t/t7414-submodule-mistakes.sh
@@ -30,7 +30,8 @@
 
 test_expect_success 'submodule add does not warn' '
 	test_when_finished "git rm -rf submodule .gitmodules" &&
-	git submodule add ./embed submodule 2>stderr &&
+	git -c protocol.file.allow=always \
+		submodule add ./embed submodule 2>stderr &&
 	test_i18ngrep ! warning stderr
 '
 
diff --git a/t/t7415-submodule-names.sh b/t/t7415-submodule-names.sh
index f70368b..f37456f 100755
--- a/t/t7415-submodule-names.sh
+++ b/t/t7415-submodule-names.sh
@@ -8,6 +8,10 @@
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-pack.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'check names' '
 	cat >expect <<-\EOF &&
 	valid
diff --git a/t/t7416-submodule-dash-url.sh b/t/t7416-submodule-dash-url.sh
index d21dc8b..3ebd985 100755
--- a/t/t7416-submodule-dash-url.sh
+++ b/t/t7416-submodule-dash-url.sh
@@ -3,6 +3,10 @@
 test_description='check handling of disallowed .gitmodule urls'
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'create submodule with protected dash in url' '
 	git init upstream &&
 	git -C upstream commit --allow-empty -m base &&
diff --git a/t/t7417-submodule-path-url.sh b/t/t7417-submodule-path-url.sh
index f0f6b9f..2f4b25d 100755
--- a/t/t7417-submodule-path-url.sh
+++ b/t/t7417-submodule-path-url.sh
@@ -6,6 +6,10 @@
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'create submodule with dash in path' '
 	git init upstream &&
 	git -C upstream commit --allow-empty -m base &&
diff --git a/t/t7418-submodule-sparse-gitmodules.sh b/t/t7418-submodule-sparse-gitmodules.sh
index 3f7f2718..16331c3 100755
--- a/t/t7418-submodule-sparse-gitmodules.sh
+++ b/t/t7418-submodule-sparse-gitmodules.sh
@@ -14,6 +14,10 @@
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'sparse checkout setup which hides .gitmodules' '
 	git init upstream &&
 	git init submodule &&
diff --git a/t/t7419-submodule-set-branch.sh b/t/t7419-submodule-set-branch.sh
index 3b925c3..5357093 100755
--- a/t/t7419-submodule-set-branch.sh
+++ b/t/t7419-submodule-set-branch.sh
@@ -12,6 +12,10 @@
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule config cache setup' '
 	mkdir submodule &&
 	(cd submodule &&
diff --git a/t/t7420-submodule-set-url.sh b/t/t7420-submodule-set-url.sh
index ef0cb6e..d6bf62b 100755
--- a/t/t7420-submodule-set-url.sh
+++ b/t/t7420-submodule-set-url.sh
@@ -12,6 +12,10 @@
 TEST_NO_CREATE_REPO=1
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'submodule config cache setup' '
 	mkdir submodule &&
 	(
diff --git a/t/t7421-submodule-summary-add.sh b/t/t7421-submodule-summary-add.sh
index b070f13..ce64d8b 100755
--- a/t/t7421-submodule-summary-add.sh
+++ b/t/t7421-submodule-summary-add.sh
@@ -12,6 +12,10 @@
 
 . ./test-lib.sh
 
+test_expect_success 'setup' '
+	git config --global protocol.file.allow always
+'
+
 test_expect_success 'summary test environment setup' '
 	git init sm &&
 	test_commit -C sm "add file" file file-content file-tag &&
diff --git a/t/t7506-status-submodule.sh b/t/t7506-status-submodule.sh
index 3fcb447..459300c 100755
--- a/t/t7506-status-submodule.sh
+++ b/t/t7506-status-submodule.sh
@@ -251,6 +251,7 @@
 	test_create_repo_with_commit sub1 &&
 	test_tick &&
 	test_create_repo_with_commit sub2 &&
+	test_config_global protocol.file.allow always &&
 	(
 		cd super &&
 		prev=$(git rev-parse HEAD) &&
@@ -326,6 +327,7 @@
 # sub2 will have an untracked file
 # sub3 will have an untracked repository
 test_expect_success 'setup superproject with untracked file in nested submodule' '
+	test_config_global protocol.file.allow always &&
 	(
 		cd super &&
 		git clean -dfx &&
diff --git a/t/t7507-commit-verbose.sh b/t/t7507-commit-verbose.sh
index ed2653d..bd0ae4b 100755
--- a/t/t7507-commit-verbose.sh
+++ b/t/t7507-commit-verbose.sh
@@ -74,6 +74,7 @@
 
 test_expect_success 'submodule log is stripped out too with -v' '
 	git config diff.submodule log &&
+	test_config_global protocol.file.allow always &&
 	git submodule add ./. sub &&
 	git commit -m "sub added" &&
 	(
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 3e041e8..77db028 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -629,6 +629,7 @@
 
 test_expect_success 'difftool properly honors gitlink and core.worktree' '
 	test_when_finished rm -rf submod/ule &&
+	test_config_global protocol.file.allow always &&
 	git submodule add ./. submod/ule &&
 	test_config -C submod/ule diff.tool checktrees &&
 	test_config -C submod/ule difftool.checktrees.cmd '\''
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 828cb3b..f465c0d 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -193,6 +193,7 @@
 	git -C "su:b" commit -m "add fi:le" &&
 	test_tick &&
 
+	test_config_global protocol.file.allow always &&
 	git -C parent submodule add "../su:b" "su:b" &&
 	git -C parent commit -m "add submodule" &&
 	test_tick &&
@@ -227,6 +228,7 @@
 	git -C sub commit -m "add file" &&
 	test_tick &&
 
+	test_config_global protocol.file.allow always &&
 	git -C parent submodule add ../sub dir/sub &&
 	git -C parent commit -m "add submodule" &&
 	test_tick &&
@@ -271,6 +273,7 @@
 	mkdir parent/src &&
 	echo "(1|2)d(3|4)" >parent/src/file2 &&
 	git -C parent add src/file2 &&
+	test_config_global protocol.file.allow always &&
 	git -C parent submodule add ../sub &&
 	git -C parent commit -m "add files and submodule" &&
 	test_tick &&
@@ -313,6 +316,7 @@
 	mkdir parent/src &&
 	echo "(1|2)d(3|4)" >parent/src/file &&
 	git -C parent add src/file &&
+	test_config_global protocol.file.allow always &&
 	git -C parent submodule add ../sub src/sub &&
 	git -C parent submodule add ../sub sub &&
 	git -C parent commit -m "add files and submodules" &&
diff --git a/t/t9304-fast-import-marks.sh b/t/t9304-fast-import-marks.sh
index d4359db..73f3ca2 100755
--- a/t/t9304-fast-import-marks.sh
+++ b/t/t9304-fast-import-marks.sh
@@ -25,6 +25,7 @@
 '
 
 test_expect_success 'setup dump with submodule' '
+	test_config_global protocol.file.allow always &&
 	git submodule add "$PWD" sub &&
 	git commit -m "add submodule" &&
 	git fast-export HEAD >dump
diff --git a/t/t9350-fast-export.sh b/t/t9350-fast-export.sh
index 409b48e..3e0b039 100755
--- a/t/t9350-fast-export.sh
+++ b/t/t9350-fast-export.sh
@@ -268,6 +268,7 @@
 
 test_expect_success 'setup submodule' '
 
+	test_config_global protocol.file.allow always &&
 	git checkout -f main &&
 	mkdir sub &&
 	(
@@ -293,6 +294,7 @@
 
 test_expect_success 'submodule fast-export | fast-import' '
 
+	test_config_global protocol.file.allow always &&
 	SUBENT1=$(git ls-tree main^ sub) &&
 	SUBENT2=$(git ls-tree main sub) &&
 	rm -rf new &&
diff --git a/t/t9850-shell.sh b/t/t9850-shell.sh
new file mode 100755
index 0000000..cfc71c3
--- /dev/null
+++ b/t/t9850-shell.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+test_description='git shell tests'
+. ./test-lib.sh
+
+test_expect_success 'shell allows upload-pack' '
+	printf 0000 >input &&
+	git upload-pack . <input >expect &&
+	git shell -c "git-upload-pack $SQ.$SQ" <input >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'shell forbids other commands' '
+	test_must_fail git shell -c "git config foo.bar baz"
+'
+
+test_expect_success 'shell forbids interactive use by default' '
+	test_must_fail git shell
+'
+
+test_expect_success 'shell allows interactive command' '
+	mkdir git-shell-commands &&
+	write_script git-shell-commands/ping <<-\EOF &&
+	echo pong
+	EOF
+	echo pong >expect &&
+	echo ping | git shell >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'shell complains of overlong commands' '
+	perl -e "print \"a\" x 2**12 for (0..2**19)" |
+	test_must_fail git shell 2>err &&
+	grep "too long" err
+'
+
+test_done
diff --git a/transport.c b/transport.c
index 1c4ab67..f357ffc 100644
--- a/transport.c
+++ b/transport.c
@@ -964,8 +964,7 @@
 	if (!strcmp(type, "http") ||
 	    !strcmp(type, "https") ||
 	    !strcmp(type, "git") ||
-	    !strcmp(type, "ssh") ||
-	    !strcmp(type, "file"))
+	    !strcmp(type, "ssh"))
 		return PROTOCOL_ALLOW_ALWAYS;
 
 	/* known scary; err on the side of caution */