@@ -826,6 +826,32 @@ module PrivateDjango {
826826 API:: Node getModelClass ( ) { result = modelClass }
827827 }
828828
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+
829855 /** Additional data-flow steps for Django ORM models. */
830856 class DjangOrmSteps extends AdditionalOrmSteps {
831857 override predicate storeStep (
@@ -867,7 +893,7 @@ module PrivateDjango {
867893 )
868894 or
869895 // -> DB store on synthetic node
870- nodeTo . ( SyntheticDjangoOrmModelNode ) . getModelClass ( ) = modelClass
896+ nodeTo = nodeToStoreIn ( modelClass , fieldName )
871897 )
872898 )
873899 or
@@ -877,7 +903,7 @@ module PrivateDjango {
877903 call = [ manager ( modelClass ) , querySet ( modelClass ) ] .getMember ( "update" ) .getACall ( ) and
878904 nodeFrom = call .getArgByName ( fieldName ) and
879905 c .( DataFlow:: AttributeContent ) .getAttribute ( ) = fieldName and
880- nodeTo . ( SyntheticDjangoOrmModelNode ) . getModelClass ( ) = modelClass
906+ nodeTo = nodeToStoreIn ( modelClass , fieldName )
881907 )
882908 or
883909 // synthetic -> method-call that returns collection of ORM models (all/filter/...)
@@ -900,7 +926,12 @@ module PrivateDjango {
900926 override predicate jumpStep ( DataFlow:: Node nodeFrom , DataFlow:: Node nodeTo ) {
901927 // save -> synthetic
902928 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
904935 saveCall .calls ( Model:: instance ( modelClass ) , "save" ) and
905936 nodeFrom = saveCall .getObject ( )
906937 )
0 commit comments