44
55import csharp
66private import DataFlow
7+ private import semmle.code.csharp.commons.QualifiedName
78private import semmle.code.csharp.frameworks.System
89private import semmle.code.csharp.frameworks.system.data.Entity
910private import semmle.code.csharp.frameworks.system.collections.Generic
@@ -236,7 +237,7 @@ module EntityFramework {
236237 * }
237238 * ```
238239 */
239- private Property getADbSetProperty ( Class elementType ) {
240+ Property getADbSetProperty ( Class elementType ) {
240241 exists ( ConstructedClass c |
241242 result .getType ( ) = c and
242243 c .getUnboundDeclaration ( ) instanceof DbSet and
@@ -334,6 +335,18 @@ module EntityFramework {
334335 result .getName ( ) .matches ( "SaveChanges%" )
335336 }
336337
338+ /**
339+ * Gets the string representation for synthetic identifiers for SaveChanges methods
340+ * on this.
341+ */
342+ string getSyntheticNames ( ) {
343+ exists ( string qualifier , string type , string name |
344+ this .getASaveChanges ( ) .hasQualifiedName ( qualifier , type , name )
345+ |
346+ result = getQualifiedName ( qualifier , type , name )
347+ )
348+ }
349+
337350 /** Holds if component stack `head :: tail` is required for the input specification. */
338351 predicate requiresComponentStackIn (
339352 Content head , Type headType , SummaryComponentStack tail , int dist
@@ -351,56 +364,150 @@ module EntityFramework {
351364
352365 /** Holds if component stack `head :: tail` is required for the output specification. */
353366 predicate requiresComponentStackOut (
354- Content head , Type headType , SummaryComponentStack tail , int dist
367+ Content head , Type headType , SummaryComponentStack tail , int dist ,
368+ DbContextClassSetProperty dbSetProp
355369 ) {
356- exists ( Property dbSetProp , PropertyContent c1 |
370+ exists ( PropertyContent c1 |
357371 dbSetProp = this .getADbSetProperty ( headType ) and
358372 this .stepRev ( c1 , _, head , headType , 0 ) and
359373 c1 .getProperty ( ) = dbSetProp and
360- tail = SummaryComponentStack:: jump ( dbSetProp . getGetter ( ) ) and
374+ tail = SummaryComponentStack:: return ( ) and
361375 dist = 0
362376 )
363377 or
364378 exists ( Content tailHead , SummaryComponentStack tailTail , Type tailType |
365- this .requiresComponentStackOut ( tailHead , tailType , tailTail , dist - 1 ) and
379+ this .requiresComponentStackOut ( tailHead , tailType , tailTail , dist - 1 , dbSetProp ) and
366380 tail = SummaryComponentStack:: push ( SummaryComponent:: content ( tailHead ) , tailTail ) and
367381 this .stepRev ( tailHead , tailType , head , headType , dist )
368382 )
369383 }
370384 }
371385
372- private class DbContextSaveChanges extends EFSummarizedCallable {
386+ private class DbContextClassSetProperty extends Property {
373387 private DbContextClass c ;
374388
375- DbContextSaveChanges ( ) { this = c .getASaveChanges ( ) }
389+ DbContextClassSetProperty ( ) { this = c .getADbSetProperty ( _ ) }
376390
377- pragma [ noinline]
378- private predicate input ( SummaryComponentStack input , Property mapped ) {
379- exists ( PropertyContent head , SummaryComponentStack tail |
380- c .requiresComponentStackIn ( head , _, tail , _) and
381- head .getProperty ( ) = mapped and
382- mapped = c .getAColumnProperty ( _) and
383- input = SummaryComponentStack:: push ( SummaryComponent:: content ( head ) , tail )
391+ /**
392+ * Gets the string representation for a synthetic identifier for this.
393+ */
394+ string getSyntheticName ( ) {
395+ exists ( string qualifier , string type , string name |
396+ this .hasQualifiedName ( qualifier , type , name )
397+ |
398+ result = getQualifiedName ( qualifier , type , name )
384399 )
385400 }
386401
387- pragma [ noinline]
388- private predicate output ( SummaryComponentStack output , Property mapped ) {
389- exists ( PropertyContent head , SummaryComponentStack tail |
390- c .requiresComponentStackOut ( head , _, tail , _) and
391- head .getProperty ( ) = mapped and
392- mapped = c .getAColumnProperty ( _) and
393- output = SummaryComponentStack:: push ( SummaryComponent:: content ( head ) , tail )
402+ /**
403+ * Gets the context class where this is a Db set property.
404+ */
405+ DbContextClass getDbContextClass ( ) { result = c }
406+ }
407+
408+ /**
409+ * Holds if `input` is a valid summary component stack for property `mapped`
410+ * for the context class `c`.
411+ */
412+ pragma [ noinline]
413+ predicate input ( DbContextClass c , SummaryComponentStack input , Property mapped ) {
414+ exists ( PropertyContent head , SummaryComponentStack tail |
415+ c .requiresComponentStackIn ( head , _, tail , _) and
416+ head .getProperty ( ) = mapped and
417+ mapped = c .getAColumnProperty ( _) and
418+ input = SummaryComponentStack:: push ( SummaryComponent:: content ( head ) , tail )
419+ )
420+ }
421+
422+ /**
423+ * Holds if `output` is a valid summary component stack for the getter of `dbSet`
424+ * for property `mapped` for the context class `c`.
425+ */
426+ pragma [ noinline]
427+ predicate output (
428+ DbContextClass c , SummaryComponentStack output , Property mapped , DbContextClassSetProperty dbSet
429+ ) {
430+ exists ( PropertyContent head , SummaryComponentStack tail |
431+ c .requiresComponentStackOut ( head , _, tail , _, dbSet ) and
432+ head .getProperty ( ) = mapped and
433+ mapped = c .getAColumnProperty ( _) and
434+ output = SummaryComponentStack:: push ( SummaryComponent:: content ( head ) , tail )
435+ )
436+ }
437+
438+ bindingset [ save, prop, stack1, stack2]
439+ private string getFullSyntheticName (
440+ string save , string prop , SummaryComponentStack stack1 , SummaryComponentStack stack2
441+ ) {
442+ result =
443+ save + "#" //
444+ + prop + "#" //
445+ + SummaryComponentStack:: getComponentStack ( stack1 ) + "#" //
446+ + SummaryComponentStack:: getComponentStack ( stack2 )
447+ }
448+
449+ private class DbContextClassSetPropertySynthetic extends EFSummarizedCallable {
450+ private DbContextClassSetProperty p ;
451+
452+ DbContextClassSetPropertySynthetic ( ) { this = p .getGetter ( ) }
453+
454+ override predicate propagatesFlow (
455+ SummaryComponentStack input , SummaryComponentStack output , boolean preservesValue
456+ ) {
457+ exists ( SummaryComponentStack synthetic , string name , DbContextClass c , Property mapped |
458+ preservesValue = true and
459+ c = p .getDbContextClass ( ) and
460+ input ( c , synthetic , mapped ) and
461+ output ( c , output , mapped , p ) and
462+ name = getFullSyntheticName ( c .getSyntheticNames ( ) , p .getSyntheticName ( ) , synthetic , output ) and
463+ input = SummaryComponentStack:: syntheticGlobal ( name )
464+ )
465+ }
466+ }
467+
468+ private class DbContextSaveChanges extends EFSummarizedCallable {
469+ private DbContextClass c ;
470+
471+ DbContextSaveChanges ( ) { this = c .getASaveChanges ( ) }
472+
473+ private string getSyntheticName ( ) {
474+ exists ( string qualifier , string type , string name |
475+ this .( Method ) .hasQualifiedName ( qualifier , type , name )
476+ |
477+ result = getQualifiedName ( qualifier , type , name )
394478 )
395479 }
396480
397481 override predicate propagatesFlow (
398482 SummaryComponentStack input , SummaryComponentStack output , boolean preservesValue
399483 ) {
400- exists ( Property mapped |
484+ exists (
485+ SummaryComponentStack synthetic , string name , Property mapped ,
486+ DbContextClassSetProperty dbSet
487+ |
401488 preservesValue = true and
402- this .input ( input , mapped ) and
403- this .output ( output , mapped )
489+ input ( c , input , mapped ) and
490+ output ( c , synthetic , mapped , dbSet ) and
491+ name =
492+ getFullSyntheticName ( this .getSyntheticName ( ) , dbSet .getSyntheticName ( ) , input , synthetic ) and
493+ output = SummaryComponentStack:: syntheticGlobal ( name )
494+ )
495+ }
496+ }
497+
498+ /**
499+ * Add all `DbContext` property names as potential synthetic globals.
500+ */
501+ private class EFSummarizedCallableSyntheticGlobal extends SummaryComponent:: SyntheticGlobal {
502+ EFSummarizedCallableSyntheticGlobal ( ) {
503+ exists (
504+ DbContextClass c , SummaryComponentStack input , SummaryComponentStack output ,
505+ Property mapped , DbContextClassSetProperty dbSet
506+ |
507+ input ( c , input , mapped ) and
508+ output ( c , output , mapped , dbSet )
509+ |
510+ this = getFullSyntheticName ( c .getSyntheticNames ( ) , dbSet .getSyntheticName ( ) , input , output )
404511 )
405512 }
406513 }
@@ -411,7 +518,7 @@ module EntityFramework {
411518 exists ( Content c | head = SummaryComponent:: content ( c ) |
412519 any ( DbContextClass cls ) .requiresComponentStackIn ( c , _, tail , _)
413520 or
414- any ( DbContextClass cls ) .requiresComponentStackOut ( c , _, tail , _)
521+ any ( DbContextClass cls ) .requiresComponentStackOut ( c , _, tail , _, _ )
415522 )
416523 }
417524 }
0 commit comments