All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Anupam Mediratta via GitGitGadget" <gitgitgadget@gmail.com>
To: git@vger.kernel.org
Cc: Anupam Mediratta <mediratta@gmail.com>,
	Anupam Mediratta <mediratta@gmail.com>
Subject: [PATCH] git-p4: avoid shell interpretation of commit ids in applyCommit
Date: Tue, 22 Sep 2026 16:11:46 +0000	[thread overview]
Message-ID: <pull.2411.git.git.1790093506966.gitgitgadget@gmail.com> (raw)

From: Anupam Mediratta <mediratta@gmail.com>

applyCommit() builds a `git diff-tree ... | git apply ...` pipeline as a
shell command string, interpolating the commit id and running it via
os.system()/system(shell=True). The id usually comes from `git rev-list`
output (safe, plain SHA-1s), but it can also come verbatim from the
user-supplied `--commit` option, which is never validated
(git-p4.py:2620-2631). A value such as `$(some-command)` passed to
`--commit` is executed by the shell during command substitution, even
though the value is wrapped in double quotes.

Replace the shell pipeline with two argument-vector subprocess calls
connected directly through a pipe, matching the pattern already used
throughout this file (read_pipe, read_pipe_lines, p4_system). This
removes the shell entirely, rather than relying on quoting the
interpolated value.

Add a regression test exercising `git p4 submit --commit` with a shell
metacharacter payload, verifying it is never interpreted.

Signed-off-by: Anupam Mediratta <mediratta@gmail.com>
---
    git-p4: avoid shell interpretation of commit ids in applyCommit
    
    P4Submit.applyCommit() builds a git diff-tree | git apply pipeline as a
    shell command string and runs it with os.system()/system(shell=True).
    The commit id it interpolates is usually a plain SHA-1 from git
    rev-list, but it can also come straight from the unvalidated --commit
    command-line option, so a value such as $(some-command) passed to
    --commit gets executed by the shell during command substitution.
    
    This replaces the shell pipeline with two argument-vector subprocess
    calls connected directly through a pipe, the same pattern already used
    everywhere else in this file (read_pipe, read_pipe_lines, p4_system), so
    there's no shell left to escape correctly. It also adds a regression
    test in t9803 that submits a commit id crafted with shell metacharacters
    and checks they're never executed.
    
    I have read https://git-scm.com/docs/SubmittingPatches#ai and confirm
    this contribution complies with it.
    
    Changes since v1: corrected the description of the affected data (it's
    the unvalidated --commit argument, not Perforce server data, that was
    ever exploitable here), removed the shell entirely instead of quoting
    the interpolated value, replaced a test that never reached the
    vulnerable code with one that drives it through git p4 submit --commit,
    and fixed the commit message format (subsystem prefix, rationale,
    sign-off).

Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-2411%2Fanupamme%2Ffix-repo-git-git-p4-cwe-78-shell-injection-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v1
Pull-Request: https://github.com/git/git/pull/2411

 git-p4.py                         | 31 +++++++++++++++++++++----------
 t/t9803-git-p4-shell-metachars.sh | 16 ++++++++++++++++
 2 files changed, 37 insertions(+), 10 deletions(-)

diff --git a/git-p4.py b/git-p4.py
index c0ca7becaf..b09f5cd740 100755
--- a/git-p4.py
+++ b/git-p4.py
@@ -465,6 +465,22 @@ def p4_system(cmd, *k, **kw):
         raise subprocess.CalledProcessError(retcode, real_cmd)
 
 
+def diffTreeApply(id, applyArgs):
+    """Pipe `git diff-tree --full-index -p <id>` into `git apply <applyArgs>`
+    without a shell, so id can never be interpreted as shell syntax. Returns
+    the exit status of git apply."""
+    diffArgv = ["git", "diff-tree", "--full-index", "-p", id]
+    applyArgv = ["git", "apply"] + applyArgs
+    if verbose:
+        print("TryPatch: %s | %s" % (" ".join(diffArgv), " ".join(applyArgv)))
+    diffProc = subprocess.Popen(diffArgv, stdout=subprocess.PIPE)
+    applyProc = subprocess.Popen(applyArgv, stdin=diffProc.stdout)
+    diffProc.stdout.close()
+    applyProc.wait()
+    diffProc.wait()
+    return applyProc.returncode
+
+
 def die_bad_access(s):
     die("failure accessing depot: {0}".format(s.rstrip()))
 
@@ -2234,16 +2250,11 @@ class P4Submit(Command, P4UserMap):
             else:
                 die("unknown modifier %s for %s" % (modifier, path))
 
-        diffcmd = "git diff-tree --full-index -p \"%s\"" % (id)
-        patchcmd = diffcmd + " | git apply "
-        tryPatchCmd = patchcmd + "--check -"
-        applyPatchCmd = patchcmd + "--check --apply -"
+        tryPatchArgs = ["--check", "-"]
+        applyPatchArgs = ["--check", "--apply", "-"]
         patch_succeeded = True
 
-        if verbose:
-            print("TryPatch: %s" % tryPatchCmd)
-
-        if os.system(tryPatchCmd) != 0:
+        if diffTreeApply(id, tryPatchArgs) != 0:
             fixed_rcs_keywords = False
             patch_succeeded = False
             print("Unfortunately applying the change failed!")
@@ -2279,7 +2290,7 @@ class P4Submit(Command, P4UserMap):
 
             if fixed_rcs_keywords:
                 print("Retrying the patch with RCS keywords cleaned up")
-                if os.system(tryPatchCmd) == 0:
+                if diffTreeApply(id, tryPatchArgs) == 0:
                     patch_succeeded = True
                     print("Patch succeesed this time with RCS keywords cleaned")
 
@@ -2291,7 +2302,7 @@ class P4Submit(Command, P4UserMap):
         #
         # Apply the patch for real, and do add/delete/+x handling.
         #
-        system(applyPatchCmd, shell=True)
+        diffTreeApply(id, applyPatchArgs)
 
         for f in filesToChangeType:
             p4_edit(f, "-t", "auto")
diff --git a/t/t9803-git-p4-shell-metachars.sh b/t/t9803-git-p4-shell-metachars.sh
index 2913277013..ef8fd6e094 100755
--- a/t/t9803-git-p4-shell-metachars.sh
+++ b/t/t9803-git-p4-shell-metachars.sh
@@ -105,4 +105,20 @@ test_expect_success 'branch with shell char' '
 	)
 '
 
+test_expect_success 'git p4 submit --commit does not execute shell metachars in commit id' '
+	git p4 clone --dest="$git" //depot &&
+	test_when_finished cleanup_git &&
+	(
+		cd "$git" &&
+		git config git-p4.skipSubmitEditCheck true &&
+		echo f3 >file3 &&
+		git add file3 &&
+		git commit -m "add file3" &&
+		name='"'"'$(touch${IFS}injection-marker)'"'"' &&
+		git branch "$name" HEAD &&
+		P4EDITOR="test-tool chmtime +5" git p4 submit --commit "$name"
+	) &&
+	test_path_is_missing "$cli/injection-marker"
+'
+
 test_done

base-commit: d38352cd43ab9745686d697872408bc3249a153f
-- 
gitgitgadget

             reply	other threads:[~2026-09-22 16:11 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-22 16:11 Anupam Mediratta via GitGitGadget [this message]
2026-09-23 19:11 ` [PATCH] git-p4: avoid shell interpretation of commit ids in applyCommit Junio C Hamano
2026-09-24  8:28 ` [PATCH v2] " Anupam Mediratta via GitGitGadget

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=pull.2411.git.git.1790093506966.gitgitgadget@gmail.com \
    --to=gitgitgadget@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=mediratta@gmail.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.