Skip to content

Commit a2eeffa

Browse files
committed
Add support for Apache Commons Lang StringUtils
1 parent bf03c0f commit a2eeffa

File tree

7 files changed

+1329
-0
lines changed

7 files changed

+1329
-0
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* Added support for the Apache Commons Lang StringUtils library.

java/ql/src/semmle/code/java/frameworks/apache/Lang.qll

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,58 @@ private class ApacheLangArrayUtilsTaintPreservingMethod extends TaintPreservingC
6363
src = [0, 2]
6464
}
6565
}
66+
67+
private Type getAnExcludedParameterType() {
68+
result instanceof PrimitiveType or
69+
result.(RefType).hasQualifiedName("java.nio.charset", "Charset") or
70+
result.(RefType).hasQualifiedName("java.util", "Locale")
71+
}
72+
73+
private class ApacheStringUtilsTaintPreservingMethod extends TaintPreservingCallable {
74+
ApacheStringUtilsTaintPreservingMethod() {
75+
this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3", "StringUtils") and
76+
this.hasName([
77+
"abbreviate", "abbreviateMiddle", "appendIfMissing", "appendIfMissingIgnoreCase",
78+
"capitalize", "center", "chomp", "chop", "defaultIfBlank", "defaultIfEmpty",
79+
"defaultString", "deleteWhitespace", "difference", "firstNonBlank", "firstNonEmpty",
80+
"getBytes", "getCommonPrefix", "getDigits", "getIfBlank", "getIfEmpty", "join", "joinWith",
81+
"left", "leftPad", "lowerCase", "mid", "normalizeSpace", "overlay", "prependIfMissing",
82+
"prependIfMissingIgnoreCase", "remove", "removeAll", "removeEnd", "removeEndIgnoreCase",
83+
"removeFirst", "removeIgnoreCase", "removePattern", "removeStart", "removeStartIgnoreCase",
84+
"repeat", "replace", "replaceAll", "replaceChars", "replaceEach", "replaceEachRepeatedly",
85+
"replaceFirst", "replaceIgnoreCase", "replaceOnce", "replaceOnceIgnoreCase",
86+
"replacePattern", "reverse", "reverseDelimited", "right", "rightPad", "rotate", "split",
87+
"splitByCharacterType", "splitByCharacterTypeCamelCase", "splitByWholeSeparator",
88+
"splitByWholeSeparatorPreserveAllTokens", "splitPreserveAllTokens", "strip", "stripAccents",
89+
"stripAll", "stripEnd", "stripStart", "stripToEmpty", "stripToNull", "substring",
90+
"substringAfter", "substringAfterLast", "substringBefore", "substringBeforeLast",
91+
"substringBetween", "substringsBetween", "swapCase", "toCodePoints", "toEncodedString",
92+
"toRootLowerCase", "toRootUpperCase", "toString", "trim", "trimToEmpty", "trimToNull",
93+
"truncate", "uncapitalize", "unwrap", "upperCase", "valueOf", "wrap", "wrapIfMissing"
94+
])
95+
}
96+
97+
private predicate isExcludedParameter(int arg) {
98+
this.getName().matches(["appendIfMissing%", "prependIfMissing%"]) and arg = [2, 3]
99+
or
100+
this.getName().matches(["remove%", "split%", "substring%", "strip%"]) and
101+
arg = [1 .. getNumberOfParameters() - 1]
102+
or
103+
this.getName().matches(["chomp", "getBytes", "replace%", "toString", "unwrap"]) and arg = 1
104+
or
105+
this.getName() = "join" and
106+
// Exclude joins of types that render numerically (char[] and non-primitive arrays
107+
// are still considered taint sources)
108+
exists(PrimitiveType pt |
109+
this.getParameterType(arg).(Array).getComponentType() = pt and
110+
not pt instanceof CharacterType
111+
) and
112+
arg = 0
113+
}
114+
115+
override predicate returnsTaintFrom(int arg) {
116+
arg = [0 .. getNumberOfParameters() - 1] and
117+
not this.getParameterType(arg) = getAnExcludedParameterType() and
118+
not isExcludedParameter(arg)
119+
}
120+
}

java/ql/test/library-tests/frameworks/apache-commons-lang3/Test.java

Lines changed: 278 additions & 0 deletions
Large diffs are not rendered by default.

java/ql/test/library-tests/frameworks/apache-commons-lang3/flow.expected

Whitespace-only changes.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import java
2+
import semmle.code.java.dataflow.TaintTracking
3+
import TestUtilities.InlineExpectationsTest
4+
5+
class Conf extends TaintTracking::Configuration {
6+
Conf() { this = "qltest:frameworks:apache-commons-lang3" }
7+
8+
override predicate isSource(DataFlow::Node n) {
9+
n.asExpr().(MethodAccess).getMethod().hasName("taint")
10+
}
11+
12+
override predicate isSink(DataFlow::Node n) {
13+
exists(MethodAccess ma | ma.getMethod().hasName("sink") | n.asExpr() = ma.getAnArgument())
14+
}
15+
}
16+
17+
class HasFlowTest extends InlineExpectationsTest {
18+
HasFlowTest() { this = "HasFlowTest" }
19+
20+
override string getARelevantTag() { result = "hasTaintFlow" }
21+
22+
override predicate hasActualResult(Location location, string element, string tag, string value) {
23+
tag = "hasTaintFlow" and
24+
exists(DataFlow::Node src, DataFlow::Node sink, Conf conf | conf.hasFlow(src, sink) |
25+
sink.getLocation() = location and
26+
element = sink.toString() and
27+
value = "y"
28+
)
29+
}
30+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//semmle-extractor-options: --javac-args -cp ${testdir}/../../../stubs/apache-commons-lang3-3.7

0 commit comments

Comments
 (0)