@@ -826,6 +826,32 @@ module PrivateDjango {
826
826
API:: Node getModelClass ( ) { result = modelClass }
827
827
}
828
828
829
+ /**
830
+ * Gets a synthetic node where the data in the attribute `fieldName` can flow
831
+ * to, when a DB store is made on `subModel`, taking ORM inheritance into
832
+ * account.
833
+ *
834
+ * If `fieldName` is defined in class `base`, the results will include the
835
+ * synthetic node for `base` itself, the synthetic node for `subModel`, as
836
+ * well as all the classes in-between (in the class hierarchy).
837
+ */
838
+ SyntheticDjangoOrmModelNode nodeToStoreIn ( API:: Node subModel , string fieldName ) {
839
+ exists ( Class base , API:: Node baseModel , API:: Node resultModel |
840
+ baseModel = Model:: subclassRef ( ) and
841
+ resultModel = Model:: subclassRef ( ) and
842
+ baseModel .getASubclass * ( ) = subModel and
843
+ base = getModelClassClass ( baseModel ) and
844
+ exists ( Variable v |
845
+ base .getBody ( ) .getAnItem ( ) .( AssignStmt ) .defines ( v ) and
846
+ v .getId ( ) = fieldName
847
+ )
848
+ |
849
+ baseModel .getASubclass * ( ) = resultModel and
850
+ resultModel .getASubclass * ( ) = subModel and
851
+ result .getModelClass ( ) = resultModel
852
+ )
853
+ }
854
+
829
855
/** Additional data-flow steps for Django ORM models. */
830
856
class DjangOrmSteps extends AdditionalOrmSteps {
831
857
override predicate storeStep (
@@ -867,7 +893,7 @@ module PrivateDjango {
867
893
)
868
894
or
869
895
// -> DB store on synthetic node
870
- nodeTo . ( SyntheticDjangoOrmModelNode ) . getModelClass ( ) = modelClass
896
+ nodeTo = nodeToStoreIn ( modelClass , fieldName )
871
897
)
872
898
)
873
899
or
@@ -877,7 +903,7 @@ module PrivateDjango {
877
903
call = [ manager ( modelClass ) , querySet ( modelClass ) ] .getMember ( "update" ) .getACall ( ) and
878
904
nodeFrom = call .getArgByName ( fieldName ) and
879
905
c .( DataFlow:: AttributeContent ) .getAttribute ( ) = fieldName and
880
- nodeTo . ( SyntheticDjangoOrmModelNode ) . getModelClass ( ) = modelClass
906
+ nodeTo = nodeToStoreIn ( modelClass , fieldName )
881
907
)
882
908
or
883
909
// synthetic -> method-call that returns collection of ORM models (all/filter/...)
@@ -900,7 +926,12 @@ module PrivateDjango {
900
926
override predicate jumpStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
901
927
// save -> synthetic
902
928
exists ( API:: Node modelClass , DataFlow:: MethodCallNode saveCall |
903
- nodeTo .( SyntheticDjangoOrmModelNode ) .getModelClass ( ) = modelClass and
929
+ // TODO: The `nodeTo` should be restricted more, such that flow to
930
+ // base-classes are only for the fields that are defined in the
931
+ // base-class... but only passing on flow for a specific attribute requires flow-summaries,
932
+ // so we can do
933
+ // `obj (in obj.save call) ==read of attr==> synthetic attr on base-class ==store of attr==> synthetic for base-class`
934
+ nodeTo = nodeToStoreIn ( modelClass , _) and
904
935
saveCall .calls ( Model:: instance ( modelClass ) , "save" ) and
905
936
nodeFrom = saveCall .getObject ( )
906
937
)
0 commit comments