@@ -6,10 +6,14 @@ import semmle.code.java.dataflow.TaintTracking
66import semmle.code.java.security.SensitiveActions
77import semmle.code.java.frameworks.android.Compose
88private import semmle.code.java.security.Sanitizers
9+ private import semmle.code.java.dataflow.RangeAnalysis
910
1011/** A data flow source node for sensitive logging sources. */
1112abstract class SensitiveLoggerSource extends DataFlow:: Node { }
1213
14+ /** A data flow barrier node for sensitive logging sanitizers. */
15+ abstract class SensitiveLoggerBarrier extends DataFlow:: Node { }
16+
1317/** A variable that may hold sensitive information, judging by its name. */
1418class VariableWithSensitiveName extends Variable {
1519 VariableWithSensitiveName ( ) {
@@ -40,17 +44,89 @@ private class TypeType extends RefType {
4044 }
4145}
4246
47+ /**
48+ * A sanitizer that may remove sensitive information from a string before logging.
49+ *
50+ * It allows for substring operations taking the first N (or last N, for Kotlin) characters, limited to 7 or fewer.
51+ */
52+ private class PrefixSuffixBarrier extends SensitiveLoggerBarrier {
53+ PrefixSuffixBarrier ( ) {
54+ exists ( MethodCall mc , Method m , int limit |
55+ limit = 7 and
56+ mc .getMethod ( ) = m
57+ |
58+ // substring in Java
59+ (
60+ m .hasQualifiedName ( "java.lang" , "String" , "substring" ) or
61+ m .hasQualifiedName ( "java.lang" , "StringBuffer" , "substring" ) or
62+ m .hasQualifiedName ( "java.lang" , "StringBuilder" , "substring" )
63+ ) and
64+ (
65+ twoArgLimit ( mc , limit , false ) or
66+ singleArgLimit ( mc , limit , false )
67+ ) and
68+ this .asExpr ( ) = mc .getQualifier ( )
69+ or
70+ // Kotlin string operations, which use extension methods (so the string is the first argument)
71+ (
72+ m .hasQualifiedName ( "kotlin.text" , "StringsKt" , "substring" ) and
73+ (
74+ twoArgLimit ( mc , limit , true ) or
75+ singleArgLimit ( mc , limit , true )
76+ )
77+ or
78+ m .hasQualifiedName ( "kotlin.text" , "StringsKt" , [ "take" , "takeLast" ] ) and
79+ singleArgLimit ( mc , limit , true )
80+ ) and
81+ this .asExpr ( ) = mc .getArgument ( 0 )
82+ )
83+ }
84+ }
85+
86+ /** A predicate to check single-argument method calls for a constant integer below a set limit. */
87+ bindingset [ limit, isKotlin]
88+ private predicate singleArgLimit ( MethodCall mc , int limit , boolean isKotlin ) {
89+ mc .getNumArgument ( ) = 1 and
90+ exists ( int firstArgIndex , int delta |
91+ if isKotlin = true then firstArgIndex = 1 else firstArgIndex = 0
92+ |
93+ bounded ( mc .getArgument ( firstArgIndex ) , any ( ZeroBound z ) , delta , true , _) and
94+ delta <= limit
95+ )
96+ }
97+
98+ /** A predicate to check two-argument method calls for zero and a constant integer below a set limit. */
99+ bindingset [ limit, isKotlin]
100+ private predicate twoArgLimit ( MethodCall mc , int limit , boolean isKotlin ) {
101+ mc .getNumArgument ( ) = 2 and
102+ exists ( int firstArgIndex , int secondArgIndex , int delta |
103+ isKotlin = true and firstArgIndex = 1 and secondArgIndex = 2
104+ or
105+ isKotlin = false and firstArgIndex = 0 and secondArgIndex = 1
106+ |
107+ // mc.getArgument(firstArgIndex).(CompileTimeConstantExpr).getIntValue() = 0 and
108+ bounded ( mc .getArgument ( firstArgIndex ) , any ( ZeroBound z ) , 0 , true , _) and
109+ bounded ( mc .getArgument ( firstArgIndex ) , any ( ZeroBound z ) , 0 , false , _) and
110+ bounded ( mc .getArgument ( secondArgIndex ) , any ( ZeroBound z ) , delta , true , _) and
111+ delta <= limit
112+ )
113+ }
114+
115+ private class DefaultSensitiveLoggerBarrier extends SensitiveLoggerBarrier {
116+ DefaultSensitiveLoggerBarrier ( ) {
117+ this .asExpr ( ) instanceof LiveLiteral or
118+ this instanceof SimpleTypeSanitizer or
119+ this .getType ( ) instanceof TypeType
120+ }
121+ }
122+
43123/** A data-flow configuration for identifying potentially-sensitive data flowing to a log output. */
44124module SensitiveLoggerConfig implements DataFlow:: ConfigSig {
45125 predicate isSource ( DataFlow:: Node source ) { source instanceof SensitiveLoggerSource }
46126
47127 predicate isSink ( DataFlow:: Node sink ) { sinkNode ( sink , "log-injection" ) }
48128
49- predicate isBarrier ( DataFlow:: Node sanitizer ) {
50- sanitizer .asExpr ( ) instanceof LiveLiteral or
51- sanitizer instanceof SimpleTypeSanitizer or
52- sanitizer .getType ( ) instanceof TypeType
53- }
129+ predicate isBarrier ( DataFlow:: Node sanitizer ) { sanitizer instanceof SensitiveLoggerBarrier }
54130
55131 predicate isBarrierIn ( DataFlow:: Node node ) { isSource ( node ) }
56132
0 commit comments