@@ -45,21 +45,19 @@ class ActiveRecordModelClass extends ClassDeclaration {
4545 }
4646
4747 /**
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 ( )
6361 )
6462 )
6563 }
@@ -201,11 +199,23 @@ private string constantQualifiedName(ConstantWriteAccess w) {
201199/**
202200 * A node that may evaluate to one or more `ActiveRecordModelClass` instances.
203201 */
204- abstract class ActiveRecordModelInstantiation extends OrmInstantiation:: Range {
202+ abstract class ActiveRecordModelInstantiation extends OrmInstantiation:: Range , DataFlow :: LocalSourceNode {
205203 abstract ActiveRecordModelClass getClass ( ) ;
206204
207205 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+ )
209219 }
210220}
211221
@@ -260,8 +270,7 @@ private class ActiveRecordModelFinderCall extends ActiveRecordModelInstantiation
260270}
261271
262272// 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 {
265274 private ActiveRecordModelClass cls ;
266275
267276 ActiveRecordModelClassSelfReference ( ) {
@@ -276,15 +285,19 @@ private class ActiveRecordModelClassSelfReference extends ActiveRecordModelInsta
276285}
277286
278287// 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 ( ) }
283294}
284295
285296// A call whose receiver may be an active record model object
286297private class ActiveRecordInstanceMethodCall extends DataFlow:: CallNode {
287- ActiveRecordInstanceMethodCall ( ) { this .getReceiver ( ) = activeRecordModelInstance ( ) }
298+ private ActiveRecordInstance instance ;
299+ ActiveRecordInstanceMethodCall ( ) { this .getReceiver ( ) = instance }
300+ ActiveRecordInstance getInstance ( ) { result = instance }
288301}
289302
290303private string activeRecordPersistenceInstanceMethodName ( ) {
0 commit comments