4
4
5
5
import csharp
6
6
private import DataFlow
7
+ private import semmle.code.csharp.commons.QualifiedName
7
8
private import semmle.code.csharp.frameworks.System
8
9
private import semmle.code.csharp.frameworks.system.data.Entity
9
10
private import semmle.code.csharp.frameworks.system.collections.Generic
@@ -236,7 +237,7 @@ module EntityFramework {
236
237
* }
237
238
* ```
238
239
*/
239
- private Property getADbSetProperty ( Class elementType ) {
240
+ Property getADbSetProperty ( Class elementType ) {
240
241
exists ( ConstructedClass c |
241
242
result .getType ( ) = c and
242
243
c .getUnboundDeclaration ( ) instanceof DbSet and
@@ -334,6 +335,18 @@ module EntityFramework {
334
335
result .getName ( ) .matches ( "SaveChanges%" )
335
336
}
336
337
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
+
337
350
/** Holds if component stack `head :: tail` is required for the input specification. */
338
351
predicate requiresComponentStackIn (
339
352
Content head , Type headType , SummaryComponentStack tail , int dist
@@ -351,56 +364,150 @@ module EntityFramework {
351
364
352
365
/** Holds if component stack `head :: tail` is required for the output specification. */
353
366
predicate requiresComponentStackOut (
354
- Content head , Type headType , SummaryComponentStack tail , int dist
367
+ Content head , Type headType , SummaryComponentStack tail , int dist ,
368
+ DbContextClassSetProperty dbSetProp
355
369
) {
356
- exists ( Property dbSetProp , PropertyContent c1 |
370
+ exists ( PropertyContent c1 |
357
371
dbSetProp = this .getADbSetProperty ( headType ) and
358
372
this .stepRev ( c1 , _, head , headType , 0 ) and
359
373
c1 .getProperty ( ) = dbSetProp and
360
- tail = SummaryComponentStack:: jump ( dbSetProp . getGetter ( ) ) and
374
+ tail = SummaryComponentStack:: return ( ) and
361
375
dist = 0
362
376
)
363
377
or
364
378
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
366
380
tail = SummaryComponentStack:: push ( SummaryComponent:: content ( tailHead ) , tailTail ) and
367
381
this .stepRev ( tailHead , tailType , head , headType , dist )
368
382
)
369
383
}
370
384
}
371
385
372
- private class DbContextSaveChanges extends EFSummarizedCallable {
386
+ private class DbContextClassSetProperty extends Property {
373
387
private DbContextClass c ;
374
388
375
- DbContextSaveChanges ( ) { this = c .getASaveChanges ( ) }
389
+ DbContextClassSetProperty ( ) { this = c .getADbSetProperty ( _ ) }
376
390
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 )
384
399
)
385
400
}
386
401
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 )
394
478
)
395
479
}
396
480
397
481
override predicate propagatesFlow (
398
482
SummaryComponentStack input , SummaryComponentStack output , boolean preservesValue
399
483
) {
400
- exists ( Property mapped |
484
+ exists (
485
+ SummaryComponentStack synthetic , string name , Property mapped ,
486
+ DbContextClassSetProperty dbSet
487
+ |
401
488
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 )
404
511
)
405
512
}
406
513
}
@@ -411,7 +518,7 @@ module EntityFramework {
411
518
exists ( Content c | head = SummaryComponent:: content ( c ) |
412
519
any ( DbContextClass cls ) .requiresComponentStackIn ( c , _, tail , _)
413
520
or
414
- any ( DbContextClass cls ) .requiresComponentStackOut ( c , _, tail , _)
521
+ any ( DbContextClass cls ) .requiresComponentStackOut ( c , _, tail , _, _ )
415
522
)
416
523
}
417
524
}
0 commit comments