Skip to content

Commit 93d1c90

Browse files
authored
New Sonar JNDI Injection Codemod (#453)
\close #work
1 parent 6fc81f2 commit 93d1c90

File tree

12 files changed

+1858
-1
lines changed

12 files changed

+1858
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public static List<Class<? extends CodeChanger>> asList() {
7474
SemgrepOverlyPermissiveFilePermissionsCodemod.class,
7575
SimplifyRestControllerAnnotationsCodemod.class,
7676
SubstituteReplaceAllCodemod.class,
77+
SonarJNDIInjectionCodemod.class,
7778
SonarRemoveUnthrowableExceptionCodemod.class,
7879
SonarXXECodemod.class,
7980
SonarSQLInjectionCodemod.class,
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package io.codemodder.codemods;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import io.codemodder.*;
5+
import io.codemodder.codetf.DetectorRule;
6+
import io.codemodder.providers.sonar.ProvidedSonarScan;
7+
import io.codemodder.providers.sonar.RuleIssue;
8+
import io.codemodder.providers.sonar.SonarRemediatingJavaParserChanger;
9+
import io.codemodder.remediation.GenericRemediationMetadata;
10+
import io.codemodder.remediation.jndiinjection.JNDIInjectionRemediator;
11+
import io.codemodder.sonar.model.Issue;
12+
import io.codemodder.sonar.model.SonarFinding;
13+
import java.util.List;
14+
import java.util.Objects;
15+
import javax.inject.Inject;
16+
17+
/** This codemod knows how to fix JNDI vulnerabilities found by sonar. */
18+
@Codemod(
19+
id = "sonar:java/jndi-injection-s2078",
20+
reviewGuidance = ReviewGuidance.MERGE_AFTER_CURSORY_REVIEW,
21+
executionPriority = CodemodExecutionPriority.HIGH,
22+
importance = Importance.HIGH)
23+
public final class SonarJNDIInjectionCodemod extends SonarRemediatingJavaParserChanger {
24+
25+
private final JNDIInjectionRemediator remediator;
26+
private final RuleIssue issues;
27+
28+
@Inject
29+
public SonarJNDIInjectionCodemod(
30+
@ProvidedSonarScan(ruleId = "javasecurity:S2078") final RuleIssue issues) {
31+
super(GenericRemediationMetadata.JNDI.reporter(), issues);
32+
this.issues = Objects.requireNonNull(issues);
33+
this.remediator = JNDIInjectionRemediator.DEFAULT;
34+
}
35+
36+
@Override
37+
public DetectorRule detectorRule() {
38+
return new DetectorRule(
39+
"javasecurity:S2078",
40+
"LDAP queries should not be vulnerable to injection attacks",
41+
"https://rules.sonarsource.com/java/RSPEC-2078/");
42+
}
43+
44+
@Override
45+
public CodemodFileScanningResult visit(
46+
final CodemodInvocationContext context, final CompilationUnit cu) {
47+
List<Issue> issuesForFile = issues.getResultsByPath(context.path());
48+
return remediator.remediateAll(
49+
cu,
50+
context.path().toString(),
51+
detectorRule(),
52+
issuesForFile,
53+
SonarFinding::getKey,
54+
i -> i.getTextRange() != null ? i.getTextRange().getStartLine() : i.getLine(),
55+
i -> i.getTextRange() != null ? i.getTextRange().getEndLine() : null,
56+
i -> i.getTextRange() != null ? i.getTextRange().getStartOffset() : null);
57+
}
58+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package io.codemodder.codemods;
2+
3+
import io.codemodder.DependencyGAV;
4+
import io.codemodder.testutils.CodemodTestMixin;
5+
import io.codemodder.testutils.Metadata;
6+
import org.junit.jupiter.api.Nested;
7+
8+
final class SonarJNDIInjectionCodemodTest {
9+
10+
@Nested
11+
@Metadata(
12+
codemodType = SonarJNDIInjectionCodemod.class,
13+
testResourceDir = "sonar-jndi-injection-s2078/normal",
14+
renameTestFile = "src/main/java/com/mycompany/app/jndi/JNDIVuln.java",
15+
expectingFixesAtLines = {15},
16+
dependencies = DependencyGAV.JAVA_SECURITY_TOOLKIT_GAV)
17+
final class ExpectedSinkInLocationTest implements CodemodTestMixin {}
18+
19+
@Nested
20+
@Metadata(
21+
codemodType = SonarJNDIInjectionCodemod.class,
22+
testResourceDir = "sonar-jndi-injection-s2078/misleading-location",
23+
renameTestFile = "src/main/java/com/mycompany/app/jndi/FindResource.java",
24+
expectingFixesAtLines = {18},
25+
dependencies = DependencyGAV.JAVA_SECURITY_TOOLKIT_GAV)
26+
final class MisleadingSinkInLocationTest implements CodemodTestMixin {}
27+
28+
/** Just confirms that when there are no changes for a given file, nothing errors. */
29+
@Nested
30+
@Metadata(
31+
codemodType = SonarJNDIInjectionCodemod.class,
32+
testResourceDir = "sonar-jndi-injection-s2078/unrelated-file",
33+
renameTestFile = "src/main/java/com/acme/jndi/UnrelatedFile.java",
34+
dependencies = {})
35+
final class UnrelatedFileTest implements CodemodTestMixin {}
36+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.acme.jndi;
2+
3+
import io.github.pixee.security.JNDI;
4+
import javax.naming.Context;
5+
import javax.naming.InitialContext;
6+
import javax.naming.NamingException;
7+
8+
/** JNDI resource finder. */
9+
public final class FindResource {
10+
11+
private FindResource() { }
12+
13+
public static String findResource(final String resource) throws NamingException {
14+
return lookupResource(resource);
15+
}
16+
17+
private static String lookupResource(final String resource) throws NamingException {
18+
Context ctx = new InitialContext();
19+
return String.valueOf(JNDI.limitedContext(ctx).lookup(resource));
20+
}
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.acme.jndi;
2+
3+
import javax.naming.Context;
4+
import javax.naming.InitialContext;
5+
import javax.naming.NamingException;
6+
7+
/** JNDI resource finder. */
8+
public final class FindResource {
9+
10+
private FindResource() { }
11+
12+
public static String findResource(final String resource) throws NamingException {
13+
return lookupResource(resource);
14+
}
15+
16+
private static String lookupResource(final String resource) throws NamingException {
17+
Context ctx = new InitialContext();
18+
return String.valueOf(ctx.lookup(resource));
19+
}
20+
}

0 commit comments

Comments
 (0)