Skip to content

Commit adbb44d

Browse files
authored
HardenProcessCreationCodemod hardens when there are any variables (#307)
Now this codemod will not be executed when the parameters received are only constant variables or hardcoded values. Issue #299
1 parent 8cfc73d commit adbb44d

File tree

4 files changed

+129
-5
lines changed

4 files changed

+129
-5
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package io.codemodder.codemods;
2+
3+
import com.github.javaparser.ast.Node;
4+
import com.github.javaparser.ast.NodeList;
5+
import com.github.javaparser.ast.expr.Expression;
6+
import com.github.javaparser.ast.expr.MethodCallExpr;
7+
import java.util.List;
8+
import java.util.stream.Collectors;
9+
10+
final class ArgumentExpressionExtractor {
11+
12+
private ArgumentExpressionExtractor() {}
13+
14+
/**
15+
* Extracts all expressions from the arguments and their child nodes of a MethodCallExpr.
16+
*
17+
* @param methodCallExpr The MethodCallExpr to extract expressions from.
18+
* @return A list containing all expressions found in the MethodCallExpr arguments and their child
19+
* nodes.
20+
*/
21+
static List<Expression> extractExpressions(final MethodCallExpr methodCallExpr) {
22+
final NodeList<Expression> arguments = methodCallExpr.getArguments();
23+
return arguments.stream()
24+
.map(ArgumentExpressionExtractor::getExpressionsFromNode)
25+
.flatMap(List::stream)
26+
.toList();
27+
}
28+
29+
/**
30+
* Recursively collects all expressions from a given node and its child nodes.
31+
*
32+
* @param node The node to collect expressions from.
33+
* @return A list containing all expressions found in the node and its child nodes.
34+
*/
35+
private static List<Expression> getExpressionsFromNode(final Node node) {
36+
final List<Expression> expressions =
37+
node.getChildNodes().stream()
38+
.flatMap(childNode -> getExpressionsFromNode(childNode).stream())
39+
.collect(Collectors.toList());
40+
if (node instanceof Expression expression) {
41+
expressions.add(expression);
42+
}
43+
return expressions;
44+
}
45+
}

core-codemods/src/main/java/io/codemodder/codemods/HardenProcessCreationCodemod.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44
import com.github.javaparser.ast.CompilationUnit;
55
import com.github.javaparser.ast.Node;
66
import com.github.javaparser.ast.NodeList;
7+
import com.github.javaparser.ast.body.FieldDeclaration;
8+
import com.github.javaparser.ast.body.VariableDeclarator;
79
import com.github.javaparser.ast.expr.Expression;
810
import com.github.javaparser.ast.expr.MethodCallExpr;
911
import com.github.javaparser.ast.expr.NameExpr;
12+
import com.github.javaparser.ast.expr.SimpleName;
1013
import io.codemodder.*;
1114
import io.codemodder.ast.ASTTransforms;
1215
import io.codemodder.providers.sarif.semgrep.SemgrepScan;
@@ -34,6 +37,31 @@ public boolean onResultFound(
3437
final CompilationUnit cu,
3538
final MethodCallExpr methodCallExpr,
3639
final Result result) {
40+
41+
final List<FieldDeclaration> fieldDeclarations = cu.findAll(FieldDeclaration.class);
42+
final List<FieldDeclaration> constantFieldDeclarations =
43+
getConstantFieldDeclarations(fieldDeclarations);
44+
final List<SimpleName> constantNames = extractVariableNames(constantFieldDeclarations);
45+
46+
final List<Expression> allArgumentsExpressions =
47+
ArgumentExpressionExtractor.extractExpressions(methodCallExpr);
48+
49+
final List<Expression> onlyVariableExpressions =
50+
allArgumentsExpressions.stream()
51+
.filter(Expression::isNameExpr)
52+
.filter(
53+
expression -> {
54+
final NameExpr nameExpr = (NameExpr) expression;
55+
return !constantNames.contains(nameExpr.getName());
56+
})
57+
.toList();
58+
59+
final boolean containsOnlyConstantsAndHardcodedValues = onlyVariableExpressions.isEmpty();
60+
61+
if (containsOnlyConstantsAndHardcodedValues) {
62+
return false;
63+
}
64+
3765
Node parent = methodCallExpr.getParentNode().get();
3866
Expression scope = methodCallExpr.getScope().get();
3967
ASTTransforms.addImportIfMissing(cu, SystemCommand.class);
@@ -52,4 +80,27 @@ public boolean onResultFound(
5280
public List<DependencyGAV> dependenciesRequired() {
5381
return List.of(DependencyGAV.JAVA_SECURITY_TOOLKIT);
5482
}
83+
84+
private List<SimpleName> extractVariableNames(List<FieldDeclaration> fieldDeclarations) {
85+
return fieldDeclarations.stream()
86+
.flatMap(fieldDeclaration -> fieldDeclaration.getVariables().stream())
87+
.map(VariableDeclarator::getName)
88+
.toList();
89+
}
90+
91+
private List<FieldDeclaration> getConstantFieldDeclarations(
92+
final List<FieldDeclaration> fieldDeclarations) {
93+
94+
return fieldDeclarations.stream()
95+
.filter(FieldDeclaration::isFinal) // Check if the fieldDeclaration has the 'final' modifier
96+
.filter(
97+
fieldDeclaration ->
98+
fieldDeclaration.getVariables().stream()
99+
.allMatch(
100+
variable ->
101+
variable
102+
.getInitializer()
103+
.isPresent())) // Check if all variables have initializers
104+
.toList();
105+
}
55106
}

core-codemods/src/test/resources/harden-process-creation/Test.java.after

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,43 @@ public final class Test {
88

99
private Runtime memberRuntime;
1010
private static Runtime staticRuntime;
11+
private static final String A = "a";
12+
13+
void variable(final String var){
14+
SystemCommand.runCommand(Runtime.getRuntime(), new String[]{"a", "b", var});
15+
}
16+
17+
void constantAndHardcodedValues(){
18+
Runtime.getRuntime().exec(new Test(new String[]{"a", "b", A}));
19+
}
20+
21+
void test(){
22+
Runtime.getRuntime().exec(new String[]{"a", "b", "c"});
23+
Runtime.getRuntime().exec(new String[]{A, "b", "c"});
24+
}
1125

1226
void instantiated() {
1327
Runtime runtime = Runtime.getRuntime();
1428
}
1529

1630
void fromArgument(Runtime runtime) throws IOException {
17-
Process p = SystemCommand.runCommand(runtime, "foo");
31+
Process p = runtime.exec("foo");
1832
}
1933

2034
void fromMemberField() throws IOException {
21-
SystemCommand.runCommand(memberRuntime, "ls al");
35+
memberRuntime.exec("ls al");
2236
}
2337

2438
void fromStaticField() throws IOException {
25-
SystemCommand.runCommand(staticRuntime, new String[] {"cat", "/app/data.txt"});
39+
staticRuntime.exec(new String[] {"cat", "/app/data.txt"});
2640
}
2741

2842
void withEnvironment() throws IOException {
29-
SystemCommand.runCommand(Runtime.getRuntime(), "foo", new String[] {"FOO=BAR"});
43+
Runtime.getRuntime().exec("foo", new String[] {"FOO=BAR"});
3044
}
3145

3246
void withDirectory() throws IOException {
33-
SystemCommand.runCommand(Runtime.getRuntime(), "foo", getEnvironment(), getDirectory());
47+
Runtime.getRuntime().exec("foo", getEnvironment(), getDirectory());
3448
}
3549

3650
private String[] getEnvironment() {

core-codemods/src/test/resources/harden-process-creation/Test.java.before

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,20 @@ public final class Test {
77

88
private Runtime memberRuntime;
99
private static Runtime staticRuntime;
10+
private static final String A = "a";
11+
12+
void variable(final String var){
13+
Runtime.getRuntime().exec(new String[]{"a", "b", var});
14+
}
15+
16+
void constantAndHardcodedValues(){
17+
Runtime.getRuntime().exec(new Test(new String[]{"a", "b", A}));
18+
}
19+
20+
void test(){
21+
Runtime.getRuntime().exec(new String[]{"a", "b", "c"});
22+
Runtime.getRuntime().exec(new String[]{A, "b", "c"});
23+
}
1024

1125
void instantiated() {
1226
Runtime runtime = Runtime.getRuntime();

0 commit comments

Comments
 (0)