44
55private import unified
66private import codeql.dataflow.internal.AccessPathSyntax
7+ private import codeql.unified.internal.typeinference.TypeInference as T
8+ private import codeql.unified.internal.typeinference.Type as Types
79
810extensible predicate legacySourceModel (
911 string namespace , string type , boolean subtypes , string name , string signature , string ext ,
@@ -29,7 +31,6 @@ private predicate modelContainsMethodName(string name) {
2931}
3032
3133private predicate parsedRawMethodName ( string rawName , string name , string argLabels ) {
32- // NOTE: We only support method names at the moment, not property names like `String.count`.
3334 modelContainsMethodName ( rawName ) and
3435 exists ( string regex |
3536 regex = "([^(]+)\\((.*)\\)" and
@@ -123,8 +124,36 @@ private predicate callableSelector(
123124 )
124125}
125126
127+ /** Holds if `name` from a model refers to a property, such as `count` in `String.count`. */
128+ private predicate isRawPropertyName ( string name ) {
129+ modelContainsMethodName ( name ) and
130+ not name .matches ( "%(%" )
131+ }
132+
133+ /** Holds if `cls` is named `type` or, if `subtypes` is true, is a subclass of `type`. */
134+ bindingset [ cls, type]
135+ private predicate classMatchesType ( ClassLikeDeclaration cls , string type , boolean subtypes ) {
136+ cls .getQualifiedName ( ) = type and subtypes = [ true , false ]
137+ or
138+ isSubclassOfType ( cls , type ) and subtypes = true
139+ }
140+
141+ /**
142+ * Holds if `access` may be an access to the property `name`, and its base has the inferred
143+ * type `cls`.
144+ */
145+ pragma [ nomagic]
146+ private predicate propertyAccessSelector (
147+ MemberAccessExpr access , ClassLikeDeclaration cls , string name
148+ ) {
149+ name = access .getMemberName ( ) and
150+ name = any ( Selector s ) .getPropertyName ( ) and
151+ not access = any ( CallExpr call ) .getCallee ( ) and
152+ cls = T:: inferType ( access .getBase ( ) ) .( Types:: ClassLikeDeclarationType ) .getClassLikeDeclaration ( )
153+ }
154+
126155private newtype TSelector =
127- MkSelector ( string type , boolean subtypes , string name , string argLabels , string argTypes ) {
156+ MkCallSelector ( string type , boolean subtypes , string name , string argLabels , string argTypes ) {
128157 exists ( string rawName |
129158 legacySourceModel ( _, type , subtypes , rawName , argTypes , _, _, _, _)
130159 or
@@ -134,21 +163,38 @@ private newtype TSelector =
134163 |
135164 parsedRawMethodName ( rawName , name , argLabels )
136165 )
166+ } or
167+ MkPropertySelector ( string type , boolean subtypes , string name ) {
168+ (
169+ legacySourceModel ( _, type , subtypes , name , _, _, _, _, _)
170+ or
171+ legacySinkModel ( _, type , subtypes , name , _, _, _, _, _)
172+ ) and
173+ isRawPropertyName ( name )
137174 }
138175
139176/**
140- * A tuple from a model that identifies call sites.
177+ * A tuple from a model that identifies call sites, callables, or property accesses .
141178 */
142179class Selector extends TSelector {
143- string getTypeString ( ) { this = MkSelector ( result , _, _, _, _) }
180+ string getTypeString ( ) {
181+ this = MkCallSelector ( result , _, _, _, _) or this = MkPropertySelector ( result , _, _)
182+ }
183+
184+ boolean getSubtypesFlag ( ) {
185+ this = MkCallSelector ( _, result , _, _, _) or this = MkPropertySelector ( _, result , _)
186+ }
144187
145- boolean getSubtypesFlag ( ) { this = MkSelector ( _, result , _, _, _) }
188+ string getName ( ) {
189+ this = MkCallSelector ( _, _, result , _, _) or this = MkPropertySelector ( _, _, result )
190+ }
146191
147- string getName ( ) { this = MkSelector ( _, _, result , _, _) }
192+ /** Gets the property name, if this is a property selector. */
193+ string getPropertyName ( ) { this = MkPropertySelector ( _, _, result ) }
148194
149- string getArgLabels ( ) { this = MkSelector ( _, _, _, result , _) }
195+ string getArgLabels ( ) { this = MkCallSelector ( _, _, _, result , _) }
150196
151- string getArgTypes ( ) { this = MkSelector ( _, _, _, _, result ) }
197+ string getArgTypes ( ) { this = MkCallSelector ( _, _, _, _, result ) }
152198
153199 private string ppSubtypes ( ) {
154200 if this .getSubtypesFlag ( ) = true then result = "+" else result = ""
@@ -158,19 +204,21 @@ class Selector extends TSelector {
158204 result =
159205 this .getTypeString ( ) + this .ppSubtypes ( ) + "." + this .getName ( ) + "(" + this .getArgLabels ( ) +
160206 ")" + this .getArgTypes ( )
207+ or
208+ result = this .getTypeString ( ) + this .ppSubtypes ( ) + "." + this .getPropertyName ( )
161209 }
162210
163211 /** Holds if `name,argLabels` should be used to join with `methodCallSelector`. */
164212 pragma [ nomagic]
165213 private predicate matchesMethodCallSelector ( string name , string argLabels ) {
166- this = MkSelector ( _, _, name , argLabels , _) and
214+ this = MkCallSelector ( _, _, name , argLabels , _) and
167215 name != "init"
168216 }
169217
170218 /** Holds if `name,argLabels` should be used to join with `constructorCallSelector`. */
171219 pragma [ nomagic]
172220 private predicate matchesConstructorCallSelector ( string name , string argLabels ) {
173- this = MkSelector ( name , _, "init" , argLabels , _)
221+ this = MkCallSelector ( name , _, "init" , argLabels , _)
174222 }
175223
176224 predicate matchesCall ( CallExpr call ) {
@@ -186,7 +234,19 @@ class Selector extends TSelector {
186234 predicate matchesCallable ( Callable callable ) {
187235 exists ( string type , string name , string argLabels |
188236 callableSelector ( callable , type , name , argLabels ) and
189- this = MkSelector ( type , true , name , argLabels , _)
237+ this = MkCallSelector ( type , true , name , argLabels , _)
238+ )
239+ }
240+
241+ /**
242+ * Holds if `access` is an access to the property identified by this selector, as determined
243+ * by the inferred type of its base.
244+ */
245+ predicate matchesPropertyAccess ( MemberAccessExpr access ) {
246+ exists ( ClassLikeDeclaration cls , string type , boolean subtypes , string name |
247+ this = MkPropertySelector ( type , subtypes , name ) and
248+ propertyAccessSelector ( access , cls , name ) and
249+ classMatchesType ( cls , type , subtypes )
190250 )
191251 }
192252
@@ -267,27 +327,57 @@ private DataFlow::Node getSourceFromCallable(Callable callable, AccessPathToken
267327 result .isReceiverParameter ( callable )
268328}
269329
330+ /**
331+ * Gets the source from the property `access` specified by `path`.
332+ */
333+ bindingset [ access, path]
334+ private DataFlow:: Node getSourceFromPropertyAccess ( MemberAccessExpr access , AccessPath path ) {
335+ path = "" and
336+ result .isResultValue ( access )
337+ }
338+
339+ /**
340+ * Gets the sink from the property `access` specified by `path`.
341+ */
342+ bindingset [ access, path]
343+ private DataFlow:: Node getSinkFromPropertyAccess ( MemberAccessExpr access , AccessPath path ) {
344+ path = "PostUpdate" and
345+ (
346+ // The value being assigned to the property, as in `x.prop = value`
347+ result .isIncomingValue ( access )
348+ or
349+ // The property value after being mutated in-place, as in `x.prop.append(value)`
350+ result .isPostUpdate ( access )
351+ )
352+ }
353+
270354private predicate normalizedSourceModel (
271355 Selector selector , AccessPath output , string kind , string provenance
272356) {
273- exists (
274- string type , boolean subtypes , string rawName , string name , string argLabels , string argTypes
357+ exists ( string type , boolean subtypes , string rawName , string argTypes |
358+ legacySourceModel ( _ , type , subtypes , rawName , argTypes , _ , output , kind , provenance )
275359 |
276- legacySourceModel ( _, type , subtypes , rawName , argTypes , _, output , kind , provenance ) and
277- parsedRawMethodName ( rawName , name , argLabels ) and
278- selector = MkSelector ( type , subtypes , name , argLabels , argTypes )
360+ exists ( string name , string argLabels |
361+ parsedRawMethodName ( rawName , name , argLabels ) and
362+ selector = MkCallSelector ( type , subtypes , name , argLabels , argTypes )
363+ )
364+ or
365+ selector = MkPropertySelector ( type , subtypes , rawName )
279366 )
280367}
281368
282369private predicate normalizedSinkModel (
283370 Selector selector , AccessPath output , string kind , string provenance
284371) {
285- exists (
286- string type , boolean subtypes , string rawName , string name , string argLabels , string argTypes
372+ exists ( string type , boolean subtypes , string rawName , string argTypes |
373+ legacySinkModel ( _ , type , subtypes , rawName , argTypes , _ , output , kind , provenance )
287374 |
288- legacySinkModel ( _, type , subtypes , rawName , argTypes , _, output , kind , provenance ) and
289- parsedRawMethodName ( rawName , name , argLabels ) and
290- selector = MkSelector ( type , subtypes , name , argLabels , argTypes )
375+ exists ( string name , string argLabels |
376+ parsedRawMethodName ( rawName , name , argLabels ) and
377+ selector = MkCallSelector ( type , subtypes , name , argLabels , argTypes )
378+ )
379+ or
380+ selector = MkPropertySelector ( type , subtypes , rawName )
291381 )
292382}
293383
@@ -299,7 +389,7 @@ predicate normalizedSummaryModel(
299389 |
300390 legacySummaryModel ( _, type , subtypes , rawName , argTypes , _, input , output , kind , provenance ) and
301391 parsedRawMethodName ( rawName , name , argLabels ) and
302- selector = MkSelector ( type , subtypes , name , argLabels , argTypes )
392+ selector = MkCallSelector ( type , subtypes , name , argLabels , argTypes )
303393 )
304394}
305395
@@ -319,6 +409,11 @@ DataFlow::Node getASource(string kind, string provenance) {
319409 selector .matchesCallable ( callable ) and
320410 result = getSourceFromCallable ( callable , output )
321411 )
412+ or
413+ exists ( MemberAccessExpr access |
414+ selector .matchesPropertyAccess ( access ) and
415+ result = getSourceFromPropertyAccess ( access , output )
416+ )
322417 )
323418}
324419
@@ -338,6 +433,11 @@ DataFlow::Node getASink(string kind, string provenance) {
338433 selector .matchesCallable ( callable ) and
339434 result = getSinkFromCallable ( callable , input )
340435 )
436+ or
437+ exists ( MemberAccessExpr access |
438+ selector .matchesPropertyAccess ( access ) and
439+ result = getSinkFromPropertyAccess ( access , input )
440+ )
341441 )
342442}
343443
0 commit comments