@@ -561,18 +561,36 @@ predicate runtimeJumpStep(Node nodeFrom, Node nodeTo) {
561561 globalVariableNestedFieldJumpStep ( nodeFrom , nodeTo )
562562}
563563
564+ /** Helper predicate for `globalVariableNestedFieldJumpStep`. */
565+ pragma [ nomagic]
566+ private predicate globalVariableAttrPathRead (
567+ ModuleVariableNode globalVar , string accessPath , AttrRead r , string attrName
568+ ) {
569+ globalVariableAttrPathAtDepth ( globalVar , accessPath , r .getObject ( ) , _) and
570+ attrName = r .getAttributeName ( )
571+ }
572+
573+ /** Helper predicate for `globalVariableNestedFieldJumpStep`. */
574+ pragma [ nomagic]
575+ private predicate globalVariableAttrPathWrite (
576+ ModuleVariableNode globalVar , string accessPath , AttrWrite w , string attrName
577+ ) {
578+ globalVariableAttrPathAtDepth ( globalVar , accessPath , w .getObject ( ) , _) and
579+ attrName = w .getAttributeName ( )
580+ }
581+
564582/**
565583 * Holds if there is a jump step from `nodeFrom` to `nodeTo` through global variable field access.
566584 * This supports tracking nested object field access through global variables like `app.obj.foo`.
567585 */
586+ pragma [ nomagic]
568587private predicate globalVariableNestedFieldJumpStep ( Node nodeFrom , Node nodeTo ) {
569588 exists ( ModuleVariableNode globalVar , AttrWrite write , AttrRead read |
570589 // Match writes and reads on the same global variable attribute path
571- exists ( string accessPath |
572- globalVariableAttrPathAtDepth ( globalVar , accessPath , write . getObject ( ) , _ ) and
573- globalVariableAttrPathAtDepth ( globalVar , accessPath , read . getObject ( ) , _ )
590+ exists ( string accessPath , string attrName |
591+ globalVariableAttrPathRead ( globalVar , accessPath , read , attrName ) and
592+ globalVariableAttrPathWrite ( globalVar , accessPath , write , attrName )
574593 ) and
575- write .getAttributeName ( ) = read .getAttributeName ( ) and
576594 nodeFrom = write .getValue ( ) and
577595 nodeTo = read
578596 )
0 commit comments