fix(java): catch chained ProcessBuilder command injection - #4026
Conversation
command-injection-process-builder missed two shapes:
new ProcessBuilder().command(userInput) was never matched, because the
command() branch required a preceding assignment of the builder to a
variable.
new ProcessBuilder("ls", userInput) and builder.command("java", "-jar",
userInput) were excluded by the "first argument is a literal" negative
patterns, even though a later argument carried the dynamic value.
Add a branch for the chained receiver and a branch for a non-literal
value in any later argument position, keeping the existing requirement
that command() is called on a ProcessBuilder.
Signed-off-by: Eljees <3.14hell@gmail.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c1feef21a7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
This is a fair challenge and I want to be straight about the trade-off rather than defend the patch reflexively. The security point is correct: What a dynamic later argument can be is argument injection: a value that starts with So there are two defensible outcomes and I'd rather you pick:
I lean towards 2 — narrower and free of the semantic objection above — and I'm happy to push that immediately if you agree. Cases 3 and 4 would then be better served by a separate argument-injection rule with its own CWE, rather than being folded into this one. |
Signed-off-by: Eljees <3.14hell@gmail.com>
|
Ping — open since 28 July, no review yet. Checks are green on This catches One caveat I would rather state than bury: the review point about later arguments is correct. |
Fixes #3814
Problem
The existing rule misses chained calls where the command itself is dynamic:
new ProcessBuilder().command(userInput)new ProcessBuilder().command(userInput.split(" "))new ProcessBuilder(userInput)(control)new ProcessBuilder("ls", userInput)builder.command("java", "-jar", userInput)new ProcessBuilder("ls", "-la")The
command()branch requirespattern-inside: $TYPE $PB = new ProcessBuilder(...), so a chained call on a fresh builder has no matching assignment.Change
Add a chained-receiver branch for
new ProcessBuilder().command(...), carrying the same literal/Arrays.asList/String[]exclusions as the existing branch.After review, the proposed later-argument branch was removed:
ProcessBuilderpasses separate arguments directly to the child process, so a dynamic later argument is not command injection unless the executable is itself a shell. The existingsh -c/cmd /cbranches continue to cover those cases. Tests now explicitly mark ordinary later dynamic arguments asokto prevent that false positive from returning.Tests