Parameterize into methodcalls on object¶
The methodOnObject
extraction will extract the untrusted input into method calls.
A well known example of this type of binding is the
SQL Prepared Statement API.
methods¶
The methods property allows the configuration of what methods should be used when extracting the
untrusted input. Each method can be configured with a name and the arguments it should have. The
type
property specifies for what type of untrusted input this method should be used.
Note
Not adding a type
means that method will be used for all elements that don't have a specific
method defined for its type.
parameterize:
placeholderFormat: "?"
extractUntrustedInput:
methodsOnObject:
methods:
- type: "java.lang.String"
methodName: "setString"
args:
1: "{{{index}}}"
2: "{{{.}}}"
- type: "int"
methodName: "setInt"
args:
1: "{{{index}}}"
2: "{{{.}}}"
target:
returnValue: {}
- String sql = "SELECT count(*) FROM feedback WHERE email = '" + email + "' AND id = '" + id + "'";
+ String sql = "SELECT count(*) FROM feedback WHERE email = ? AND id = ?";
- Statement statement = this.connection.createStatement();
- ResultSet resultSet = statement.executeQuery(sql);
+ PreparedStatement statement = this.connection.prepareStatement(sql);
+ statement.setString(1, email);
+ statement.setInt(2, id);
+ ResultSet resultSet = statement.executeQuery();
resultSet.next();
return resultSet.getInt(0);
Some variables that can be used:
variable |
functionality |
---|---|
|
starts at 1 and increments for each untrusted input |
|
starts at 0 and increments for each untrusted input |
|
same as |
|
a suitable name for the untrusted input it replaces |
Warning
Only {{{index}}}
is available on Sensei versions prior to 4.25
target¶
To have configurability it is possible to change the target. The target is the element on which the methods will be called.
argument¶
The argument target takes one of the arguments and places the binding methods on that argument. Usages may include collecting elements inside a Map or List.
parameterize:
placeholderFormat: ":{{{name}}}"
extractUntrustedInput:
methodsOnObject:
methods:
- methodName: "put"
args:
1: "\"{{{name}}}\""
2: "{{{.}}}"
target:
argument:
type: java.util.HashMap<String, Object>
atArgumentPosition: 2
- String sql = "SELECT * FROM users WHERE email = '" + email + "'";
+ String sql = "SELECT * FROM users WHERE email = :email";
+ HashMap<String, Object> hashMap = new HashMap<>();
+ hashMap.put("email", email);
- getSimpleJdbcTemplate().update(sql);
+ getSimpleJdbcTemplate().update(sql, hashMap);
Note
atArgumentPosition
is 1-based, not 0-based.
Note
type
is only used to create a new variable when there is no existing argument present at the
specified position. When neither argument or type are present, Sensei will try to determine the
type of the new variable based of the method declarations available.
returnValue¶
The returnValue
target uses the return value of the marked element as a subject to perform the
binding method calls on.
parameterize:
placeholderFormat: ":{{{name}}}"
extractUntrustedInput:
methodsOnObject:
methods:
- methodName: "bind"
args:
1: "\"{{{name}}}\""
2: "{{{.}}}"
target:
returnValue:
variableName: "myQuery"
useMethodChaining: false
abstract Query createQuery(String sql);
abstract Query bind(String key, Object value);
public User findUser(String email) {
- String sql = "SELECT * FROM users WHERE email = '" + email + "'";
+ String sql = "SELECT * FROM users WHERE email = :email";
- createQuery(sql);
+ Query myQuery = createQuery(sql);
+ myQuery.bind("email", email);
}
The useMethodChaining
property can be used to change or enforce the behaviour when adding the method
bindings. Sensei will check if method chaining is available and use it by default. Placing
useMethodChaining
explicitly to false
will force Sensei to create a variable to use as a subject
to the binding methods, for this the variableName
property will be used.
subject¶
It uses the subject of the marked element and applies the method bindings on this subject.
Note
The subject
target is only available when the marked element is a method call.
availableFixes:
- actions:
- parameterize:
placeholderFormat: ""
extractUntrustedInput:
methodsOnObject:
methods:
- methodName: "appendWhereEscapeString"
args:
"1": "{{{.}}}"
target:
subject:
insertBefore: false
User fetchUser(String id) {
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
builder.setTables(TABLE_USERS);
- builder.appendWhere("id = " + id);
+ builder.appendWhere("id = ");
+ builder.appendWhereEscapeString(id);
// ...
}
In the example below the insertBefore
property is used to add the setString
binding before the
marked method call (statement.executeQuery
).
search:
methodcall:
name:
matches: execute.*
declaration:
type: java.sql.Statement
args:
1:
containsUntrustedInput: true
type: java.lang.String
public int getFeedbackCount(String email)
{
String sql = "SELECT count(*) FROM feedback WHERE email = '" + email + "'";
Statement statement = this.connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
resultSet.next();
return resultSet.getInt(0);
}
availableFixes:
- name: "Use parameterized queries"
actions:
- parameterize:
placeholderFormat: "?"
extractUntrustedInput:
methodsOnObject:
methods:
- type: "java.lang.String"
methodName: "setString"
args:
"1": "{{{ index }}}"
"2": "{{{ . }}}"
target:
subject:
insertBefore: true
- changeTypeOfCallObject:
rewriteLastAssignment: "{{{ qualifier }}}.prepareStatement({{{ markedElement.arguments.0}}}
{{#arguments}},\\ {{{.}}}{{/arguments}})"
type: "java.sql.PreparedStatement"
- modifyArguments:
remove:
- 1
public int getFeedbackCount(String email)
{
- String sql = "SELECT count(*) FROM feedback WHERE email = '" + email + "'";
+ String sql = "SELECT count(*) FROM feedback WHERE email = ?";
- Statement statement = this.connection.createStatement();
+ PreparedStatement statement = this.connection.prepareStatement(sql);
+ statement.setString(1, email);
- ResultSet resultSet = statement.executeQuery(sql);
+ ResultSet resultSet = statement.executeQuery();
resultSet.next();
return resultSet.getInt(0);
}