Skip to content

Commit 1580d23

Browse files
committed
Add models for WordUtils and StrTokenizer
Both of these have commons-text and commons-lang variants.
1 parent fe07630 commit 1580d23

File tree

10 files changed

+429
-0
lines changed

10 files changed

+429
-0
lines changed

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

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,3 +211,58 @@ private class ApacheStrBuilderTaintWriter extends ApacheStrBuilderCallable, Tain
211211
toArg = 2
212212
}
213213
}
214+
215+
private class ApacheWordUtilsTaintPreservingMethod extends TaintPreservingCallable {
216+
ApacheWordUtilsTaintPreservingMethod() {
217+
this.getDeclaringType()
218+
.hasQualifiedName(["org.apache.commons.lang3.text", "org.apache.commons.text"], "WordUtils") and
219+
this.getReturnType() instanceof TypeString
220+
}
221+
222+
override predicate returnsTaintFrom(int arg) {
223+
this.getParameterType(arg) instanceof TypeString and
224+
arg != 4 // Exclude the wrapOn parameter from `wrap(String, int, String, boolean, String)`
225+
}
226+
}
227+
228+
private class ApacheStrTokenizer extends RefType {
229+
ApacheStrTokenizer() {
230+
this.hasQualifiedName(["org.apache.commons.lang3.text", "org.apache.commons.text"],
231+
"StrTokenizer") or
232+
this.hasQualifiedName("org.apache.commons.text", "StringTokenizer")
233+
}
234+
}
235+
236+
/**
237+
* A callable that sets the string to be tokenized by an Apache Commons `Str[ing]Tokenizer`.
238+
*
239+
* Note `reset` is an instance method that taints an existing instance; all others return a fresh instance.
240+
*/
241+
private class ApacheStrTokenizerTaintingMethod extends TaintPreservingCallable {
242+
ApacheStrTokenizerTaintingMethod() {
243+
this.getDeclaringType() instanceof ApacheStrTokenizer and
244+
(
245+
this instanceof Constructor or
246+
this.getName() in ["getCSVInstance", "getTSVInstance", "reset"]
247+
)
248+
}
249+
250+
override predicate returnsTaintFrom(int arg) { arg = 0 }
251+
252+
override predicate transfersTaint(int fromArg, int toArg) {
253+
this.getName() = "reset" and
254+
returnsTaintFrom(fromArg) and
255+
toArg = -1
256+
}
257+
}
258+
259+
private class ApacheStrTokenizerTaintGetter extends TaintPreservingCallable {
260+
ApacheStrTokenizerTaintGetter() {
261+
this.getDeclaringType() instanceof ApacheStrTokenizer and
262+
this.getName() in [
263+
"getContent", "getTokenArray", "getTokenList", "nextToken", "previousToken", "toString"
264+
]
265+
}
266+
267+
override predicate returnsTaintFrom(int arg) { arg = -1 }
268+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import org.apache.commons.lang3.text.StrTokenizer;
2+
import org.apache.commons.lang3.text.StrMatcher;
3+
4+
public class StrTokenizerTest {
5+
String taint() { return "tainted"; }
6+
7+
void sink(Object o) {}
8+
9+
void test() throws Exception {
10+
11+
// Test constructors:
12+
sink((new StrTokenizer(taint().toCharArray())).toString()); // $hasTaintFlow=y
13+
sink((new StrTokenizer(taint().toCharArray(), ',')).toString()); // $hasTaintFlow=y
14+
sink((new StrTokenizer(taint().toCharArray(), ',', '"')).toString()); // $hasTaintFlow=y
15+
sink((new StrTokenizer(taint().toCharArray(), ",")).toString()); // $hasTaintFlow=y
16+
sink((new StrTokenizer(taint().toCharArray(), (StrMatcher)null)).toString()); // $hasTaintFlow=y
17+
sink((new StrTokenizer(taint().toCharArray(), (StrMatcher)null, (StrMatcher)null)).toString()); // $hasTaintFlow=y
18+
sink((new StrTokenizer(taint())).toString()); // $hasTaintFlow=y
19+
sink((new StrTokenizer(taint(), ',')).toString()); // $hasTaintFlow=y
20+
sink((new StrTokenizer(taint(), ',', '"')).toString()); // $hasTaintFlow=y
21+
sink((new StrTokenizer(taint(), ",")).toString()); // $hasTaintFlow=y
22+
sink((new StrTokenizer(taint(), (StrMatcher)null)).toString()); // $hasTaintFlow=y
23+
sink((new StrTokenizer(taint(), (StrMatcher)null, (StrMatcher)null)).toString()); // $hasTaintFlow=y
24+
25+
// Test constructing static methods:
26+
sink(StrTokenizer.getCSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
27+
sink(StrTokenizer.getCSVInstance(taint()).toString()); // $hasTaintFlow=y
28+
sink(StrTokenizer.getTSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
29+
sink(StrTokenizer.getTSVInstance(taint()).toString()); // $hasTaintFlow=y
30+
31+
// Test accessors:
32+
sink((new StrTokenizer(taint())).clone()); // $hasTaintFlow=y
33+
sink((new StrTokenizer(taint())).getContent()); // $hasTaintFlow=y
34+
sink((new StrTokenizer(taint())).getTokenArray()); // $hasTaintFlow=y
35+
sink((new StrTokenizer(taint())).getTokenList()); // $hasTaintFlow=y
36+
sink((new StrTokenizer(taint())).next()); // $hasTaintFlow=y
37+
sink((new StrTokenizer(taint())).nextToken()); // $hasTaintFlow=y
38+
sink((new StrTokenizer(taint())).previous()); // $hasTaintFlow=y
39+
sink((new StrTokenizer(taint())).previousToken()); // $hasTaintFlow=y
40+
41+
// Test mutators:
42+
sink((new StrTokenizer()).reset(taint().toCharArray()).toString()); // $hasTaintFlow=y
43+
sink((new StrTokenizer()).reset(taint()).toString()); // $hasTaintFlow=y
44+
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import org.apache.commons.text.StrTokenizer;
2+
import org.apache.commons.text.StrMatcher;
3+
4+
public class StrTokenizerTextTest {
5+
String taint() { return "tainted"; }
6+
7+
void sink(Object o) {}
8+
9+
void test() throws Exception {
10+
11+
// Test constructors:
12+
sink((new StrTokenizer(taint().toCharArray())).toString()); // $hasTaintFlow=y
13+
sink((new StrTokenizer(taint().toCharArray(), ',')).toString()); // $hasTaintFlow=y
14+
sink((new StrTokenizer(taint().toCharArray(), ',', '"')).toString()); // $hasTaintFlow=y
15+
sink((new StrTokenizer(taint().toCharArray(), ",")).toString()); // $hasTaintFlow=y
16+
sink((new StrTokenizer(taint().toCharArray(), (StrMatcher)null)).toString()); // $hasTaintFlow=y
17+
sink((new StrTokenizer(taint().toCharArray(), (StrMatcher)null, (StrMatcher)null)).toString()); // $hasTaintFlow=y
18+
sink((new StrTokenizer(taint())).toString()); // $hasTaintFlow=y
19+
sink((new StrTokenizer(taint(), ',')).toString()); // $hasTaintFlow=y
20+
sink((new StrTokenizer(taint(), ',', '"')).toString()); // $hasTaintFlow=y
21+
sink((new StrTokenizer(taint(), ",")).toString()); // $hasTaintFlow=y
22+
sink((new StrTokenizer(taint(), (StrMatcher)null)).toString()); // $hasTaintFlow=y
23+
sink((new StrTokenizer(taint(), (StrMatcher)null, (StrMatcher)null)).toString()); // $hasTaintFlow=y
24+
25+
// Test constructing static methods:
26+
sink(StrTokenizer.getCSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
27+
sink(StrTokenizer.getCSVInstance(taint()).toString()); // $hasTaintFlow=y
28+
sink(StrTokenizer.getTSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
29+
sink(StrTokenizer.getTSVInstance(taint()).toString()); // $hasTaintFlow=y
30+
31+
// Test accessors:
32+
sink((new StrTokenizer(taint())).clone()); // $hasTaintFlow=y
33+
sink((new StrTokenizer(taint())).getContent()); // $hasTaintFlow=y
34+
sink((new StrTokenizer(taint())).getTokenArray()); // $hasTaintFlow=y
35+
sink((new StrTokenizer(taint())).getTokenList()); // $hasTaintFlow=y
36+
sink((new StrTokenizer(taint())).next()); // $hasTaintFlow=y
37+
sink((new StrTokenizer(taint())).nextToken()); // $hasTaintFlow=y
38+
sink((new StrTokenizer(taint())).previous()); // $hasTaintFlow=y
39+
sink((new StrTokenizer(taint())).previousToken()); // $hasTaintFlow=y
40+
41+
// Test mutators:
42+
sink((new StrTokenizer()).reset(taint().toCharArray()).toString()); // $hasTaintFlow=y
43+
sink((new StrTokenizer()).reset(taint()).toString()); // $hasTaintFlow=y
44+
45+
}
46+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import org.apache.commons.text.StringTokenizer;
2+
import org.apache.commons.text.matcher.StringMatcher;
3+
4+
public class StringTokenizerTest {
5+
String taint() { return "tainted"; }
6+
7+
void sink(Object o) {}
8+
9+
void test() throws Exception {
10+
11+
// Test constructors:
12+
sink((new StringTokenizer(taint().toCharArray())).toString()); // $hasTaintFlow=y
13+
sink((new StringTokenizer(taint().toCharArray(), ',')).toString()); // $hasTaintFlow=y
14+
sink((new StringTokenizer(taint().toCharArray(), ',', '"')).toString()); // $hasTaintFlow=y
15+
sink((new StringTokenizer(taint().toCharArray(), ",")).toString()); // $hasTaintFlow=y
16+
sink((new StringTokenizer(taint().toCharArray(), (StringMatcher)null)).toString()); // $hasTaintFlow=y
17+
sink((new StringTokenizer(taint().toCharArray(), (StringMatcher)null, (StringMatcher)null)).toString()); // $hasTaintFlow=y
18+
sink((new StringTokenizer(taint())).toString()); // $hasTaintFlow=y
19+
sink((new StringTokenizer(taint(), ',')).toString()); // $hasTaintFlow=y
20+
sink((new StringTokenizer(taint(), ',', '"')).toString()); // $hasTaintFlow=y
21+
sink((new StringTokenizer(taint(), ",")).toString()); // $hasTaintFlow=y
22+
sink((new StringTokenizer(taint(), (StringMatcher)null)).toString()); // $hasTaintFlow=y
23+
sink((new StringTokenizer(taint(), (StringMatcher)null, (StringMatcher)null)).toString()); // $hasTaintFlow=y
24+
25+
// Test constructing static methods:
26+
sink(StringTokenizer.getCSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
27+
sink(StringTokenizer.getCSVInstance(taint()).toString()); // $hasTaintFlow=y
28+
sink(StringTokenizer.getTSVInstance(taint().toCharArray()).toString()); // $hasTaintFlow=y
29+
sink(StringTokenizer.getTSVInstance(taint()).toString()); // $hasTaintFlow=y
30+
31+
// Test accessors:
32+
sink((new StringTokenizer(taint())).clone()); // $hasTaintFlow=y
33+
sink((new StringTokenizer(taint())).getContent()); // $hasTaintFlow=y
34+
sink((new StringTokenizer(taint())).getTokenArray()); // $hasTaintFlow=y
35+
sink((new StringTokenizer(taint())).getTokenList()); // $hasTaintFlow=y
36+
sink((new StringTokenizer(taint())).next()); // $hasTaintFlow=y
37+
sink((new StringTokenizer(taint())).nextToken()); // $hasTaintFlow=y
38+
sink((new StringTokenizer(taint())).previous()); // $hasTaintFlow=y
39+
sink((new StringTokenizer(taint())).previousToken()); // $hasTaintFlow=y
40+
41+
// Test mutators:
42+
sink((new StringTokenizer()).reset(taint().toCharArray()).toString()); // $hasTaintFlow=y
43+
sink((new StringTokenizer()).reset(taint()).toString()); // $hasTaintFlow=y
44+
45+
}
46+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import org.apache.commons.lang3.text.WordUtils;
2+
3+
public class WordUtilsTest {
4+
String taint() { return "tainted"; }
5+
6+
void sink(Object o) {}
7+
8+
void test() throws Exception {
9+
sink(WordUtils.capitalize(taint())); // $hasTaintFlow=y
10+
sink(WordUtils.capitalize(taint(), ' ', ',')); // $hasTaintFlow=y
11+
sink(WordUtils.capitalizeFully(taint())); // $hasTaintFlow=y
12+
sink(WordUtils.capitalizeFully(taint(), ' ', ',')); // $hasTaintFlow=y
13+
sink(WordUtils.initials(taint())); // $hasTaintFlow=y
14+
sink(WordUtils.initials(taint(), ' ', ',')); // $hasTaintFlow=y
15+
sink(WordUtils.swapCase(taint())); // $hasTaintFlow=y
16+
sink(WordUtils.uncapitalize(taint())); // $hasTaintFlow=y
17+
sink(WordUtils.uncapitalize(taint(), ' ', ',')); // $hasTaintFlow=y
18+
sink(WordUtils.wrap(taint(), 0)); // $hasTaintFlow=y
19+
sink(WordUtils.wrap(taint(), 0, "\n", false)); // $hasTaintFlow=y
20+
sink(WordUtils.wrap("wrap me", 0, taint(), false)); // $hasTaintFlow=y
21+
sink(WordUtils.wrap(taint(), 0, "\n", false, "\n")); // $hasTaintFlow=y
22+
sink(WordUtils.wrap("wrap me", 0, taint(), false, "\n")); // $hasTaintFlow=y
23+
// GOOD: the wrap-on line terminator does not propagate to the return value
24+
sink(WordUtils.wrap("wrap me", 0, "\n", false, taint()));
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import org.apache.commons.text.WordUtils;
2+
3+
public class WordUtilsTextTest {
4+
String taint() { return "tainted"; }
5+
6+
void sink(Object o) {}
7+
8+
void test() throws Exception {
9+
sink(WordUtils.abbreviate(taint(), 0, 0, "append me")); // $hasTaintFlow=y
10+
sink(WordUtils.abbreviate("abbreviate me", 0, 0, taint())); // $hasTaintFlow=y
11+
sink(WordUtils.capitalize(taint())); // $hasTaintFlow=y
12+
sink(WordUtils.capitalize(taint(), ' ', ',')); // $hasTaintFlow=y
13+
sink(WordUtils.capitalizeFully(taint())); // $hasTaintFlow=y
14+
sink(WordUtils.capitalizeFully(taint(), ' ', ',')); // $hasTaintFlow=y
15+
sink(WordUtils.initials(taint())); // $hasTaintFlow=y
16+
sink(WordUtils.initials(taint(), ' ', ',')); // $hasTaintFlow=y
17+
sink(WordUtils.swapCase(taint())); // $hasTaintFlow=y
18+
sink(WordUtils.uncapitalize(taint())); // $hasTaintFlow=y
19+
sink(WordUtils.uncapitalize(taint(), ' ', ',')); // $hasTaintFlow=y
20+
sink(WordUtils.wrap(taint(), 0)); // $hasTaintFlow=y
21+
sink(WordUtils.wrap(taint(), 0, "\n", false)); // $hasTaintFlow=y
22+
sink(WordUtils.wrap("wrap me", 0, taint(), false)); // $hasTaintFlow=y
23+
sink(WordUtils.wrap(taint(), 0, "\n", false, "\n")); // $hasTaintFlow=y
24+
sink(WordUtils.wrap("wrap me", 0, taint(), false, "\n")); // $hasTaintFlow=y
25+
// GOOD: the wrap-on line terminator does not propagate to the return value
26+
sink(WordUtils.wrap("wrap me", 0, "\n", false, taint()));
27+
}
28+
}

java/ql/test/stubs/apache-commons-lang3-3.7/org/apache/commons/lang3/text/StrTokenizer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.apache.commons.lang3.text;
1818

1919
import java.util.ArrayList;
20+
import java.util.Arrays;
2021
import java.util.Collections;
2122
import java.util.List;
2223
import java.util.ListIterator;
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.lang3.text;
18+
19+
import java.util.regex.Matcher;
20+
import java.util.regex.Pattern;
21+
22+
23+
public class WordUtils {
24+
public WordUtils() {
25+
}
26+
27+
public static String wrap(final String str, final int wrapLength) {
28+
return null;
29+
}
30+
31+
public static String wrap(final String str, final int wrapLength, final String newLineStr, final boolean wrapLongWords) {
32+
return null;
33+
}
34+
35+
public static String wrap(final String str, int wrapLength, String newLineStr, final boolean wrapLongWords, String wrapOn) {
36+
return null;
37+
}
38+
39+
public static String capitalize(final String str) {
40+
return null;
41+
}
42+
43+
public static String capitalize(final String str, final char... delimiters) {
44+
return null;
45+
}
46+
47+
public static String capitalizeFully(final String str) {
48+
return null;
49+
}
50+
51+
public static String capitalizeFully(String str, final char... delimiters) {
52+
return null;
53+
}
54+
55+
public static String uncapitalize(final String str) {
56+
return null;
57+
}
58+
59+
public static String uncapitalize(final String str, final char... delimiters) {
60+
return null;
61+
}
62+
63+
public static String swapCase(final String str) {
64+
return null;
65+
}
66+
67+
public static String initials(final String str) {
68+
return null;
69+
}
70+
71+
public static String initials(final String str, final char... delimiters) {
72+
return null;
73+
}
74+
75+
public static boolean containsAllWords(final CharSequence word, final CharSequence... words) {
76+
return false;
77+
}
78+
79+
}

java/ql/test/stubs/apache-commons-text-1.9/org/apache/commons/text/StrTokenizer.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@
1616
*/
1717
package org.apache.commons.text;
1818

19+
import java.util.ArrayList;
20+
import java.util.Collections;
1921
import java.util.List;
2022
import java.util.ListIterator;
23+
import java.util.NoSuchElementException;
24+
2125

2226
public class StrTokenizer implements ListIterator<String>, Cloneable {
2327
public static StrTokenizer getCSVInstance() {

0 commit comments

Comments
 (0)