Skip to content

Commit 60377a8

Browse files
authored
Merge pull request github#5383 from smowton/smowton/feature/strbuilder-fluent-methods
Java: Add models for StrBuilder's fluent methods
2 parents 30cb80b + 42b63a6 commit 60377a8

File tree

5 files changed

+325
-0
lines changed

5 files changed

+325
-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 and Commons Text StrBuilder class, and its successor TextStringBuilder.

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,101 @@ private class ApacheStrBuilderModel extends SummaryModelCsv {
452452
}
453453
}
454454

455+
private class ApacheStrBuilderFluentMethodsModel extends SummaryModelCsv {
456+
override predicate row(string row) {
457+
row =
458+
[
459+
"org.apache.commons.lang3.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
460+
"org.apache.commons.lang3.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
461+
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
462+
"org.apache.commons.lang3.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
463+
"org.apache.commons.lang3.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
464+
"org.apache.commons.lang3.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
465+
"org.apache.commons.lang3.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
466+
"org.apache.commons.lang3.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
467+
"org.apache.commons.lang3.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
468+
"org.apache.commons.lang3.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
469+
"org.apache.commons.lang3.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
470+
"org.apache.commons.lang3.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
471+
"org.apache.commons.lang3.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
472+
"org.apache.commons.lang3.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
473+
"org.apache.commons.lang3.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
474+
"org.apache.commons.lang3.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
475+
"org.apache.commons.lang3.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
476+
"org.apache.commons.lang3.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
477+
"org.apache.commons.lang3.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
478+
"org.apache.commons.lang3.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
479+
"org.apache.commons.lang3.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
480+
"org.apache.commons.lang3.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
481+
"org.apache.commons.lang3.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
482+
"org.apache.commons.lang3.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
483+
"org.apache.commons.lang3.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
484+
"org.apache.commons.lang3.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
485+
"org.apache.commons.text;StrBuilder;false;append;;;Argument[-1];ReturnValue;value",
486+
"org.apache.commons.text;StrBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
487+
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
488+
"org.apache.commons.text;StrBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
489+
"org.apache.commons.text;StrBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
490+
"org.apache.commons.text;StrBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
491+
"org.apache.commons.text;StrBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
492+
"org.apache.commons.text;StrBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
493+
"org.apache.commons.text;StrBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
494+
"org.apache.commons.text;StrBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
495+
"org.apache.commons.text;StrBuilder;false;delete;;;Argument[-1];ReturnValue;value",
496+
"org.apache.commons.text;StrBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
497+
"org.apache.commons.text;StrBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
498+
"org.apache.commons.text;StrBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
499+
"org.apache.commons.text;StrBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
500+
"org.apache.commons.text;StrBuilder;false;insert;;;Argument[-1];ReturnValue;value",
501+
"org.apache.commons.text;StrBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
502+
"org.apache.commons.text;StrBuilder;false;replace;;;Argument[-1];ReturnValue;value",
503+
"org.apache.commons.text;StrBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
504+
"org.apache.commons.text;StrBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
505+
"org.apache.commons.text;StrBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
506+
"org.apache.commons.text;StrBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
507+
"org.apache.commons.text;StrBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
508+
"org.apache.commons.text;StrBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
509+
"org.apache.commons.text;StrBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
510+
"org.apache.commons.text;StrBuilder;false;trim;;;Argument[-1];ReturnValue;value",
511+
"org.apache.commons.text;TextStringBuilder;false;append;;;Argument[-1];ReturnValue;value",
512+
"org.apache.commons.text;TextStringBuilder;false;appendAll;;;Argument[-1];ReturnValue;value",
513+
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadLeft;;;Argument[-1];ReturnValue;value",
514+
"org.apache.commons.text;TextStringBuilder;false;appendFixedWidthPadRight;;;Argument[-1];ReturnValue;value",
515+
"org.apache.commons.text;TextStringBuilder;false;appendln;;;Argument[-1];ReturnValue;value",
516+
"org.apache.commons.text;TextStringBuilder;false;appendNewLine;;;Argument[-1];ReturnValue;value",
517+
"org.apache.commons.text;TextStringBuilder;false;appendNull;;;Argument[-1];ReturnValue;value",
518+
"org.apache.commons.text;TextStringBuilder;false;appendPadding;;;Argument[-1];ReturnValue;value",
519+
"org.apache.commons.text;TextStringBuilder;false;appendSeparator;;;Argument[-1];ReturnValue;value",
520+
"org.apache.commons.text;TextStringBuilder;false;appendWithSeparators;;;Argument[-1];ReturnValue;value",
521+
"org.apache.commons.text;TextStringBuilder;false;delete;;;Argument[-1];ReturnValue;value",
522+
"org.apache.commons.text;TextStringBuilder;false;deleteAll;;;Argument[-1];ReturnValue;value",
523+
"org.apache.commons.text;TextStringBuilder;false;deleteCharAt;;;Argument[-1];ReturnValue;value",
524+
"org.apache.commons.text;TextStringBuilder;false;deleteFirst;;;Argument[-1];ReturnValue;value",
525+
"org.apache.commons.text;TextStringBuilder;false;ensureCapacity;;;Argument[-1];ReturnValue;value",
526+
"org.apache.commons.text;TextStringBuilder;false;insert;;;Argument[-1];ReturnValue;value",
527+
"org.apache.commons.text;TextStringBuilder;false;minimizeCapacity;;;Argument[-1];ReturnValue;value",
528+
"org.apache.commons.text;TextStringBuilder;false;replace;;;Argument[-1];ReturnValue;value",
529+
"org.apache.commons.text;TextStringBuilder;false;replaceAll;;;Argument[-1];ReturnValue;value",
530+
"org.apache.commons.text;TextStringBuilder;false;replaceFirst;;;Argument[-1];ReturnValue;value",
531+
"org.apache.commons.text;TextStringBuilder;false;reverse;;;Argument[-1];ReturnValue;value",
532+
"org.apache.commons.text;TextStringBuilder;false;setCharAt;;;Argument[-1];ReturnValue;value",
533+
"org.apache.commons.text;TextStringBuilder;false;setLength;;;Argument[-1];ReturnValue;value",
534+
"org.apache.commons.text;TextStringBuilder;false;setNewLineText;;;Argument[-1];ReturnValue;value",
535+
"org.apache.commons.text;TextStringBuilder;false;setNullText;;;Argument[-1];ReturnValue;value",
536+
"org.apache.commons.text;TextStringBuilder;false;trim;;;Argument[-1];ReturnValue;value"
537+
]
538+
}
539+
}
540+
541+
/**
542+
* An Apache Commons-Lang StrBuilder method that returns `this`.
543+
*/
544+
private class ApacheStrBuilderFluentMethod extends FluentMethod {
545+
ApacheStrBuilderFluentMethod() {
546+
this.getReturnType().(RefType).hasQualifiedName("org.apache.commons.lang3.text", "StrBuilder")
547+
}
548+
}
549+
455550
/**
456551
* Taint-propagating models for `WordUtils`.
457552
*/

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,82 @@ void test() throws Exception {
128128
StrBuilder sb72 = new StrBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
129129
StrBuilder sb73 = new StrBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
130130
StrBuilder sb74 = new StrBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
131+
132+
// Tests for fluent methods (those returning `this`):
133+
134+
StrBuilder fluentTest = new StrBuilder();
135+
sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
136+
137+
StrBuilder fluentBackflowTest = new StrBuilder();
138+
fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
139+
sink(fluentBackflowTest.toString()); // $hasTaintFlow
140+
141+
// Test the case where the fluent method contributing taint is at the end of a statement:
142+
StrBuilder fluentBackflowTest2 = new StrBuilder();
143+
fluentBackflowTest2.append("Harmless").append(taint());
144+
sink(fluentBackflowTest2.toString()); // $hasTaintFlow
145+
146+
// Test all fluent methods are passing taint through to their result:
147+
StrBuilder fluentAllMethodsTest = new StrBuilder(taint());
148+
sink(fluentAllMethodsTest // $hasTaintFlow
149+
.append("text")
150+
.appendAll("text")
151+
.appendFixedWidthPadLeft("text", 4, ' ')
152+
.appendFixedWidthPadRight("text", 4, ' ')
153+
.appendln("text")
154+
.appendNewLine()
155+
.appendNull()
156+
.appendPadding(0, ' ')
157+
.appendSeparator(',')
158+
.appendWithSeparators(new String[] { }, ",")
159+
.delete(0, 0)
160+
.deleteAll(' ')
161+
.deleteCharAt(0)
162+
.deleteFirst("delme")
163+
.ensureCapacity(100)
164+
.insert(1, "insertme")
165+
.minimizeCapacity()
166+
.replace(0, 0, "replacement")
167+
.replaceAll("find", "replace")
168+
.replaceFirst("find", "replace")
169+
.reverse()
170+
.setCharAt(0, 'a')
171+
.setLength(500)
172+
.setNewLineText("newline")
173+
.setNullText("NULL")
174+
.trim());
175+
176+
// Test all fluent methods are passing taint back to their qualifier:
177+
StrBuilder fluentAllMethodsTest2 = new StrBuilder();
178+
fluentAllMethodsTest2
179+
.append("text")
180+
.appendAll("text")
181+
.appendFixedWidthPadLeft("text", 4, ' ')
182+
.appendFixedWidthPadRight("text", 4, ' ')
183+
.appendln("text")
184+
.appendNewLine()
185+
.appendNull()
186+
.appendPadding(0, ' ')
187+
.appendSeparator(',')
188+
.appendWithSeparators(new String[] { }, ",")
189+
.delete(0, 0)
190+
.deleteAll(' ')
191+
.deleteCharAt(0)
192+
.deleteFirst("delme")
193+
.ensureCapacity(100)
194+
.insert(1, "insertme")
195+
.minimizeCapacity()
196+
.replace(0, 0, "replacement")
197+
.replaceAll("find", "replace")
198+
.replaceFirst("find", "replace")
199+
.reverse()
200+
.setCharAt(0, 'a')
201+
.setLength(500)
202+
.setNewLineText("newline")
203+
.setNullText("NULL")
204+
.trim()
205+
.append(taint());
206+
sink(fluentAllMethodsTest2); // $hasTaintFlow
131207
}
132208

133209
}

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,82 @@ void test() throws Exception {
128128
StrBuilder sb72 = new StrBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
129129
StrBuilder sb73 = new StrBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
130130
StrBuilder sb74 = new StrBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
131+
132+
// Tests for fluent methods (those returning `this`):
133+
134+
StrBuilder fluentTest = new StrBuilder();
135+
sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
136+
137+
StrBuilder fluentBackflowTest = new StrBuilder();
138+
fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
139+
sink(fluentBackflowTest.toString()); // $hasTaintFlow
140+
141+
// Test the case where the fluent method contributing taint is at the end of a statement:
142+
StrBuilder fluentBackflowTest2 = new StrBuilder();
143+
fluentBackflowTest2.append("Harmless").append(taint());
144+
sink(fluentBackflowTest2.toString()); // $hasTaintFlow
145+
146+
// Test all fluent methods are passing taint through to their result:
147+
StrBuilder fluentAllMethodsTest = new StrBuilder(taint());
148+
sink(fluentAllMethodsTest // $hasTaintFlow
149+
.append("text")
150+
.appendAll("text")
151+
.appendFixedWidthPadLeft("text", 4, ' ')
152+
.appendFixedWidthPadRight("text", 4, ' ')
153+
.appendln("text")
154+
.appendNewLine()
155+
.appendNull()
156+
.appendPadding(0, ' ')
157+
.appendSeparator(',')
158+
.appendWithSeparators(new String[] { }, ",")
159+
.delete(0, 0)
160+
.deleteAll(' ')
161+
.deleteCharAt(0)
162+
.deleteFirst("delme")
163+
.ensureCapacity(100)
164+
.insert(1, "insertme")
165+
.minimizeCapacity()
166+
.replace(0, 0, "replacement")
167+
.replaceAll("find", "replace")
168+
.replaceFirst("find", "replace")
169+
.reverse()
170+
.setCharAt(0, 'a')
171+
.setLength(500)
172+
.setNewLineText("newline")
173+
.setNullText("NULL")
174+
.trim());
175+
176+
// Test all fluent methods are passing taint back to their qualifier:
177+
StrBuilder fluentAllMethodsTest2 = new StrBuilder();
178+
fluentAllMethodsTest2
179+
.append("text")
180+
.appendAll("text")
181+
.appendFixedWidthPadLeft("text", 4, ' ')
182+
.appendFixedWidthPadRight("text", 4, ' ')
183+
.appendln("text")
184+
.appendNewLine()
185+
.appendNull()
186+
.appendPadding(0, ' ')
187+
.appendSeparator(',')
188+
.appendWithSeparators(new String[] { }, ",")
189+
.delete(0, 0)
190+
.deleteAll(' ')
191+
.deleteCharAt(0)
192+
.deleteFirst("delme")
193+
.ensureCapacity(100)
194+
.insert(1, "insertme")
195+
.minimizeCapacity()
196+
.replace(0, 0, "replacement")
197+
.replaceAll("find", "replace")
198+
.replaceFirst("find", "replace")
199+
.reverse()
200+
.setCharAt(0, 'a')
201+
.setLength(500)
202+
.setNewLineText("newline")
203+
.setNullText("NULL")
204+
.trim()
205+
.append(taint());
206+
sink(fluentAllMethodsTest2); // $hasTaintFlow
131207
}
132208

133209
}

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

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,82 @@ void test() throws Exception {
129129
TextStringBuilder sb72 = new TextStringBuilder(); sb72.append(taint()); sink(sb72.toCharArray(0, 0)); // $hasTaintFlow
130130
TextStringBuilder sb73 = new TextStringBuilder(); sb73.append(taint()); sink(sb73.toStringBuffer()); // $hasTaintFlow
131131
TextStringBuilder sb74 = new TextStringBuilder(); sb74.append(taint()); sink(sb74.toStringBuilder()); // $hasTaintFlow
132+
133+
// Tests for fluent methods (those returning `this`):
134+
135+
TextStringBuilder fluentTest = new TextStringBuilder();
136+
sink(fluentTest.append("Harmless").append(taint()).append("Also harmless").toString()); // $hasTaintFlow
137+
138+
TextStringBuilder fluentBackflowTest = new TextStringBuilder();
139+
fluentBackflowTest.append("Harmless").append(taint()).append("Also harmless");
140+
sink(fluentBackflowTest.toString()); // $hasTaintFlow
141+
142+
// Test the case where the fluent method contributing taint is at the end of a statement:
143+
TextStringBuilder fluentBackflowTest2 = new TextStringBuilder();
144+
fluentBackflowTest2.append("Harmless").append(taint());
145+
sink(fluentBackflowTest2.toString()); // $hasTaintFlow
146+
147+
// Test all fluent methods are passing taint through to their result:
148+
TextStringBuilder fluentAllMethodsTest = new TextStringBuilder(taint());
149+
sink(fluentAllMethodsTest // $hasTaintFlow
150+
.append("text")
151+
.appendAll("text")
152+
.appendFixedWidthPadLeft("text", 4, ' ')
153+
.appendFixedWidthPadRight("text", 4, ' ')
154+
.appendln("text")
155+
.appendNewLine()
156+
.appendNull()
157+
.appendPadding(0, ' ')
158+
.appendSeparator(',')
159+
.appendWithSeparators(new String[] { }, ",")
160+
.delete(0, 0)
161+
.deleteAll(' ')
162+
.deleteCharAt(0)
163+
.deleteFirst("delme")
164+
.ensureCapacity(100)
165+
.insert(1, "insertme")
166+
.minimizeCapacity()
167+
.replace(0, 0, "replacement")
168+
.replaceAll("find", "replace")
169+
.replaceFirst("find", "replace")
170+
.reverse()
171+
.setCharAt(0, 'a')
172+
.setLength(500)
173+
.setNewLineText("newline")
174+
.setNullText("NULL")
175+
.trim());
176+
177+
// Test all fluent methods are passing taint back to their qualifier:
178+
TextStringBuilder fluentAllMethodsTest2 = new TextStringBuilder();
179+
fluentAllMethodsTest2
180+
.append("text")
181+
.appendAll("text")
182+
.appendFixedWidthPadLeft("text", 4, ' ')
183+
.appendFixedWidthPadRight("text", 4, ' ')
184+
.appendln("text")
185+
.appendNewLine()
186+
.appendNull()
187+
.appendPadding(0, ' ')
188+
.appendSeparator(',')
189+
.appendWithSeparators(new String[] { }, ",")
190+
.delete(0, 0)
191+
.deleteAll(' ')
192+
.deleteCharAt(0)
193+
.deleteFirst("delme")
194+
.ensureCapacity(100)
195+
.insert(1, "insertme")
196+
.minimizeCapacity()
197+
.replace(0, 0, "replacement")
198+
.replaceAll("find", "replace")
199+
.replaceFirst("find", "replace")
200+
.reverse()
201+
.setCharAt(0, 'a')
202+
.setLength(500)
203+
.setNewLineText("newline")
204+
.setNullText("NULL")
205+
.trim()
206+
.append(taint());
207+
sink(fluentAllMethodsTest2); // $hasTaintFlow
132208
}
133209

134210
}

0 commit comments

Comments
 (0)