@@ -7,28 +7,63 @@ class TypeSearchRequest extends Class {
77 TypeSearchRequest ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "SearchRequest" ) }
88}
99
10- /** The class `com.unboundid.ldap.sdk.ReadOnlySearchRequest`. */
10+ /** The interface `com.unboundid.ldap.sdk.ReadOnlySearchRequest`. */
1111class TypeReadOnlySearchRequest extends Interface {
1212 TypeReadOnlySearchRequest ( ) {
1313 this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "ReadOnlySearchRequest" )
1414 }
1515}
1616
1717/** The class `com.unboundid.ldap.sdk.Filter`. */
18- class TypeFilter extends Class {
19- TypeFilter ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "Filter" ) }
18+ class TypeUnboundIdLdapFilter extends Class {
19+ TypeUnboundIdLdapFilter ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "Filter" ) }
2020}
2121
2222/** The class `com.unboundid.ldap.sdk.LDAPConnection`. */
2323class TypeLDAPConnection extends Class {
2424 TypeLDAPConnection ( ) { this .hasQualifiedName ( "com.unboundid.ldap.sdk" , "LDAPConnection" ) }
2525}
2626
27+ /** The class `org.springframework.ldap.core.LdapTemplate`. */
28+ class TypeLdapTemplate extends Class {
29+ TypeLdapTemplate ( ) { this .hasQualifiedName ( "org.springframework.ldap.core" , "LdapTemplate" ) }
30+ }
31+
32+ /** The interface `org.springframework.ldap.query.LdapQuery`. */
33+ class TypeLdapQuery extends Interface {
34+ TypeLdapQuery ( ) { this .hasQualifiedName ( "org.springframework.ldap.query" , "LdapQuery" ) }
35+ }
36+
37+ /** The interface `org.springframework.ldap.query.LdapQueryBuilder`. */
38+ class TypeLdapQueryBuilder extends Class {
39+ TypeLdapQueryBuilder ( ) {
40+ this .hasQualifiedName ( "org.springframework.ldap.query" , "LdapQueryBuilder" )
41+ }
42+ }
43+
44+ /** The interface `org.springframework.ldap.filter.HardcodedFilter`. */
45+ class TypeHardcodedFilter extends Class {
46+ TypeHardcodedFilter ( ) {
47+ this .hasQualifiedName ( "org.springframework.ldap.filter" , "HardcodedFilter" )
48+ }
49+ }
50+
51+ /** The interface `org.springframework.ldap.filter.Filter`. */
52+ class TypeSpringLdapFilter extends Interface {
53+ TypeSpringLdapFilter ( ) { this .hasQualifiedName ( "org.springframework.ldap.filter" , "Filter" ) }
54+ }
55+
2756/** The class `org.springframework.ldap.support.LdapEncoder`. */
2857class TypeLdapEncoder extends Class {
2958 TypeLdapEncoder ( ) { this .hasQualifiedName ( "org.springframework.ldap.support" , "LdapEncoder" ) }
3059}
3160
61+ /** Holds if the parameter of `c` at index `paramIndex` is varargs. */
62+ bindingset [ paramIndex]
63+ predicate isVarargs ( Callable c , int paramIndex ) {
64+ c .getParameter ( min ( int i | i = paramIndex or i = c .getNumberOfParameters ( ) - 1 | i ) ) .isVarargs ( )
65+ }
66+
3267/** A data flow source for unvalidated user input that is used to construct LDAP queries. */
3368abstract class LdapInjectionSource extends DataFlow:: Node { }
3469
@@ -51,7 +86,10 @@ class LdapInjectionFlowConfig extends TaintTracking::Configuration {
5186 override predicate isSanitizer ( DataFlow:: Node node ) { node instanceof LdapInjectionSanitizer }
5287
5388 override predicate isAdditionalTaintStep ( DataFlow:: Node node1 , DataFlow:: Node node2 ) {
54- filterStep ( node1 , node2 ) or searchRequestStep ( node1 , node2 )
89+ filterStep ( node1 , node2 ) or
90+ searchRequestStep ( node1 , node2 ) or
91+ ldapQueryStep ( node1 , node2 ) or
92+ hardcodedFilterStep ( node1 , node2 )
5593 }
5694}
5795
@@ -110,64 +148,54 @@ class JndiLdapInjectionSink extends LdapInjectionSink {
110148 */
111149class UnboundIdLdapInjectionSink extends LdapInjectionSink {
112150 UnboundIdLdapInjectionSink ( ) {
113- exists ( MethodAccess ma , Method m , int index , RefType argType |
151+ exists ( MethodAccess ma , Method m , int index , Parameter param |
114152 ma .getMethod ( ) = m and
115153 ma .getArgument ( index ) = this .getExpr ( ) and
116- ma . getArgument ( index ) . getType ( ) = argType
154+ m . getParameter ( index ) = param
117155 |
118156 // LDAPConnection.search or LDAPConnection.searchForEntry method
119157 m .getDeclaringType ( ) instanceof TypeLDAPConnection and
120158 ( m .hasName ( "search" ) or m .hasName ( "searchForEntry" ) ) and
159+ // Parameter is not varargs
160+ not isVarargs ( m , index ) and
121161 (
122162 // Parameter type is SearchRequest or ReadOnlySearchRequest
123- (
124- argType instanceof TypeReadOnlySearchRequest or
125- argType instanceof TypeSearchRequest
126- ) or
163+ param .getType ( ) instanceof TypeReadOnlySearchRequest or
164+ param .getType ( ) instanceof TypeSearchRequest or
127165 // Or parameter index is 2, 3, 5, 6 or 7 (this is where filter parameter is)
128- // but it's not the last one nor beyond the last one (varargs representing attributes)
129- index = any ( int i |
130- ( i = [ 2 ..3 ] or i = [ 5 ..7 ] ) and i < ma .getMethod ( ) .getNumberOfParameters ( ) - 1
131- )
166+ index = any ( int i | i = [ 2 ..3 ] or i = [ 5 ..7 ] )
132167 )
133168 )
134169 }
135170}
136171
137172/**
138173 * Spring LDAP sink for LDAP injection vulnerabilities,
139- * i.e. LDAPConnection.search or LDAPConnection.searchForEntry method.
174+ * i.e. LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method.
140175 */
141- // LdapTemplate:
142- // find(LdapQuery query, Class<T> clazz)
143- // find(Name base, Filter filter, SearchControls searchControls, Class<T> clazz)
144- // findOne(LdapQuery query, Class<T> clazz)
145- // search - 2nd param if String (filter)
146- // search - 1st param if LdapQuery
147- // searchForContext(LdapQuery query)
148- // searchForObject - 2nd param if String (filter)
149- // searchForObject - 1st param if LdapQuery
150176class SpringLdapInjectionSink extends LdapInjectionSink {
151177 SpringLdapInjectionSink ( ) {
152- exists ( MethodAccess ma , Method m , int index , RefType argType |
178+ exists ( MethodAccess ma , Method m , int index , RefType paramType |
153179 ma .getMethod ( ) = m and
154180 ma .getArgument ( index ) = this .getExpr ( ) and
155- ma . getArgument ( index ) . getType ( ) = argType
181+ m . getParameterType ( index ) = paramType
156182 |
157- // LDAPConnection.search or LDAPConnection.searchForEntry method
158- m .getDeclaringType ( ) instanceof TypeLDAPConnection and
159- ( m .hasName ( "search" ) or m .hasName ( "searchForEntry" ) ) and
183+ // LdapTemplate.authenticate, LdapTemplate.find* or LdapTemplate.search* method
184+ m .getDeclaringType ( ) instanceof TypeLdapTemplate and
160185 (
161- // Parameter type is SearchRequest or ReadOnlySearchRequest
162- (
163- argType instanceof TypeReadOnlySearchRequest or
164- argType instanceof TypeSearchRequest
165- ) or
166- // Or parameter index is 2, 3, 5, 6 or 7 (this is where filter parameter is)
167- // but it's not the last one nor beyond the last one (varargs representing attributes)
168- index = any ( int i |
169- ( i = [ 2 ..3 ] or i = [ 5 ..7 ] ) and i < ma .getMethod ( ) .getNumberOfParameters ( ) - 1
170- )
186+ m .hasName ( "authenticate" ) or
187+ m .hasName ( "find" ) or
188+ m .hasName ( "findOne" ) or
189+ m .hasName ( "search" ) or
190+ m .hasName ( "searchForContext" ) or
191+ m .hasName ( "searchForObject" )
192+ ) and
193+ (
194+ // Parameter type is LdapQuery or Filter
195+ paramType instanceof TypeLdapQuery or
196+ paramType instanceof TypeSpringLdapFilter or
197+ // Or parameter index is 1 (this is where filter parameter is)
198+ index = 1
171199 )
172200 )
173201 }
@@ -201,7 +229,7 @@ class SpringLdapSanitizer extends LdapInjectionSanitizer {
201229/** Filter.encodeValue from UnboundID. */
202230class UnboundIdSanitizer extends LdapInjectionSanitizer {
203231 UnboundIdSanitizer ( ) {
204- this .getType ( ) instanceof TypeFilter and
232+ this .getType ( ) instanceof TypeUnboundIdLdapFilter and
205233 this .getExpr ( ) .( MethodAccess ) .getMethod ( ) .hasName ( "encodeValue" )
206234 }
207235}
@@ -217,22 +245,53 @@ predicate filterStep(ExprNode n1, ExprNode n2) {
217245 |
218246 n2 .asExpr ( ) = ma and
219247 ma .getMethod ( ) = m and
220- m .getDeclaringType ( ) instanceof TypeFilter and
248+ m .getDeclaringType ( ) instanceof TypeUnboundIdLdapFilter and
221249 m .hasName ( "create" )
222250 )
223251}
224252
225253/**
226254 * Holds if `n1` to `n2` is a dataflow step that converts between `String` and UnboundID
227255 * `SearchRequest`, i.e. `new SearchRequest([...], tainted, [...])`, where `tainted` is
228- * parameter number 3, 4, 7, 8 or 9, but not the last one or beyond the last one ( varargs) .
256+ * parameter number 3, 4, 7, 8 or 9, but is not varargs.
229257 */
230258predicate searchRequestStep ( ExprNode n1 , ExprNode n2 ) {
231- exists ( ConstructorCall cc , int index | cc .getConstructedType ( ) instanceof TypeSearchRequest |
259+ exists ( ConstructorCall cc , Constructor c , int index |
260+ cc .getConstructedType ( ) instanceof TypeSearchRequest
261+ |
232262 n1 .asExpr ( ) = cc .getArgument ( index ) and
233263 n2 .asExpr ( ) = cc and
234- index = any ( int i |
235- ( i = [ 2 ..3 ] or i = [ 6 ..8 ] ) and i < cc .getConstructor ( ) .getNumberOfParameters ( ) - 1
236- )
264+ c = cc .getConstructor ( ) and
265+ // not c.getParameter(min(int i | i = index or i = c.getNumberOfParameters() - 1 | i)).isVarargs() and
266+ not isVarargs ( c , index ) and
267+ index = any ( int i | i = [ 2 ..3 ] or i = [ 6 ..8 ] )
268+ )
269+ }
270+
271+ /**
272+ * Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring `LdapQuery`,
273+ * i.e. `LdapQueryBuilder.query().filter(tainted)`.
274+ */
275+ predicate ldapQueryStep ( ExprNode n1 , ExprNode n2 ) {
276+ exists ( MethodAccess ma , Method m , int index |
277+ n1 .asExpr ( ) = ma .getQualifier ( ) or
278+ n1 .asExpr ( ) = ma .getArgument ( index )
279+ |
280+ n2 .asExpr ( ) = ma and
281+ ma .getMethod ( ) = m and
282+ m .getDeclaringType ( ) instanceof TypeLdapQueryBuilder and
283+ m .hasName ( "filter" ) and
284+ index = 0
285+ )
286+ }
287+
288+ /**
289+ * Holds if `n1` to `n2` is a dataflow step that converts between `String` and Spring
290+ * `HardcodedFilter`, i.e. `new HardcodedFilter(tainted)`.
291+ */
292+ predicate hardcodedFilterStep ( ExprNode n1 , ExprNode n2 ) {
293+ exists ( ConstructorCall cc | cc .getConstructedType ( ) instanceof TypeHardcodedFilter |
294+ n1 .asExpr ( ) = cc .getAnArgument ( ) and
295+ n2 .asExpr ( ) = cc
237296 )
238297}
0 commit comments