@@ -45,21 +45,19 @@ class ActiveRecordModelClass extends ClassDeclaration {
45
45
}
46
46
47
47
/**
48
- * Returns true if `call` may refer to a method that returns a database value
49
- * if invoked against an instance of this class.
50
- */
51
- predicate methodCallMayAccessField ( MethodCall call ) {
52
- not (
53
- // Methods whose names can be hardcoded
54
- isCallToBuiltInMethod ( call )
55
- or
56
- // Methods defined in the ActiveRecord model class that do not return database fields
57
- exists ( Method m | m = this .getMethod ( call .getMethodName ( ) ) |
58
- forall ( DataFlow:: Node returned , ActiveRecordInstanceMethodCall c |
59
- exprNodeReturnedFrom ( returned , m ) and c .flowsTo ( returned )
60
- |
61
- not this .methodCallMayAccessField ( returned .asExpr ( ) .getExpr ( ) )
62
- )
48
+ * Gets methods defined in this class that may access a field from the database.
49
+ */
50
+ Method methodMayAccessField ( ) {
51
+ result = this .getAMethod ( ) and
52
+ // There is a value that can be returned by this method which may include field data
53
+ exists ( DataFlow:: Node returned , ActiveRecordInstanceMethodCall cNode , MethodCall c |
54
+ exprNodeReturnedFrom ( returned , result ) and cNode .flowsTo ( returned ) and c = cNode .asExpr ( ) .getExpr ( ) |
55
+ // The referenced method is not built-in, and
56
+ not isCallToBuiltInMethod ( c ) and (
57
+ // There is no matching method definition in the class, or
58
+ not exists ( cNode .getInstance ( ) .getClass ( ) .getMethod ( c .getMethodName ( ) ) ) or
59
+ // The called method can access a field
60
+ c .getATarget ( ) = cNode .getInstance ( ) .getClass ( ) .methodMayAccessField ( )
63
61
)
64
62
)
65
63
}
@@ -201,11 +199,23 @@ private string constantQualifiedName(ConstantWriteAccess w) {
201
199
/**
202
200
* A node that may evaluate to one or more `ActiveRecordModelClass` instances.
203
201
*/
204
- abstract class ActiveRecordModelInstantiation extends OrmInstantiation:: Range {
202
+ abstract class ActiveRecordModelInstantiation extends OrmInstantiation:: Range , DataFlow :: LocalSourceNode {
205
203
abstract ActiveRecordModelClass getClass ( ) ;
206
204
207
205
override predicate methodCallMayAccessField ( MethodCall call ) {
208
- this .getClass ( ) .methodCallMayAccessField ( call )
206
+ // The method is not a built-in
207
+ not isCallToBuiltInMethod ( call ) and (
208
+ // There is no matching method definition in the class, or
209
+ not exists ( this .getClass ( ) .getMethod ( call .getMethodName ( ) ) ) or
210
+ // The called method can access a field
211
+ exists ( Method m |
212
+ m = this .getClass ( ) .methodMayAccessField ( ) |
213
+ // TODO: this may be too broad - we haven't limited the call target
214
+ // It's likely that the call graph isn't sufficient here, as resolution
215
+ // e.g. from ActionView views won't catch everything
216
+ m .getName ( ) = call .getMethodName ( )
217
+ )
218
+ )
209
219
}
210
220
}
211
221
@@ -260,8 +270,7 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation
260
270
}
261
271
262
272
// A `self` reference that may resolve to an active record model object
263
- private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation ,
264
- DataFlow:: LocalSourceNode {
273
+ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInstantiation {
265
274
private ActiveRecordModelClass cls ;
266
275
267
276
ActiveRecordModelClassSelfReference ( ) {
@@ -276,15 +285,19 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta
276
285
}
277
286
278
287
// A (locally tracked) active record model object
279
- private DataFlow:: Node activeRecordModelInstance ( ) {
280
- result instanceof ActiveRecordModelInstantiation
281
- or
282
- exists ( ActiveRecordModelInstantiation inst | inst .( DataFlow:: LocalSourceNode ) .flowsTo ( result ) )
288
+ private class ActiveRecordInstance extends DataFlow:: Node {
289
+ private ActiveRecordModelInstantiation instantiation ;
290
+
291
+ ActiveRecordInstance ( ) { this = instantiation or instantiation .flowsTo ( this ) }
292
+
293
+ ActiveRecordModelClass getClass ( ) { result = instantiation .getClass ( ) }
283
294
}
284
295
285
296
// A call whose receiver may be an active record model object
286
297
private class ActiveRecordInstanceMethodCall extends DataFlow:: CallNode {
287
- ActiveRecordInstanceMethodCall ( ) { this .getReceiver ( ) = activeRecordModelInstance ( ) }
298
+ private ActiveRecordInstance instance ;
299
+ ActiveRecordInstanceMethodCall ( ) { this .getReceiver ( ) = instance }
300
+ ActiveRecordInstance getInstance ( ) { result = instance }
288
301
}
289
302
290
303
private string activeRecordPersistenceInstanceMethodName ( ) {
0 commit comments