Skip to content

Commit ad822f5

Browse files
carlosu7nahsra
andauthored
New Sonarcloud codemod to substitute String#replaceAll() (#247)
This change replaces the usage of `String#replaceAll()` to `String#replace()` when the first argument is not a regular expression. It does exactly the same thing as `String#replaceAll()` without the performance drawback of the regex. --------- Co-authored-by: Arshan Dabirsiaghi <[email protected]>
1 parent 5fefc1c commit ad822f5

File tree

11 files changed

+290
-2
lines changed

11 files changed

+290
-2
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package io.codemodder.codemods;
2+
3+
import com.github.javaparser.ast.CompilationUnit;
4+
import com.github.javaparser.ast.expr.SimpleName;
5+
import io.codemodder.*;
6+
import io.codemodder.providers.sonar.ProvidedSonarScan;
7+
import io.codemodder.providers.sonar.RuleIssues;
8+
import io.codemodder.providers.sonar.SonarPluginJavaParserChanger;
9+
import io.codemodder.providers.sonar.api.Issue;
10+
import javax.inject.Inject;
11+
12+
/** A codemod for automatically replacing replaceAll() calls to replace() . */
13+
@Codemod(
14+
id = "sonar:java/substitute-replaceAll-s5361",
15+
reviewGuidance = ReviewGuidance.MERGE_WITHOUT_REVIEW,
16+
executionPriority = CodemodExecutionPriority.HIGH)
17+
public final class SubstituteReplaceAllCodemod extends SonarPluginJavaParserChanger<SimpleName> {
18+
19+
@Inject
20+
public SubstituteReplaceAllCodemod(
21+
@ProvidedSonarScan(ruleId = "java:S5361") final RuleIssues issues) {
22+
super(issues, SimpleName.class, RegionNodeMatcher.MATCHES_START);
23+
}
24+
25+
@Override
26+
public boolean onIssueFound(
27+
final CodemodInvocationContext context,
28+
final CompilationUnit cu,
29+
final SimpleName name,
30+
final Issue issue) {
31+
name.setIdentifier("replace");
32+
return true;
33+
}
34+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
This change replaces `String#replaceAll()` with `String#replace()` to enhance performance and avoid confusion.
2+
3+
The `String#replaceAll()` call takes a regular expression for the first argument, which is then compiled and used to replace string subsections. However, the argument being passed to it doesn't actually appear to be a regular expression. Therefore, the `replace()` [API](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#replace-java.lang.CharSequence-java.lang.CharSequence-) appears to be a better fit.
4+
5+
Our changes look something like this:
6+
7+
```diff
8+
String init = "my string\n";
9+
10+
- String changed = init.replaceAll("\n", "<br>");
11+
+ String changed = init.replace("\n", "<br>");
12+
```
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"summary" : "Fixed inefficient usage of `String#replaceAll()` (Sonar).",
3+
"change" : "Fixed inefficient usage of `String#replaceAll()`.",
4+
"references" : [
5+
"https://rules.sonarsource.com/java/RSPEC-5361/"
6+
]
7+
}

core-codemods/src/test/java/io/codemodder/codemods/AddMissingOverrideCodemodTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
@Metadata(
77
codemodType = AddMissingOverrideCodemod.class,
88
testResourceDir = "add-missing-override-s1161",
9+
renameTestFile = "src/main/java/SqlInjectionLesson10b.java",
910
dependencies = {})
1011
final class AddMissingOverrideCodemodTest implements CodemodTestMixin {}

core-codemods/src/test/java/io/codemodder/codemods/FixRedundantStaticOnEnumCodemodTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
@Metadata(
77
codemodType = FixRedundantStaticOnEnumCodemod.class,
88
testResourceDir = "remove-redundant-static-s2786",
9+
renameTestFile = "src/main/java/Response.java",
910
dependencies = {})
1011
final class FixRedundantStaticOnEnumCodemodTest implements CodemodTestMixin {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package io.codemodder.codemods;
2+
3+
import io.codemodder.testutils.CodemodTestMixin;
4+
import io.codemodder.testutils.Metadata;
5+
6+
@Metadata(
7+
codemodType = SubstituteReplaceAllCodemod.class,
8+
testResourceDir = "substitute-replaceAll-s5361",
9+
renameTestFile = "src/main/java/org/owasp/webgoat/lessons/ssrf/SSRFTask2.java",
10+
dependencies = {})
11+
final class SubstituteReplaceAllCodemodTest implements CodemodTestMixin {}

core-codemods/src/test/resources/add-missing-override-s1161/sonar-issues.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"key": "AYvtrjy0LCzGLicz7Ajy",
1515
"rule": "java:S1161",
1616
"severity": "MAJOR",
17-
"component": "nahsra_WebGoat_10_23:Test.java",
17+
"component": "nahsra_WebGoat_10_23:src/main/java/SqlInjectionLesson10b.java",
1818
"project": "nahsra_WebGoat_10_23",
1919
"line": 143,
2020
"hash": "e4d44f915becc09c0ce386b304b7621b",

core-codemods/src/test/resources/remove-redundant-static-s2786/sonar-issues.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"key": "AYv0acY3pMuQdcUNcsKX",
1515
"rule": "java:S2786",
1616
"severity": "MINOR",
17-
"component": "nahsra_WebGoat_10_23:Test.java",
17+
"component": "nahsra_WebGoat_10_23:src/main/java/Response.java",
1818
"project": "nahsra_WebGoat_10_23",
1919
"line": 4,
2020
"hash": "f7cdce57b37926f1061e0ab664b60dbd",
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
3+
*
4+
* Copyright (c) 2002 - 2019 Bruce Mayhew
5+
*
6+
* This program is free software; you can redistribute it and/or modify it under the terms of the
7+
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
11+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with this program; if
15+
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16+
* 02111-1307, USA.
17+
*
18+
* Getting Source ==============
19+
*
20+
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
21+
*/
22+
23+
package org.owasp.webgoat.lessons.ssrf;
24+
25+
import java.io.IOException;
26+
import java.io.InputStream;
27+
import java.net.MalformedURLException;
28+
import java.net.URL;
29+
import java.nio.charset.StandardCharsets;
30+
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
31+
import org.owasp.webgoat.container.assignments.AssignmentHints;
32+
import org.owasp.webgoat.container.assignments.AttackResult;
33+
import org.springframework.web.bind.annotation.PostMapping;
34+
import org.springframework.web.bind.annotation.RequestParam;
35+
import org.springframework.web.bind.annotation.ResponseBody;
36+
import org.springframework.web.bind.annotation.RestController;
37+
38+
@RestController
39+
@AssignmentHints({"ssrf.hint3"})
40+
public class SSRFTask2 extends AssignmentEndpoint {
41+
42+
@PostMapping("/SSRF/task2")
43+
@ResponseBody
44+
public AttackResult completed(@RequestParam String url) {
45+
return furBall(url);
46+
}
47+
48+
protected AttackResult furBall(String url) {
49+
if (url.matches("http://ifconfig\\.pro")) {
50+
String html;
51+
try (InputStream in = new URL(url).openStream()) {
52+
html =
53+
new String(in.readAllBytes(), StandardCharsets.UTF_8)
54+
.replace("\n", "<br>"); // Otherwise the \n gets escaped in the response
55+
} catch (MalformedURLException e) {
56+
return getFailedResult(e.getMessage());
57+
} catch (IOException e) {
58+
// in case the external site is down, the test and lesson should still be ok
59+
html =
60+
"<html><body>Although the http://ifconfig.pro site is down, you still managed to solve"
61+
+ " this exercise the right way!</body></html>";
62+
}
63+
return success(this).feedback("ssrf.success").output(html).build();
64+
}
65+
var html = "<img class=\"image\" alt=\"image post\" src=\"images/cat.jpg\">";
66+
return getFailedResult(html);
67+
}
68+
69+
private AttackResult getFailedResult(String errorMsg) {
70+
return failed(this).feedback("ssrf.failure").output(errorMsg).build();
71+
}
72+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* This file is part of WebGoat, an Open Web Application Security Project utility. For details, please see http://www.owasp.org/
3+
*
4+
* Copyright (c) 2002 - 2019 Bruce Mayhew
5+
*
6+
* This program is free software; you can redistribute it and/or modify it under the terms of the
7+
* GNU General Public License as published by the Free Software Foundation; either version 2 of the
8+
* License, or (at your option) any later version.
9+
*
10+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
11+
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12+
* General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License along with this program; if
15+
* not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16+
* 02111-1307, USA.
17+
*
18+
* Getting Source ==============
19+
*
20+
* Source for this application is maintained at https://github.com/WebGoat/WebGoat, a repository for free software projects.
21+
*/
22+
23+
package org.owasp.webgoat.lessons.ssrf;
24+
25+
import java.io.IOException;
26+
import java.io.InputStream;
27+
import java.net.MalformedURLException;
28+
import java.net.URL;
29+
import java.nio.charset.StandardCharsets;
30+
import org.owasp.webgoat.container.assignments.AssignmentEndpoint;
31+
import org.owasp.webgoat.container.assignments.AssignmentHints;
32+
import org.owasp.webgoat.container.assignments.AttackResult;
33+
import org.springframework.web.bind.annotation.PostMapping;
34+
import org.springframework.web.bind.annotation.RequestParam;
35+
import org.springframework.web.bind.annotation.ResponseBody;
36+
import org.springframework.web.bind.annotation.RestController;
37+
38+
@RestController
39+
@AssignmentHints({"ssrf.hint3"})
40+
public class SSRFTask2 extends AssignmentEndpoint {
41+
42+
@PostMapping("/SSRF/task2")
43+
@ResponseBody
44+
public AttackResult completed(@RequestParam String url) {
45+
return furBall(url);
46+
}
47+
48+
protected AttackResult furBall(String url) {
49+
if (url.matches("http://ifconfig\\.pro")) {
50+
String html;
51+
try (InputStream in = new URL(url).openStream()) {
52+
html =
53+
new String(in.readAllBytes(), StandardCharsets.UTF_8)
54+
.replaceAll("\n", "<br>"); // Otherwise the \n gets escaped in the response
55+
} catch (MalformedURLException e) {
56+
return getFailedResult(e.getMessage());
57+
} catch (IOException e) {
58+
// in case the external site is down, the test and lesson should still be ok
59+
html =
60+
"<html><body>Although the http://ifconfig.pro site is down, you still managed to solve"
61+
+ " this exercise the right way!</body></html>";
62+
}
63+
return success(this).feedback("ssrf.success").output(html).build();
64+
}
65+
var html = "<img class=\"image\" alt=\"image post\" src=\"images/cat.jpg\">";
66+
return getFailedResult(html);
67+
}
68+
69+
private AttackResult getFailedResult(String errorMsg) {
70+
return failed(this).feedback("ssrf.failure").output(errorMsg).build();
71+
}
72+
}

0 commit comments

Comments
 (0)