Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 55c72ce

Browse files
committed
Improve StringBuilder append chain tracking
Previously this didn't catch the case of constructors chaining directly into appends, like `StringBuilder sb = new StringBuilder("1").append("2")`
1 parent 5b25694 commit 55c72ce

3 files changed

Lines changed: 183 additions & 77 deletions

File tree

java/ql/src/semmle/code/java/security/RequestForgery.qll

Lines changed: 81 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -248,20 +248,85 @@ private Expr getAHostnameSanitizingPrefix() {
248248
result.(AddExpr).getAnOperand() = getAHostnameSanitizingPrefix()
249249
}
250250

251-
private MethodAccess getNextAppend(MethodAccess append) {
252-
result = any(StringBuilderVar sbv).getNextAppend(append)
251+
private class StringBuilderAppend extends MethodAccess {
252+
StringBuilderAppend() {
253+
this.getMethod().getDeclaringType() instanceof StringBuildingType and
254+
this.getMethod().hasName("append")
255+
}
253256
}
254257

255-
private Expr getQualifier(MethodAccess e) { result = e.getQualifier() }
258+
private class StringBuilderConstructorOrAppend extends Call {
259+
StringBuilderConstructorOrAppend() {
260+
this instanceof StringBuilderAppend or
261+
this.(ClassInstanceExpr).getConstructedType() instanceof StringBuildingType
262+
}
263+
}
264+
265+
private Expr getQualifier(Expr e) { result = e.(MethodAccess).getQualifier() }
266+
267+
/**
268+
* An extension of `StringBuilderVar` that also accounts for strings appended in StringBuilder/Buffer's constructor
269+
* and in `append` calls chained onto the constructor call.
270+
*
271+
* The original `StringBuilderVar` doesn't care about these because it is designed to model taint, and
272+
* in taint rules terms these are not needed, as the connection between construction, appends and the
273+
* eventual `toString` is more obvious.
274+
*/
275+
private class StringBuilderVarExt extends StringBuilderVar {
276+
/**
277+
* Returns a first assignment after this StringBuilderVar is first assigned.
278+
*
279+
* For example, for `StringBuilder sbv = new StringBuilder("1").append("2"); sbv.append("3").append("4");`
280+
* this returns the append of `"3"`.
281+
*/
282+
private StringBuilderAppend getAFirstAppendAfterAssignment() {
283+
//
284+
result = this.getAnAppend() and not result = this.getNextAppend(_)
285+
}
286+
287+
/**
288+
* Gets the next `append` after `prev`, where `prev` is, perhaps after some more `append` or other
289+
* chained calls, assigned to this `StringBuilderVar`.
290+
*/
291+
private StringBuilderAppend getNextAssignmentChainedAppend(StringBuilderConstructorOrAppend prev) {
292+
getQualifier*(result) = this.getAnAssignedValue() and
293+
result.getQualifier() = prev
294+
}
295+
296+
/**
297+
* Get a constructor call or `append` call that contributes a string to this string builder.
298+
*/
299+
StringBuilderConstructorOrAppend getAConstructorOrAppend() {
300+
exists(this.getNextAssignmentChainedAppend(result)) or
301+
result = this.getAnAssignedValue() or
302+
result = this.getAnAppend()
303+
}
256304

257-
private MethodAccess getAChainedAppend(Expr e) {
258-
(
259-
result.getQualifier() = e
305+
/**
306+
* Like `StringBuilderVar.getNextAppend`, except including appends and constructors directly
307+
* assigned to this `StringBuilderVar`.
308+
*/
309+
private StringBuilderAppend getNextAppendIncludingAssignmentChains(
310+
StringBuilderConstructorOrAppend prev
311+
) {
312+
result = getNextAssignmentChainedAppend(prev)
313+
or
314+
prev = this.getAnAssignedValue() and
315+
result = this.getAFirstAppendAfterAssignment()
260316
or
261-
result.getQualifier() = getAChainedAppend(e)
262-
) and
263-
result.getCallee().getDeclaringType() instanceof StringBuildingType and
264-
result.getCallee().getName() = "append"
317+
result = this.getNextAppend(prev)
318+
}
319+
320+
/**
321+
* Implements `StringBuilderVarExt.getNextAppendIncludingAssignmentChains+(prev)`.
322+
*/
323+
StringBuilderAppend getSubsequentAppendIncludingAssignmentChains(
324+
StringBuilderConstructorOrAppend prev
325+
) {
326+
result = this.getNextAppendIncludingAssignmentChains(prev) or
327+
result =
328+
this.getSubsequentAppendIncludingAssignmentChains(this.getNextAppendIncludingAssignmentChains(prev))
329+
}
265330
}
266331

267332
/**
@@ -275,25 +340,14 @@ private class HostnameSanitizedExpr extends Expr {
275340
this =
276341
any(AddExpr add | add.getLeftOperand() = getAHostnameSanitizingPrefix()).getRightOperand()
277342
or
278-
// Sanitize all appends to a StringBuilder that is initialized with a sanitizing prefix:
279-
// (note imprecision: if the same StringBuilder/StringBuffer has more than one constructor call,
280-
// this sanitizes all of its append calls, not just those that may follow the constructor).
281-
exists(StringBuilderVar sbv, ConstructorCall constructor, Expr initializer |
282-
initializer = sbv.getAnAssignedValue() and
283-
constructor = getQualifier*(initializer) and
284-
constructor.getArgument(0) = getAHostnameSanitizingPrefix() and
285-
(
286-
this = sbv.getAnAppend().getArgument(0)
287-
or
288-
this = getAChainedAppend(constructor).getArgument(0)
289-
)
290-
)
291-
or
292343
// Sanitize expressions that come after a sanitizing prefix in a sequence of StringBuilder operations:
293-
exists(MethodAccess appendSanitizingConstant, MethodAccess subsequentAppend |
294-
appendSanitizingConstant = any(StringBuilderVar v).getAnAppend() and
344+
exists(
345+
StringBuilderConstructorOrAppend appendSanitizingConstant,
346+
StringBuilderAppend subsequentAppend, StringBuilderVarExt v
347+
|
348+
appendSanitizingConstant = v.getAConstructorOrAppend() and
295349
appendSanitizingConstant.getArgument(0) = getAHostnameSanitizingPrefix() and
296-
getNextAppend*(appendSanitizingConstant) = subsequentAppend and
350+
v.getSubsequentAppendIncludingAssignmentChains(appendSanitizingConstant) = subsequentAppend and
297351
this = subsequentAppend.getArgument(0)
298352
)
299353
or

0 commit comments

Comments
 (0)