Skip to content

Commit b1bed27

Browse files
authored
Merge pull request github#5172 from smowton/smowton/feature/commons-strbuilder
Java: Add support for commons-lang's StrBuilder class
2 parents 53711dc + 321df82 commit b1bed27

File tree

16 files changed

+3470
-1
lines changed

16 files changed

+3470
-1
lines changed

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

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,96 @@ private class ApacheStringUtilsTaintPreservingMethod extends TaintPreservingCall
118118
not isExcludedParameter(arg)
119119
}
120120
}
121+
122+
/**
123+
* A method declared on Apache Commons Lang's `StrBuilder`, or the same class or its
124+
* renamed version `TextStringBuilder` in Commons Text.
125+
*/
126+
class ApacheStrBuilderCallable extends Callable {
127+
ApacheStrBuilderCallable() {
128+
this.getDeclaringType().hasQualifiedName("org.apache.commons.lang3.text", "StrBuilder") or
129+
this.getDeclaringType()
130+
.hasQualifiedName("org.apache.commons.text", ["StrBuilder", "TextStringBuilder"])
131+
}
132+
}
133+
134+
/**
135+
* An Apache Commons Lang `StrBuilder` method that adds taint to the `StrBuilder`.
136+
*/
137+
private class ApacheStrBuilderTaintingMethod extends ApacheStrBuilderCallable,
138+
TaintPreservingCallable {
139+
ApacheStrBuilderTaintingMethod() {
140+
this instanceof Constructor
141+
or
142+
this.hasName([
143+
"append", "appendAll", "appendFixedWidthPadLeft", "appendFixedWidthPadRight", "appendln",
144+
"appendSeparator", "appendWithSeparators", "insert", "readFrom", "replace", "replaceAll",
145+
"replaceFirst"
146+
])
147+
}
148+
149+
private predicate consumesTaintFromAllArgs() {
150+
// Specifically the append[ln](String, Object...) overloads also consume taint from their other arguments:
151+
this.getName() in ["appendAll", "appendWithSeparators"]
152+
or
153+
this.getName() = ["append", "appendln"] and this.getAParameter().isVarargs()
154+
or
155+
this.getName() = "appendSeparator" and this.getParameterType(1) instanceof TypeString
156+
}
157+
158+
override predicate transfersTaint(int fromArg, int toArg) {
159+
// Taint the qualifier
160+
toArg = -1 and
161+
(
162+
this.getName().matches(["append%", "readFrom"]) and fromArg = 0
163+
or
164+
this.getName() = "insert" and fromArg = 1
165+
or
166+
this.getName().matches("replace%") and
167+
(
168+
if this.getParameterType(0).(PrimitiveType).getName() = "int"
169+
then fromArg = 2
170+
else fromArg = 1
171+
)
172+
or
173+
this.consumesTaintFromAllArgs() and fromArg in [0 .. this.getNumberOfParameters() - 1]
174+
)
175+
}
176+
177+
override predicate returnsTaintFrom(int arg) { this instanceof Constructor and arg = 0 }
178+
}
179+
180+
/**
181+
* An Apache Commons Lang `StrBuilder` method that returns taint from the `StrBuilder`.
182+
*/
183+
private class ApacheStrBuilderTaintGetter extends ApacheStrBuilderCallable, TaintPreservingCallable {
184+
ApacheStrBuilderTaintGetter() {
185+
// Taint getters:
186+
this.hasName([
187+
"asReader", "asTokenizer", "build", "getChars", "leftString", "midString", "rightString",
188+
"subSequence", "substring", "toCharArray", "toString", "toStringBuffer", "toStringBuilder"
189+
])
190+
or
191+
// Fluent methods that return an alias of `this`:
192+
this.getReturnType() = this.getDeclaringType()
193+
}
194+
195+
override predicate returnsTaintFrom(int arg) { arg = -1 }
196+
}
197+
198+
/**
199+
* An Apache Commons Lang `StrBuilder` method that writes taint from the `StrBuilder` to some parameter.
200+
*/
201+
private class ApacheStrBuilderTaintWriter extends ApacheStrBuilderCallable, TaintPreservingCallable {
202+
ApacheStrBuilderTaintWriter() { this.hasName(["appendTo", "getChars"]) }
203+
204+
override predicate transfersTaint(int fromArg, int toArg) {
205+
fromArg = -1 and
206+
// appendTo(Readable) and getChars(char[])
207+
if this.getNumberOfParameters() = 1
208+
then toArg = 0
209+
else
210+
// getChars(int, int, char[], int)
211+
toArg = 2
212+
}
213+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import org.apache.commons.lang3.text.StrBuilder;
2+
import org.apache.commons.lang3.text.StrMatcher;
3+
import org.apache.commons.lang3.text.StrTokenizer;
4+
import java.io.StringReader;
5+
import java.nio.CharBuffer;
6+
import java.util.ArrayList;
7+
import java.util.List;
8+
import java.util.Locale;
9+
10+
class StrBuilderTest {
11+
String taint() { return "tainted"; }
12+
13+
void sink(Object o) {}
14+
15+
void test() throws Exception {
16+
17+
StrBuilder cons1 = new StrBuilder(taint()); sink(cons1.toString()); // $hasTaintFlow=y
18+
19+
StrBuilder sb1 = new StrBuilder(); sb1.append(taint().toCharArray()); sink(sb1.toString()); // $hasTaintFlow=y
20+
StrBuilder sb2 = new StrBuilder(); sb2.append(taint().toCharArray(), 0, 0); sink(sb2.toString()); // $hasTaintFlow=y
21+
StrBuilder sb3 = new StrBuilder(); sb3.append(CharBuffer.wrap(taint().toCharArray())); sink(sb3.toString()); // $ MISSING: hasTaintFlow=y
22+
StrBuilder sb4 = new StrBuilder(); sb4.append(CharBuffer.wrap(taint().toCharArray()), 0, 0); sink(sb4.toString()); // $ MISSING: hasTaintFlow=y
23+
StrBuilder sb5 = new StrBuilder(); sb5.append((CharSequence)taint()); sink(sb5.toString()); // $hasTaintFlow=y
24+
StrBuilder sb6 = new StrBuilder(); sb6.append((CharSequence)taint(), 0, 0); sink(sb6.toString()); // $hasTaintFlow=y
25+
StrBuilder sb7 = new StrBuilder(); sb7.append((Object)taint()); sink(sb7.toString()); // $hasTaintFlow=y
26+
{
27+
StrBuilder auxsb = new StrBuilder(); auxsb.append(taint());
28+
StrBuilder sb8 = new StrBuilder(); sb8.append(auxsb); sink(sb8.toString()); // $hasTaintFlow=y
29+
}
30+
StrBuilder sb9 = new StrBuilder(); sb9.append(new StringBuffer(taint())); sink(sb9.toString()); // $hasTaintFlow=y
31+
StrBuilder sb10 = new StrBuilder(); sb10.append(new StringBuffer(taint()), 0, 0); sink(sb10.toString()); // $hasTaintFlow=y
32+
StrBuilder sb11 = new StrBuilder(); sb11.append(new StringBuilder(taint())); sink(sb11.toString()); // $hasTaintFlow=y
33+
StrBuilder sb12 = new StrBuilder(); sb12.append(new StringBuilder(taint()), 0, 0); sink(sb12.toString()); // $hasTaintFlow=y
34+
StrBuilder sb13 = new StrBuilder(); sb13.append(taint()); sink(sb13.toString()); // $hasTaintFlow=y
35+
StrBuilder sb14 = new StrBuilder(); sb14.append(taint(), 0, 0); sink(sb14.toString()); // $hasTaintFlow=y
36+
StrBuilder sb15 = new StrBuilder(); sb15.append(taint(), "format", "args"); sink(sb15.toString()); // $hasTaintFlow=y
37+
StrBuilder sb16 = new StrBuilder(); sb16.append("Format string", taint(), "args"); sink(sb16.toString()); // $hasTaintFlow=y
38+
{
39+
List<String> taintedList = new ArrayList<>();
40+
taintedList.add(taint());
41+
StrBuilder sb17 = new StrBuilder(); sb17.appendAll(taintedList); sink(sb17.toString()); // $hasTaintFlow=y
42+
StrBuilder sb18 = new StrBuilder(); sb18.appendAll(taintedList.iterator()); sink(sb18.toString()); // $hasTaintFlow=y
43+
}
44+
StrBuilder sb19 = new StrBuilder(); sb19.appendAll("clean", taint()); sink(sb19.toString()); // $hasTaintFlow=y
45+
StrBuilder sb20 = new StrBuilder(); sb20.appendAll(taint(), "clean"); sink(sb20.toString()); // $hasTaintFlow=y
46+
StrBuilder sb21 = new StrBuilder(); sb21.appendFixedWidthPadLeft(taint(), 0, ' '); sink(sb21.toString()); // $hasTaintFlow=y
47+
StrBuilder sb22 = new StrBuilder(); sb22.appendFixedWidthPadRight(taint(), 0, ' '); sink(sb22.toString()); // $hasTaintFlow=y
48+
StrBuilder sb23 = new StrBuilder(); sb23.appendln(taint().toCharArray()); sink(sb23.toString()); // $hasTaintFlow=y
49+
StrBuilder sb24 = new StrBuilder(); sb24.appendln(taint().toCharArray(), 0, 0); sink(sb24.toString()); // $hasTaintFlow=y
50+
StrBuilder sb25 = new StrBuilder(); sb25.appendln((Object)taint()); sink(sb25.toString()); // $hasTaintFlow=y
51+
{
52+
StrBuilder auxsb = new StrBuilder(); auxsb.appendln(taint());
53+
StrBuilder sb26 = new StrBuilder(); sb26.appendln(auxsb); sink(sb26.toString()); // $hasTaintFlow=y
54+
}
55+
StrBuilder sb27 = new StrBuilder(); sb27.appendln(new StringBuffer(taint())); sink(sb27.toString()); // $hasTaintFlow=y
56+
StrBuilder sb28 = new StrBuilder(); sb28.appendln(new StringBuffer(taint()), 0, 0); sink(sb28.toString()); // $hasTaintFlow=y
57+
StrBuilder sb29 = new StrBuilder(); sb29.appendln(new StringBuilder(taint())); sink(sb29.toString()); // $hasTaintFlow=y
58+
StrBuilder sb30 = new StrBuilder(); sb30.appendln(new StringBuilder(taint()), 0, 0); sink(sb30.toString()); // $hasTaintFlow=y
59+
StrBuilder sb31 = new StrBuilder(); sb31.appendln(taint()); sink(sb31.toString()); // $hasTaintFlow=y
60+
StrBuilder sb32 = new StrBuilder(); sb32.appendln(taint(), 0, 0); sink(sb32.toString()); // $hasTaintFlow=y
61+
StrBuilder sb33 = new StrBuilder(); sb33.appendln(taint(), "format", "args"); sink(sb33.toString()); // $hasTaintFlow=y
62+
StrBuilder sb34 = new StrBuilder(); sb34.appendln("Format string", taint(), "args"); sink(sb34.toString()); // $hasTaintFlow=y
63+
StrBuilder sb35 = new StrBuilder(); sb35.appendSeparator(taint()); sink(sb35.toString()); // $hasTaintFlow=y
64+
StrBuilder sb36 = new StrBuilder(); sb36.appendSeparator(taint(), 0); sink(sb36.toString()); // $hasTaintFlow=y
65+
StrBuilder sb37 = new StrBuilder(); sb37.appendSeparator(taint(), "default"); sink(sb37.toString()); // $hasTaintFlow=y
66+
StrBuilder sb38 = new StrBuilder(); sb38.appendSeparator("", taint()); sink(sb38.toString()); // $hasTaintFlow=y
67+
{
68+
StrBuilder auxsb = new StrBuilder(); auxsb.appendln(taint());
69+
StrBuilder sb39 = new StrBuilder(); auxsb.appendTo(sb39); sink(sb39.toString()); // $hasTaintFlow=y
70+
}
71+
{
72+
List<String> taintedList = new ArrayList<>();
73+
taintedList.add(taint());
74+
StrBuilder sb40 = new StrBuilder(); sb40.appendWithSeparators(taintedList, ", "); sink(sb40.toString()); // $hasTaintFlow=y
75+
StrBuilder sb41 = new StrBuilder(); sb41.appendWithSeparators(taintedList.iterator(), ", "); sink(sb41.toString()); // $hasTaintFlow=y
76+
List<String> untaintedList = new ArrayList<>();
77+
StrBuilder sb42 = new StrBuilder(); sb42.appendWithSeparators(untaintedList, taint()); sink(sb42.toString()); // $hasTaintFlow=y
78+
StrBuilder sb43 = new StrBuilder(); sb43.appendWithSeparators(untaintedList.iterator(), taint()); sink(sb43.toString()); // $hasTaintFlow=y
79+
String[] taintedArray = new String[] { taint() };
80+
String[] untaintedArray = new String[] {};
81+
StrBuilder sb44 = new StrBuilder(); sb44.appendWithSeparators(taintedArray, ", "); sink(sb44.toString()); // $hasTaintFlow=y
82+
StrBuilder sb45 = new StrBuilder(); sb45.appendWithSeparators(untaintedArray, taint()); sink(sb45.toString()); // $hasTaintFlow=y
83+
}
84+
{
85+
StrBuilder sb46 = new StrBuilder(); sb46.append(taint());
86+
char[] target = new char[100];
87+
sb46.asReader().read(target);
88+
sink(target); // $hasTaintFlow=y
89+
}
90+
StrBuilder sb47 = new StrBuilder(); sb47.append(taint()); sink(sb47.asTokenizer().next()); // $hasTaintFlow=y
91+
StrBuilder sb48 = new StrBuilder(); sb48.append(taint()); sink(sb48.build()); // $hasTaintFlow=y
92+
StrBuilder sb49 = new StrBuilder(); sb49.append(taint()); sink(sb49.getChars(null)); // $hasTaintFlow=y
93+
{
94+
StrBuilder sb50 = new StrBuilder(); sb50.append(taint());
95+
char[] target = new char[100];
96+
sb50.getChars(target);
97+
sink(target); // $hasTaintFlow=y
98+
}
99+
{
100+
StrBuilder sb51 = new StrBuilder(); sb51.append(taint());
101+
char[] target = new char[100];
102+
sb51.getChars(0, 0, target, 0);
103+
sink(target); // $hasTaintFlow=y
104+
}
105+
StrBuilder sb52 = new StrBuilder(); sb52.insert(0, taint().toCharArray()); sink(sb52.toString()); // $hasTaintFlow=y
106+
StrBuilder sb53 = new StrBuilder(); sb53.insert(0, taint().toCharArray(), 0, 0); sink(sb53.toString()); // $hasTaintFlow=y
107+
StrBuilder sb54 = new StrBuilder(); sb54.insert(0, taint()); sink(sb54.toString()); // $hasTaintFlow=y
108+
StrBuilder sb55 = new StrBuilder(); sb55.insert(0, (Object)taint()); sink(sb55.toString()); // $hasTaintFlow=y
109+
StrBuilder sb56 = new StrBuilder(); sb56.append(taint()); sink(sb56.leftString(0)); // $hasTaintFlow=y
110+
StrBuilder sb57 = new StrBuilder(); sb57.append(taint()); sink(sb57.midString(0, 0)); // $hasTaintFlow=y
111+
{
112+
StringReader reader = new StringReader(taint());
113+
StrBuilder sb58 = new StrBuilder(); sb58.readFrom(reader); sink(sb58.toString()); // $hasTaintFlow=y
114+
}
115+
StrBuilder sb59 = new StrBuilder(); sb59.replace(0, 0, taint()); sink(sb59.toString()); // $hasTaintFlow=y
116+
StrBuilder sb60 = new StrBuilder(); sb60.replace(null, taint(), 0, 0, 0); sink(sb60.toString()); // $hasTaintFlow=y
117+
StrBuilder sb61 = new StrBuilder(); sb61.replaceAll((StrMatcher)null, taint()); sink(sb61.toString()); // $hasTaintFlow=y
118+
StrBuilder sb62 = new StrBuilder(); sb62.replaceAll("search", taint()); sink(sb62.toString()); // $hasTaintFlow=y
119+
StrBuilder sb63 = new StrBuilder(); sb63.replaceAll(taint(), "replace"); sink(sb63.toString()); // GOOD (search string doesn't convey taint)
120+
StrBuilder sb64 = new StrBuilder(); sb64.replaceFirst((StrMatcher)null, taint()); sink(sb64.toString()); // $hasTaintFlow=y
121+
StrBuilder sb65 = new StrBuilder(); sb65.replaceFirst("search", taint()); sink(sb65.toString()); // $hasTaintFlow=y
122+
StrBuilder sb66 = new StrBuilder(); sb66.replaceFirst(taint(), "replace"); sink(sb66.toString()); // GOOD (search string doesn't convey taint)
123+
StrBuilder sb67 = new StrBuilder(); sb67.append(taint()); sink(sb67.rightString(0)); // $hasTaintFlow=y
124+
StrBuilder sb68 = new StrBuilder(); sb68.append(taint()); sink(sb68.subSequence(0, 0)); // $hasTaintFlow=y
125+
StrBuilder sb69 = new StrBuilder(); sb69.append(taint()); sink(sb69.substring(0)); // $hasTaintFlow=y
126+
StrBuilder sb70 = new StrBuilder(); sb70.append(taint()); sink(sb70.substring(0, 0)); // $hasTaintFlow=y
127+
StrBuilder sb71 = new StrBuilder(); sb71.append(taint()); sink(sb71.toCharArray()); // $hasTaintFlow=y
128+
StrBuilder sb72 = new StrBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow=y
129+
StrBuilder sb73 = new StrBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow=y
130+
StrBuilder sb74 = new StrBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow=y
131+
}
132+
133+
}

0 commit comments

Comments
 (0)