Skip to content

Commit f016b0a

Browse files
authored
Added workaround for children out of order while adding statements. (#351)
- Added a new method to add statements to nodes that preserves children order. This unfortunately causes some small formatting changes. - Enabled cleanup step that removes unused variables for some parameterization codemods. Closes #340.
1 parent 1388e32 commit f016b0a

File tree

11 files changed

+99
-23
lines changed

11 files changed

+99
-23
lines changed

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,8 @@ private Optional<CodemodChange> onNodeFound(
3737
var maybeMethodDecl = methodCallExpr.findAncestor(CallableDeclaration.class);
3838
// Cleanup, removes empty string concatenations and unused variables
3939
maybeMethodDecl.ifPresent(cd -> ASTTransforms.removeEmptyStringConcatenation(cd));
40-
// TODO hits a bug with javaparser, where adding nodes won't result in the correct children
41-
// order. This causes the following to remove actually used variables
42-
// maybeMethodDecl.ifPresent(md -> ASTTransforms.removeUnusedLocalVariables(md));
40+
// Remove potential unused variables left after transform
41+
maybeMethodDecl.ifPresent(md -> ASTTransforms.removeUnusedLocalVariables(md));
4342
return Optional.of(CodemodChange.from(methodCallExpr.getRange().get().begin.line));
4443
}
4544
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ public static boolean checkAndFix(final MethodCallExpr methodCallExpr) {
1313
if (maybeFixed.isPresent()) {
1414
// Cleanup
1515
var maybeMethodDecl = methodCallExpr.findAncestor(CallableDeclaration.class);
16+
1617
// Remove concatenation with empty strings e.g "first" + "" -> "first";
1718
maybeMethodDecl.ifPresent(ASTTransforms::removeEmptyStringConcatenation);
18-
// TODO hits a bug with javaparser, where adding nodes won't result in the correct children
19-
// order. This causes the following to remove actually used variables
20-
// maybeMethodDecl.ifPresent(md -> ASTTransforms.removeUnusedLocalVariables(md));
19+
// Remove potential unused variables left after transform
20+
maybeMethodDecl.ifPresent(md -> ASTTransforms.removeUnusedLocalVariables(md));
2121

2222
// Merge concatenated literals, e.g. "first" + " and second" -> "first and second"
2323
maybeFixed

core-codemods/src/test/resources/defectdojo-sql-injection/SqlInjectionChallenge/SqlInjectionChallenge.java.after

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ public class SqlInjectionChallenge extends AssignmentEndpoint {
6969
PreparedStatement statement = connection.prepareStatement(checkUserQuery);
7070
statement.setString(1, username_reg);
7171
ResultSet resultSet = statement.execute();
72-
7372
if (resultSet.next()) {
7473
if (username_reg.contains("tom'")) {
7574
attackResult = success(this).feedback("user.exists").build();
@@ -85,6 +84,7 @@ public class SqlInjectionChallenge extends AssignmentEndpoint {
8584
preparedStatement.execute();
8685
attackResult = success(this).feedback("user.created").feedbackArgs(username_reg).build();
8786
}
87+
8888
} catch (SQLException e) {
8989
attackResult = failed(this).output("Something went wrong").build();
9090
}

core-codemods/src/test/resources/defectdojo-sql-injection/SqlInjectionLesson8/SqlInjectionLesson8.java.after

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDAT
7575
statement.setString(1, name);
7676
statement.setString(2, auth_tan);
7777
ResultSet results = statement.execute();
78-
7978
if (results.getStatement() != null) {
8079
if (results.first()) {
8180
output.append(generateTable(results));
@@ -99,6 +98,7 @@ query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDAT
9998
} else {
10099
return failed(this).build();
101100
}
101+
102102
} catch (SQLException e) {
103103
return failed(this)
104104
.output("<br><span class='feedback-negative'>" + e.getMessage() + "</span>")
@@ -147,7 +147,6 @@ query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDAT
147147
action = action.replace('\'', '"');
148148
Calendar cal = Calendar.getInstance();
149149
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
150-
String time = sdf.format(cal.getTime());
151150

152151
String logQuery =
153152
"INSERT INTO access_log (time, action) VALUES (?, ?)";
@@ -157,7 +156,7 @@ query, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDAT
157156
statement.setString(1, sdf.format(cal.getTime()));
158157
statement.setString(2, action);
159158
statement.execute();
160-
} catch (SQLException e) {
159+
} catch (SQLException e) {
161160
System.err.println(e.getMessage());
162161
}
163162
}

core-codemods/src/test/resources/hql-parameterizer/Test.java.after

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public final class Test {
2222
}
2323

2424
void indirectMultipeString(String tainted, String tainted2) {
25-
String third = "";
2625
String second = " and p.surname like :parameter1";
2726
String first = "select p from Person p where p.name like :parameter0" + second;
2827
Query<User> hqlQuery = session.createQuery(first).setParameter(":parameter0", tainted).setParameter(":parameter1", tainted2);

core-codemods/src/test/resources/hql-parameterizer/Test.java.before

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,26 @@ public final class Test {
99

1010
void direct(String tainted) {
1111
Query<User> hqlQuery = session.createQuery("select p from Person p where p.name like '" + tainted + "'");
12+
List l = hqlQuery.list();
1213
}
1314

1415
void indirect(String tainted) {
1516
String query = "select p from Person p where p.name like '" + tainted + "'";
1617
Query<User> hqlQuery = session.createQuery(query);
18+
List l = hqlQuery.list();
1719
}
1820

1921
void indirectMultiple(String tainted, String tainted2) {
2022
String query = "select p from Person p where p.name like '" + tainted + "' and p.surname like '" + tainted2 + "'";
2123
Query<User> hqlQuery = session.createQuery(query);
24+
List l = hqlQuery.list();
2225
}
2326

2427
void indirectMultipeString(String tainted, String tainted2) {
2528
String third = "'";
2629
String second = "' and p.surname like '" + tainted2 + third;
2730
String first = "select p from Person p where p.name like '" + tainted + second;
2831
Query<User> hqlQuery = session.createQuery(first);
32+
List l = hqlQuery.list();
2933
}
3034
}

core-codemods/src/test/resources/jexl-expression-injection/Test.java.after

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public final class Test {
2727
JexlExpression expression = jexl.createExpression(input);
2828
JexlContext context = new MapContext();
2929
expression.evaluate(context);
30+
3031
}
3132
}
3233

@@ -41,6 +42,7 @@ public final class Test {
4142
sandbox.block(cls);
4243
}
4344
new JexlBuilder().sandbox(sandbox).create().createExpression(input).evaluate(context);
45+
4446
}
4547
}
4648

core-codemods/src/test/resources/sql-parameterizer/Test.java.after

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,17 @@ public final class Test {
2323
stmt.setString(1, input);
2424
var rs = stmt.execute();
2525
return rs;
26-
}
26+
}
2727

2828
public ResultSet nameConflict(String input) throws SQLException {
2929
int stmt = 0;
3030
String sql = "SELECT * FROM USERS WHERE USER = ?";
3131
PreparedStatement statement = conn.prepareStatement(sql);
3232
statement.setString(1, input);
3333
ResultSet rs = statement.execute();
34+
stmt++;
3435
return rs;
35-
}
36+
}
3637

3738
public ResultSet doubleNameConflict(String input) throws SQLException {
3839
int stmt = 0;
@@ -41,8 +42,9 @@ public final class Test {
4142
PreparedStatement stmt1 = conn.prepareStatement(sql);
4243
stmt1.setString(1, input);
4344
ResultSet rs = stmt1.execute();
45+
stmt = stmt + statement;
4446
return rs;
45-
}
47+
}
4648

4749
public ResultSet tryResource(String input) throws SQLException {
4850
String sql = "SELECT * FROM USERS WHERE USER = ?";
@@ -60,7 +62,7 @@ public final class Test {
6062
stmt.setString(1, "user_" + input + "_name");
6163
stmt.setString(2, input2);
6264
return stmt.execute();
63-
}
65+
}
6466

6567
public ResultSet referencesAfterExecute(String input) throws SQLException {
6668
String sql = "SELECT * FROM USERS WHERE USER = ?";

core-codemods/src/test/resources/sql-parameterizer/Test.java.before

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public final class Test {
2525
int stmt = 0;
2626
String sql = "SELECT * FROM USERS WHERE USER = '" + input + "'";
2727
ResultSet rs = conn.createStatement().executeQuery(sql);
28+
stmt++;
2829
return rs;
2930
}
3031

@@ -33,6 +34,7 @@ public final class Test {
3334
int statement = 0;
3435
String sql = "SELECT * FROM USERS WHERE USER = '" + input + "'";
3536
ResultSet rs = conn.createStatement().executeQuery(sql);
37+
stmt = stmt + statement;
3638
return rs;
3739
}
3840

framework/codemodder-base/src/main/java/io/codemodder/ast/ASTTransforms.java

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.github.javaparser.ast.nodeTypes.NodeWithBody;
1616
import com.github.javaparser.ast.nodeTypes.NodeWithName;
1717
import com.github.javaparser.ast.nodeTypes.NodeWithSimpleName;
18+
import com.github.javaparser.ast.nodeTypes.NodeWithStatements;
1819
import com.github.javaparser.ast.stmt.BlockStmt;
1920
import com.github.javaparser.ast.stmt.ExpressionStmt;
2021
import com.github.javaparser.ast.stmt.IfStmt;
@@ -23,7 +24,9 @@
2324
import com.github.javaparser.ast.stmt.TryStmt;
2425
import com.github.javaparser.ast.type.ClassOrInterfaceType;
2526
import com.github.javaparser.resolution.types.ResolvedType;
27+
import java.util.ArrayList;
2628
import java.util.Optional;
29+
import java.util.stream.IntStream;
2730

2831
public final class ASTTransforms {
2932
/** Add an import in alphabetical order. */
@@ -76,6 +79,34 @@ public static void addStaticImportIfMissing(final CompilationUnit cu, final Stri
7679
cu.addImport(method, true, false);
7780
}
7881

82+
/**
83+
* Adds a statement to a node at a given index. Mostly a workaround for a weird behavior of
84+
* JavaParser. See https://github.com/javaparser/javaparser/issues/4370.
85+
*/
86+
public static <N extends Node> void addStatementAt(
87+
final NodeWithStatements<N> node, final Statement stmt, final int index) {
88+
var newStatements = new ArrayList<Statement>();
89+
int i = 0;
90+
for (var s : node.getStatements()) {
91+
if (i == index) {
92+
newStatements.add(stmt);
93+
}
94+
newStatements.add(s);
95+
i++;
96+
}
97+
98+
// rebuilds the whole statements list as to preserve proper children order.
99+
100+
// workaround for maintaining indent, removes all but the first
101+
IntStream.range(0, node.getStatements().size() - 1)
102+
.forEach(j -> node.getStatements().removeLast());
103+
// replace the first with the new statement if needed
104+
if (index == 0) {
105+
node.getStatements().get(0).replace(stmt);
106+
}
107+
newStatements.stream().skip(1).forEach(node.getStatements()::add);
108+
}
109+
79110
/**
80111
* Adds an {@link Statement} before another {@link Statement}. Single {@link Statement}s in the
81112
* body of For/Do/While/If/Labeled Statements are replaced with a {@link BlockStmt} containing
@@ -105,14 +136,9 @@ public static void addStatementBeforeStatement(
105136
// NodeWithBody, MethodDeclaration, NodeWithBlockStmt, SwitchStmt(?)
106137
// No way (or reason) to add a Statement before those, maybe throw an Error?
107138
final var block = (BlockStmt) parent;
108-
// Workaround for a bug on LexicalPreservingPrinter (see
109-
// https://github.com/javaparser/javaparser/issues/3746)
110-
if (existingStatement.equals(block.getStatement(0))) {
111-
existingStatement.replace(newStatement);
112-
block.addStatement(1, existingStatement);
113-
} else {
114-
block.getStatements().addBefore(newStatement, existingStatement);
115-
}
139+
140+
int existingIndex = block.getStatements().indexOf(existingStatement);
141+
addStatementAt(block, newStatement, existingIndex);
116142
}
117143
}
118144

0 commit comments

Comments
 (0)