Skip to content

Commit d667ac2

Browse files
committed
Add list of constants sanitizer for java.util.List
1 parent e5c1c07 commit d667ac2

File tree

5 files changed

+544
-0
lines changed

5 files changed

+544
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/**
2+
* Provides classes for identifying comparisons against a list of compile-time
3+
* constants and considering them as taint-sanitizers.
4+
*/
5+
6+
import java
7+
private import semmle.code.java.controlflow.Guards
8+
private import semmle.code.java.dataflow.TaintTracking
9+
10+
/**
11+
* A comparison against a list of compile-time constants, sanitizing taint by
12+
* restricting to a set of known values.
13+
*/
14+
class ListOfConstantsComparisonSanitizerGuard extends TaintTracking::DefaultTaintSanitizer {
15+
ListOfConstantsComparisonSanitizerGuard() {
16+
this = DataFlow::BarrierGuard<listOfConstantsComparisonSanitizerGuard/3>::getABarrierNode()
17+
}
18+
}
19+
20+
private predicate listOfConstantsComparisonSanitizerGuard(Guard g, Expr e, boolean outcome) {
21+
exists(ListOfConstantsComparison locc |
22+
g = locc and
23+
e = locc.getExpr() and
24+
outcome = locc.getOutcome()
25+
)
26+
}
27+
28+
/** A comparison against a list of compile-time constants. */
29+
abstract class ListOfConstantsComparison extends Guard {
30+
Expr e;
31+
boolean outcome;
32+
33+
ListOfConstantsComparison() {
34+
exists(this) and
35+
outcome = [true, false]
36+
}
37+
38+
/** Gets the expression that is compared to a list of constants. */
39+
Expr getExpr() { result = e }
40+
41+
/** Gets the value of `this` when `e` is in the list of constants. */
42+
boolean getOutcome() { result = outcome }
43+
}
44+
45+
/**
46+
* Holds if the method call `mc` has only compile-time constant arguments (and
47+
* at least one argument). To account for varargs methods, we also include
48+
* a single array argument which is initialized locally with at least one
49+
* compile-time constant.
50+
*/
51+
predicate methodCallHasConstantArguments(MethodCall mc) {
52+
// f("a", "b", "c")
53+
forex(Expr e | e = mc.getAnArgument() |
54+
not e.getType() instanceof Array and e.isCompileTimeConstant()
55+
)
56+
or
57+
// String[] a = {"a", "b", "c"};
58+
// f(a)
59+
mc.getNumArgument() = 1 and
60+
mc.getArgument(0).getType() instanceof Array and
61+
exists(ArrayInit arr | DataFlow::localExprFlow(arr, mc.getArgument(0)) |
62+
forex(Expr e | e = arr.getAnInit() | e.isCompileTimeConstant())
63+
)
64+
}
65+
66+
/** Classes for `java.util.List`. */
67+
module JavaUtilList {
68+
private class JavaUtilListContainsCall extends MethodCall {
69+
JavaUtilListContainsCall() {
70+
exists(Method m |
71+
this.getMethod() = m and
72+
m.hasName("contains") and
73+
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "List")
74+
)
75+
}
76+
}
77+
78+
private class NonConstantElementAddition extends Expr {
79+
NonConstantElementAddition() {
80+
exists(Method m, RefType t, MethodCall mc |
81+
this = mc.getQualifier() and
82+
mc.getMethod() = m and
83+
t = m.getDeclaringType().getSourceDeclaration().getASourceSupertype*()
84+
|
85+
t.hasQualifiedName("java.util", "List") and
86+
m.getName() = ["add", "addFirst", "addLast"] and
87+
not mc.getArgument(m.getNumberOfParameters() - 1).isCompileTimeConstant()
88+
or
89+
// If a whole collection is added then we don't try to track if it contains
90+
// only compile-time constants, and conservatively assume that it does.
91+
t.hasQualifiedName("java.util", ["Collection", "List"]) and m.getName() = "addAll"
92+
)
93+
}
94+
}
95+
96+
private predicate javaUtilListOfConstantsLocalFlowTo(Expr e) {
97+
exists(JavaUtilListOfConstants loc | DataFlow::localExprFlow(loc, e) |
98+
loc.isImmutable()
99+
or
100+
not DataFlow::localExprFlow(any(NonConstantElementAddition ncea), e)
101+
)
102+
}
103+
104+
private predicate javaUtilListOfConstantsFlowsTo(Expr e) {
105+
javaUtilListOfConstantsLocalFlowTo(e)
106+
or
107+
// Access a static final field to get an immutable list of constants.
108+
exists(Field f |
109+
f.isStatic() and
110+
f.isFinal() and
111+
forall(Expr v | v = f.getInitializer() or v = f.getAnAccess().(FieldWrite).getASource() |
112+
v = any(JavaUtilListOfConstants loc | loc.isImmutable())
113+
)
114+
|
115+
DataFlow::localExprFlow(f.getAnAccess(), e)
116+
)
117+
}
118+
119+
/**
120+
* An invocation of `java.util.List.contains` where the qualifier contains only
121+
* compile-time constants.
122+
*/
123+
private class JavaUtilListOfConstantsContains extends ListOfConstantsComparison {
124+
JavaUtilListOfConstantsContains() {
125+
exists(JavaUtilListContainsCall mc |
126+
this = mc and
127+
e = mc.getArgument(0) and
128+
outcome = true and
129+
javaUtilListOfConstantsFlowsTo(mc.getQualifier())
130+
)
131+
}
132+
}
133+
134+
/**
135+
* An instance of `java.util.List` which contains only compile-time constants.
136+
*/
137+
abstract class JavaUtilListOfConstants extends Call {
138+
/** Holds if this list of constants is immutable. */
139+
abstract predicate isImmutable();
140+
}
141+
142+
/**
143+
* A invocation of a constructor of a type that extends `java.util.List`
144+
* which constructs an empty mutable list.
145+
*/
146+
private class JavaUtilListOfConstantsEmptyConstructor extends ClassInstanceExpr,
147+
JavaUtilListOfConstants
148+
{
149+
JavaUtilListOfConstantsEmptyConstructor() {
150+
this.getConstructedType()
151+
.getSourceDeclaration()
152+
.getASourceSupertype*()
153+
.hasQualifiedName("java.util", "List") and
154+
exists(Constructor c | c = this.getConstructor() |
155+
c.hasNoParameters()
156+
or
157+
c.getNumberOfParameters() = 1 and
158+
c.getParameter(0).getType().(PrimitiveType).hasName("int")
159+
)
160+
}
161+
162+
override predicate isImmutable() { none() }
163+
}
164+
165+
/**
166+
* A invocation of a constructor of a type that extends `java.util.List`
167+
* which constructs an empty mutable list.
168+
*/
169+
private class JavaUtilListOfConstantsNonEmptyConstructor extends ClassInstanceExpr,
170+
JavaUtilListOfConstants
171+
{
172+
JavaUtilListOfConstantsNonEmptyConstructor() {
173+
this.getConstructedType()
174+
.getSourceDeclaration()
175+
.getASourceSupertype*()
176+
.hasQualifiedName("java.util", "List") and
177+
exists(Constructor c | c = this.getConstructor() |
178+
c.getNumberOfParameters() = 1 and
179+
c.getParameter(0)
180+
.getType()
181+
.(RefType)
182+
.getASourceSupertype*()
183+
.hasQualifiedName("java.util", "Collection")
184+
) and
185+
javaUtilListOfConstantsFlowsTo(this.getArgument(0))
186+
}
187+
188+
override predicate isImmutable() { none() }
189+
}
190+
191+
/**
192+
* A invocation of `java.util.Arrays.asList` which constructs a mutable list.
193+
*/
194+
private class JavaUtilArraysAsList extends MethodCall, JavaUtilListOfConstants {
195+
JavaUtilArraysAsList() {
196+
exists(Method m | this.getMethod() = m |
197+
m.hasName("asList") and
198+
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Arrays")
199+
) and
200+
methodCallHasConstantArguments(this)
201+
}
202+
203+
override predicate isImmutable() { none() }
204+
}
205+
206+
/**
207+
* An invocation of `java.util.List.of` which constructs an immutable list
208+
* which contains only compile-time constants.
209+
*/
210+
private class JavaUtilListOfConstantsCreatedWithListOf extends MethodCall, JavaUtilListOfConstants
211+
{
212+
JavaUtilListOfConstantsCreatedWithListOf() {
213+
exists(Method m | this.getMethod() = m |
214+
m.hasName("of") and
215+
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "List")
216+
) and
217+
methodCallHasConstantArguments(this)
218+
}
219+
220+
override predicate isImmutable() { any() }
221+
}
222+
223+
/**
224+
* An invocation of `java.util.Collections.unmodifiableList` which constructs an immutable list
225+
* which contains only compile-time constants.
226+
*/
227+
private class JavaUtilListOfConstantsCreatedWithCollectionsUnmodifiableList extends MethodCall,
228+
JavaUtilListOfConstants
229+
{
230+
JavaUtilListOfConstantsCreatedWithCollectionsUnmodifiableList() {
231+
exists(Method m |
232+
m.hasName("unmodifiableList") and
233+
m.getDeclaringType().getSourceDeclaration().hasQualifiedName("java.util", "Collections") and
234+
this.getMethod() = m
235+
|
236+
javaUtilListOfConstantsFlowsTo(this.getArgument(0))
237+
)
238+
}
239+
240+
override predicate isImmutable() { any() }
241+
}
242+
}

java/ql/lib/semmle/code/java/dataflow/internal/TaintTrackingUtil.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ private import semmle.code.java.security.SecurityTests
77
private import semmle.code.java.security.Validation
88
private import semmle.code.java.Maps
99
private import semmle.code.java.dataflow.internal.ContainerFlow
10+
private import semmle.code.java.dataflow.ListOfConstantsSanitizer
1011
private import semmle.code.java.frameworks.spring.SpringController
1112
private import semmle.code.java.frameworks.spring.SpringHttp
1213
private import semmle.code.java.frameworks.Networking

0 commit comments

Comments
 (0)