1- /**
2- * Provides a default taint sanitizer identifying comparisons against lists of
3- * compile-time constants.
4- */
1+ /** Provides a default taint sanitizer identifying checks against compile-time constants. */
52overlay [ local?]
63module ;
74
@@ -10,6 +7,7 @@ private import codeql.typeflow.UniversalFlow as UniversalFlow
107private import semmle.code.java.Collections
118private import semmle.code.java.controlflow.Guards
129private import semmle.code.java.dataflow.internal.BaseSSA as Base
10+ private import semmle.code.java.dataflow.NullGuards
1311private import semmle.code.java.dataflow.TaintTracking
1412private import semmle.code.java.dataflow.TypeFlow
1513private import semmle.code.java.dispatch.VirtualDispatch
@@ -157,6 +155,29 @@ private predicate collectionWithPossibleMutation(FlowNode n) {
157155 )
158156}
159157
158+ /** Holds if `n` is a collection constructed by an API that rejects null elements. */
159+ private predicate nullRejectingCollectionCreation ( FlowNode n ) {
160+ exists ( Call c , Callable tgt |
161+ n .asExpr ( ) = c and
162+ c .getCallee ( ) .getSourceDeclaration ( ) = tgt
163+ |
164+ tgt .hasQualifiedName ( "java.util" , [ "List" , "Set" ] , [ "copyOf" , "of" ] )
165+ or
166+ tgt .hasQualifiedName ( "com.google.common.collect" , [ "ImmutableList" , "ImmutableSet" ] ,
167+ [ "copyOf" , "of" ] )
168+ )
169+ }
170+
171+ private module NullRejectingCollectionProp implements BaseUniversalFlow:: NullaryPropertySig {
172+ predicate hasPropertyBase ( FlowNode n ) { nullRejectingCollectionCreation ( n ) }
173+
174+ predicate barrier = collectionWithPossibleMutation / 1 ;
175+ }
176+
177+ /** Holds if the given collection cannot contain null. */
178+ private predicate nullRejectingCollection =
179+ BaseUniversalFlow:: FlowNullary< NullRejectingCollectionProp > :: hasProperty / 1 ;
180+
160181/**
161182 * A collection constructor that constructs an empty mutable collection.
162183 */
@@ -241,27 +262,89 @@ private MethodCall normalizeCaseCall(Expr arg) {
241262 * Holds if the guard `g` ensures that the expression `e` is one of a set of
242263 * known constants upon evaluating to `branch`.
243264 */
244- private predicate constantCollectionContainsCheck ( Guard g , Expr e , boolean branch ) {
245- exists ( MethodCall mc , Method m , FlowNode n , Expr checked |
246- g = mc and
247- mc .getMethod ( ) .getSourceDeclaration ( ) .overridesOrInstantiates * ( m ) and
265+ private predicate constantCollectionContainsCall (
266+ MethodCall contains , FlowNode collection , Expr checked
267+ ) {
268+ exists ( Method m , FlowNode n |
269+ contains .getMethod ( ) .getSourceDeclaration ( ) .overridesOrInstantiates * ( m ) and
248270 m .hasQualifiedName ( "java.util" , "Collection" , "contains" ) and
249- n .asExpr ( ) = mc .getQualifier ( ) and
271+ n .asExpr ( ) = contains .getQualifier ( ) and
250272 constantCollection ( n ) and
251- checked = mc .getAnArgument ( ) and
273+ checked = contains .getAnArgument ( ) and
274+ collection = n
275+ )
276+ }
277+
278+ private predicate constantCollectionContainsCheck ( Guard g , Expr e , boolean branch ) {
279+ exists ( Expr checked | checked = e or checked = normalizeCaseCall ( e ) |
280+ constantCollectionContainsCall ( g , _, checked ) and
281+ branch = true
282+ )
283+ }
284+
285+ /**
286+ * Holds if the guard `g` ensures that the string expression `e` equals a compile-time constant
287+ * upon evaluating to `branch`.
288+ */
289+ private predicate constantStringEqualityCheck ( Guard g , Expr e , boolean branch ) {
290+ exists ( MethodCall comparison , Expr constant |
291+ g = comparison and
292+ comparison .getMethod ( ) .getDeclaringType ( ) instanceof TypeString and
293+ comparison .getMethod ( ) .hasName ( [ "equals" , "equalsIgnoreCase" ] ) and
294+ branch = true and
295+ (
296+ e = comparison .getQualifier ( ) and constant = comparison .getArgument ( 0 )
297+ or
298+ e = comparison .getArgument ( 0 ) and constant = comparison .getQualifier ( )
299+ ) and
300+ exists ( constant .( CompileTimeConstantExpr ) .getStringValue ( ) )
301+ )
302+ }
303+
304+ /**
305+ * Holds if the guard `g` ensures that the expression `e` is one of a set of compile-time constants
306+ * upon evaluating to `branch`.
307+ */
308+ predicate constantValueGuardChecks ( Guard g , Expr e , boolean branch ) {
309+ constantStringEqualityCheck ( g , e , branch )
310+ or
311+ constantCollectionContainsCheck ( g , e , branch )
312+ }
313+
314+ /**
315+ * Holds if `g` ensures that `e` is a non-null member of a set of compile-time constants upon
316+ * evaluating to `branch`.
317+ */
318+ predicate nonNullConstantValueGuardChecks ( Guard g , Expr e , boolean branch ) {
319+ constantStringEqualityCheck ( g , e , branch )
320+ or
321+ exists ( FlowNode collection , Expr checked |
322+ constantCollectionContainsCall ( g , collection , checked ) and
252323 branch = true
253324 |
254- checked = e or
255- checked = normalizeCaseCall ( e )
325+ checked = e and
326+ (
327+ nullRejectingCollection ( collection )
328+ or
329+ e = clearlyNotNullExpr ( )
330+ )
331+ or
332+ checked = normalizeCaseCall ( e ) and
333+ (
334+ nullRejectingCollection ( collection )
335+ or
336+ checked .( MethodCall ) .getQualifier ( ) = e
337+ or
338+ e = clearlyNotNullExpr ( )
339+ )
256340 )
257341}
258342
259343/**
260- * A comparison against a list of compile-time constants, sanitizing taint by
261- * restricting to a set of known values.
344+ * A check against compile-time constants, sanitizing taint by restricting a value to a known set.
262345 */
263- private class ListOfConstantsComparisonSanitizerGuard extends TaintTracking:: DefaultTaintSanitizer {
264- ListOfConstantsComparisonSanitizerGuard ( ) {
265- this = DataFlow:: BarrierGuard< constantCollectionContainsCheck / 3 > :: getABarrierNode ( )
346+ private class ConstantValueSanitizerGuard extends TaintTracking:: DefaultTaintSanitizer {
347+ ConstantValueSanitizerGuard ( ) {
348+ this = DataFlow:: BarrierGuard< constantValueGuardChecks / 3 > :: getABarrierNode ( )
266349 }
267350}
0 commit comments