Skip to content

git-p4: avoid shell interpretation of commit ids in applyCommit - #2411

Open
anupamme wants to merge 1 commit into
git:masterfrom
anupamme:fix-repo-git-git-p4-cwe-78-shell-injection
Open

anupamme wants to merge 1 commit into
git:masterfrom
anupamme:fix-repo-git-git-p4-cwe-78-shell-injection

Conversation

@anupamme

@anupamme anupamme commented Sep 19, 2026 •

Copy link
Copy Markdown

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: as Junio pointed out, the call that applies the patch for
real used to go through git-p4.py's own system() helper, which raises
CalledProcessError on a non-zero exit status, so a failed git apply aborted
the submit; v1 dropped that and silently carried on. The new helper now takes
the same ignore_error argument system() does and raises by default, and the two
callers that inspect the exit status themselves pass ignore_error=True.

@gitgitgadget-git

Copy link
Copy Markdown

Welcome to GitGitGadget

Hi @anupamme, and welcome to GitGitGadget, the GitHub App to send patch series to the Git mailing list from GitHub Pull Requests.

Please make sure that either:

  • Your Pull Request has a good description, if it consists of multiple commits, as it will be used as cover letter.
  • Your Pull Request description is empty, if it consists of a single commit, as the commit message should be descriptive enough by itself.

You can CC potential reviewers by adding a footer to the PR description with the following syntax:

CC: Revi Ewer <[email protected]>, Ill Takalook <[email protected]>

NOTE: DO NOT copy/paste your CC list from a previous GGG PR's description,
because it will result in a malformed CC list on the mailing list. See
example.

Also, it is a good idea to review the commit messages one last time, as the Git project expects them in a quite specific form:

  • the lines should not exceed 76 columns,
  • the first line should be like a header and typically start with a prefix like "tests:" or "revisions:" to state which subsystem the change is about, and
  • the commit messages' body should be describing the "why?" of the change.
  • Finally, the commit messages should end in a Signed-off-by: line matching the commits' author.

It is in general a good idea to await the automated test ("Checks") in this Pull Request before contributing the patches, e.g. to avoid trivial issues such as unportable code.

Contributing the patches

Before you can contribute the patches, your GitHub username needs to be added to the list of permitted users. Any already-permitted user can do that, by adding a comment to your PR of the form /allow. A good way to find other contributors is to locate recent pull requests where someone has been /allowed:

Both the person who commented /allow and the PR author are able to /allow you.

An alternative is the channel #git-devel on the Libera Chat IRC network:

<newcontributor> I've just created my first PR, could someone please /allow me? https://github.com/gitgitgadget/git/pull/12345
<veteran> newcontributor: it is done
<newcontributor> thanks!

Once on the list of permitted usernames, you can contribute the patches to the Git mailing list by adding a PR comment /submit.

If you want to see what email(s) would be sent for a /submit request, add a PR comment /preview to have the email(s) sent to you. You must have a public GitHub email address for this. Note that any reviewers CC'd via the list in the PR description will not actually be sent emails.

After you submit, GitGitGadget will respond with another comment that contains the link to the cover letter mail in the Git mailing list archive. Please make sure to monitor the discussion in that thread and to address comments and suggestions (while the comments and suggestions will be mirrored into the PR by GitGitGadget, you will still want to reply via mail).

If you do not want to subscribe to the Git mailing list just to be able to respond to a mail, you can download the mbox from the Git mailing list archive (click the (raw) link), then import it into your mail program. If you use GMail, you can do this via:

curl -g --user "<EMailAddress>:<Password>" \
    --url "imaps://imap.gmail.com/INBOX" -T /path/to/raw.txt

To iterate on your change, i.e. send a revised patch or patch series, you will first want to (force-)push to the same branch. You probably also want to modify your Pull Request description (or title). It is a good idea to summarize the revision by adding something like this to the cover letter (read: by editing the first comment on the PR, i.e. the PR description):

Changes since v1:
- Fixed a typo in the commit message (found by ...)
- Added a code comment to ... as suggested by ...
...

To send a new iteration, just add another PR comment with the contents: /submit.

Need help?

New contributors who want advice are encouraged to join [email protected], where volunteers who regularly contribute to Git are willing to answer newbie questions, give advice, or otherwise provide mentoring to interested contributors. You must join in order to post or view messages, but anyone can join.

You may also be able to find help in real time in the developer IRC channel, #git-devel on Libera Chat. Remember that IRC does not support offline messaging, so if you send someone a private message and log out, they cannot respond to you. The scrollback of #git-devel is archived, though.

@gitgitgadget-git

Copy link
Copy Markdown

There is an issue in commit b45c880:
fix: multi_agent.cwe-78 security vulnerability

  • Commit not signed off

@dscho

dscho commented Sep 19, 2026

Copy link
Copy Markdown
Member

@anupamme with the current shape of this PR, I am not willing to /allow you.

Please understand that not only is the Git project highly critical of AI-generated contributions (you will need to polish it, manually, yourself, to at least give the impression that you had a hand in writing this patch), the project is also very particular in its requirements. See Documentation/SubmittingPatches and existing commit messages for reference. Also, do make sure to address the test failures).

Again, I am unwilling to bless this PR with an /allow in the current form. If you rework it enough, I will revisit it.

@anupamme
anupamme force-pushed the fix-repo-git-git-p4-cwe-78-shell-injection branch from b45c880 to 291728b Compare September 19, 2026 13:24
@anupamme

Copy link
Copy Markdown
Author

Review comments addressed. Pls review.

@dscho

dscho commented Sep 19, 2026

Copy link
Copy Markdown
Member

Review comments addressed. Pls review.

@anupamme there are still test failures, e.g.:

[...]
  //depot/branch1/base/file1#1 - opened for edit
  Origin branch is remotes/p4/depot/branch1
  Perforce checkout for depot path //depot/branch1/ located at /__w/git/git/t/trash directory.t9801-git-p4-branch/cli/branch1/
  Synchronizing p4 checkout...
  ('Applying', 'f57d9df update file1 in branch1')
  Traceback (most recent call last):
    File "/__w/git/git/git-p4", line 4642, in <module>
      main()
    File "/__w/git/git/git-p4", line 4636, in main
      if not cmd.run(args):
    File "/__w/git/git/git-p4", line 2716, in run
      ok = self.applyCommit(commit)
    File "/__w/git/git/git-p4", line 2260, in applyCommit
      if diffTreeApply(id, tryPatchArgs) != 0:
    File "/__w/git/git/git-p4", line 477, in diffTreeApply
      " ".join(shlex.quote(a) for a in diffArgv),
    File "/__w/git/git/git-p4", line 477, in <genexpr>
      " ".join(shlex.quote(a) for a in diffArgv),
  AttributeError: 'module' object has no attribute 'quote'
  Perforce db files in '.' will be created if missing...
  error: last command exited with $?=1

This is not a bug fix, this is introducing a new bug.

@dscho

dscho commented Sep 19, 2026

Copy link
Copy Markdown
Member

@anupamme I'm not welcoming this back-and-forth. I'll not even look unless the CI build indicates no more test failures.

@anupamme
anupamme force-pushed the fix-repo-git-git-p4-cwe-78-shell-injection branch from 291728b to 391e429 Compare September 20, 2026 02:04
@anupamme

Copy link
Copy Markdown
Author

@anupamme I'm not welcoming this back-and-forth. I'll not even look unless the CI build indicates no more test failures.

Apologies for the noise. All tests have passed now. Can you pls review again.

@dscho dscho left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a lot better. Thank you for putting in the work.

@dscho

dscho commented Sep 22, 2026

Copy link
Copy Markdown
Member

@anupamme please edit #2411 (comment) extensively (it will be sent as a cover letter, so it should look a bit more like, say, https://lore.kernel.org/git/[email protected]/, the part before the shortlog, or like #1668 (comment), which made it into Git as 55702c5).

You will want to tread very carefully around AI usage; There is at least one highly vocal voice on the Git mailing list who is very convinced that they speak for the project in trying to discourage each and every AI usage. It is usually more pleasant when one avoids triggering those responses while contributing to the Git project.

@dscho

dscho commented Sep 22, 2026

Copy link
Copy Markdown
Member

/allow

@gitgitgadget-git

Copy link
Copy Markdown

User anupamme is now allowed to use GitGitGadget.

@dscho

dscho commented Sep 22, 2026

Copy link
Copy Markdown
Member

@anupamme I would like to recommend to add a sentence to the cover letter that you have read https://git-scm.com/docs/SubmittingPatches#ai and that this contribution is in compliance. That should (hopefully!) fend of unpleasantries.

@anupamme anupamme changed the title fix: sanitize shell/subprocess call in git-p4.py (CWE-78) git-p4: avoid shell interpretation of commit ids in applyCommit Sep 22, 2026
@anupamme

Copy link
Copy Markdown
Author

Hello, I've made the changes as requested. Pls review, and if there is still something to be done on my end, let me know.

@dscho

dscho commented Sep 22, 2026

Copy link
Copy Markdown
Member

@anupamme almost perfect: Since you did not yet /submit the patch, there are no "Changes since v1"... just delete that paragraph, test with /preview, look over the email you received (but which will not have been sent to the Git mailing list), and if everything looks fine to you, /submit.

@anupamme

Copy link
Copy Markdown
Author

/preview

@gitgitgadget-git

Copy link
Copy Markdown

Preview email sent as [email protected]

@anupamme

Copy link
Copy Markdown
Author

/submit

@gitgitgadget-git

Copy link
Copy Markdown

Submitted as [email protected]

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v1

To fetch this version to local tag pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v1:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v1

@gitgitgadget-git

Copy link
Copy Markdown

Junio C Hamano wrote on the Git mailing list (how to reply to this email):

"Anupam Mediratta via GitGitGadget" <[email protected]> writes:

> @@ -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")

Both of these check the result of running diff|apply pipeline and
react to a failure.

> @@ -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)

It is a bit hard to discover, but the original code catches a failed
"diff|apply" pipeline invocation, because the "system()" used here
is what git-p4.py defines for itself.  When the pipeline fails, this
system() raises subprocess.CalledProcessError().

The new one ignores the exit status from the pipeline, so even after
a failure to apply the change, the program continues.

Which may not be what you want to see.

>  
>          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

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.

The shell-based call that applied the patch for real went through
git-p4.py's own system() helper, which raises CalledProcessError on a
non-zero exit status, so a failed apply aborted the submit. Keep that
behaviour by giving the new helper the same ignore_error contract
system() uses: it raises unless the caller asks for the status, and the
two callers that test the status for themselves ask for it.

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

Signed-off-by: Anupam Mediratta <[email protected]>
@anupamme
anupamme force-pushed the fix-repo-git-git-p4-cwe-78-shell-injection branch from 391e429 to 6fcacc7 Compare September 24, 2026 06:56
@anupamme

Copy link
Copy Markdown
Author

/preview

@gitgitgadget-git

Copy link
Copy Markdown

Preview email sent as [email protected]

@anupamme

Copy link
Copy Markdown
Author

/submit

@gitgitgadget-git

Copy link
Copy Markdown

Submitted as [email protected]

To fetch this version into FETCH_HEAD:

git fetch https://github.com/gitgitgadget/git/ pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v2

To fetch this version to local tag pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v2:

git fetch --no-tags https://github.com/gitgitgadget/git/ tag pr-git-2411/anupamme/fix-repo-git-git-p4-cwe-78-shell-injection-v2

@gitgitgadget-git

Copy link
Copy Markdown

This patch series was integrated into seen via bf4b723.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants