@@ -249,6 +249,60 @@ module API {
249249 */
250250 Node getASubscript ( ) { result = this .getASuccessor ( Label:: subscript ( ) ) }
251251
252+ /**
253+ * Gets a node representing an index of a subscript of this node.
254+ * For example, in `obj[x]`, `x` is an index of `obj`.
255+ */
256+ Node getIndex ( ) { result = this .getASuccessor ( Label:: index ( ) ) }
257+
258+ /**
259+ * Gets a node representing a subscript of this node at (string) index `key`.
260+ * This requires that the index can be statically determined.
261+ *
262+ * For example, the subscripts of `a` and `b` below would be found using
263+ * the index `foo`:
264+ * ```py
265+ * a["foo"]
266+ * x = "foo" if cond else "bar"
267+ * b[x]
268+ * ```
269+ */
270+ Node getSubscript ( string key ) {
271+ exists ( API:: Node index | result = this .getSubscriptAt ( index ) |
272+ key = index .getAValueReachingSink ( ) .asExpr ( ) .( PY:: StrConst ) .getText ( )
273+ )
274+ }
275+
276+ /**
277+ * Gets a node representing a subscript of this node at index `index`.
278+ */
279+ Node getSubscriptAt ( API:: Node index ) {
280+ result = this .getASubscript ( ) and
281+ index = this .getIndex ( ) and
282+ (
283+ // subscripting
284+ exists ( PY:: SubscriptNode subscript |
285+ subscript .getObject ( ) = this .getAValueReachableFromSource ( ) .asCfgNode ( ) and
286+ subscript .getIndex ( ) = index .asSink ( ) .asCfgNode ( )
287+ |
288+ // reading
289+ subscript = result .asSource ( ) .asCfgNode ( )
290+ or
291+ // writing
292+ subscript .( PY:: DefinitionNode ) .getValue ( ) = result .asSink ( ) .asCfgNode ( )
293+ )
294+ or
295+ // dictionary literals
296+ exists ( PY:: Dict dict , PY:: KeyValuePair item |
297+ dict = this .getAValueReachingSink ( ) .asExpr ( ) and
298+ dict .getItem ( _) = item and
299+ item .getKey ( ) = index .asSink ( ) .asExpr ( )
300+ |
301+ item .getValue ( ) = result .asSink ( ) .asExpr ( )
302+ )
303+ )
304+ }
305+
252306 /**
253307 * Gets a string representation of the lexicographically least among all shortest access paths
254308 * from the root to this node.
@@ -405,7 +459,7 @@ module API {
405459 Node builtin ( string n ) { result = moduleImport ( "builtins" ) .getMember ( n ) }
406460
407461 /**
408- * An `CallCfgNode` that is connected to the API graph.
462+ * A `CallCfgNode` that is connected to the API graph.
409463 *
410464 * Can be used to reason about calls to an external API in which the correlation between
411465 * parameters and/or return values must be retained.
@@ -694,12 +748,24 @@ module API {
694748 rhs = aw .getValue ( )
695749 )
696750 or
697- // TODO: I had expected `DataFlow::AttrWrite` to contain the attribute writes from a dict, that's how JS works.
751+ // dictionary literals
698752 exists ( PY:: Dict dict , PY:: KeyValuePair item |
699753 dict = pred .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) and
700- dict .getItem ( _) = item and
701- lbl = Label:: member ( item .getKey ( ) .( PY:: StrConst ) .getS ( ) ) and
702- rhs .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) = item .getValue ( )
754+ dict .getItem ( _) = item
755+ |
756+ // from `x` to `{ "key": x }`
757+ rhs .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) = item .getValue ( ) and
758+ lbl = Label:: subscript ( )
759+ or
760+ // from `"key"` to `{ "key": x }`
761+ rhs .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) = item .getKey ( ) and
762+ lbl = Label:: index ( )
763+ )
764+ or
765+ // list literals, from `x` to `[x]`
766+ exists ( PY:: List list | list = pred .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) |
767+ rhs .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) = list .getAnElt ( ) and
768+ lbl = Label:: subscript ( )
703769 )
704770 or
705771 exists ( PY:: CallableExpr fn | fn = pred .( DataFlow:: ExprNode ) .getNode ( ) .getNode ( ) |
@@ -720,6 +786,20 @@ module API {
720786 lbl = Label:: memberFromRef ( aw )
721787 )
722788 or
789+ // subscripting
790+ exists ( DataFlow:: LocalSourceNode src , DataFlow:: Node subscript , DataFlow:: Node index |
791+ use ( base , src ) and
792+ subscript = trackUseNode ( src ) .getSubscript ( index )
793+ |
794+ // from `x` to a definition of `x[...]`
795+ rhs .asCfgNode ( ) = subscript .asCfgNode ( ) .( PY:: DefinitionNode ) .getValue ( ) and
796+ lbl = Label:: subscript ( )
797+ or
798+ // from `x` to `"key"` in `x["key"]`
799+ rhs = index and
800+ lbl = Label:: index ( )
801+ )
802+ or
723803 exists ( EntryPoint entry |
724804 base = root ( ) and
725805 lbl = Label:: entryPoint ( entry ) and
@@ -757,7 +837,8 @@ module API {
757837 or
758838 // Subscripting a node that is a use of `base`
759839 lbl = Label:: subscript ( ) and
760- ref = pred .getASubscript ( )
840+ ref = pred .getSubscript ( _) and
841+ ref .asCfgNode ( ) .isLoad ( )
761842 or
762843 // Subclassing a node
763844 lbl = Label:: subclass ( ) and
@@ -973,8 +1054,7 @@ module API {
9731054 member = any ( DataFlow:: AttrRef pr ) .getAttributeName ( ) or
9741055 exists ( Builtins:: likelyBuiltin ( member ) ) or
9751056 ImportStar:: namePossiblyDefinedInImportStar ( _, member , _) or
976- Impl:: prefix_member ( _, member , _) or
977- member = any ( PY:: Dict d ) .getAnItem ( ) .( PY:: KeyValuePair ) .getKey ( ) .( PY:: StrConst ) .getS ( )
1057+ Impl:: prefix_member ( _, member , _)
9781058 } or
9791059 MkLabelUnknownMember ( ) or
9801060 MkLabelParameter ( int i ) {
@@ -992,6 +1072,7 @@ module API {
9921072 MkLabelSubclass ( ) or
9931073 MkLabelAwait ( ) or
9941074 MkLabelSubscript ( ) or
1075+ MkLabelIndex ( ) or
9951076 MkLabelEntryPoint ( EntryPoint ep )
9961077
9971078 /** A label for a module. */
@@ -1072,6 +1153,11 @@ module API {
10721153 override string toString ( ) { result = "getASubscript()" }
10731154 }
10741155
1156+ /** A label that gets the index of a subscript. */
1157+ class LabelIndex extends ApiLabel , MkLabelIndex {
1158+ override string toString ( ) { result = "getIndex()" }
1159+ }
1160+
10751161 /** A label for entry points. */
10761162 class LabelEntryPoint extends ApiLabel , MkLabelEntryPoint {
10771163 private EntryPoint entry ;
@@ -1120,6 +1206,9 @@ module API {
11201206 /** Gets the `subscript` edge label. */
11211207 LabelSubscript subscript ( ) { any ( ) }
11221208
1209+ /** Gets the `subscript` edge label. */
1210+ LabelIndex index ( ) { any ( ) }
1211+
11231212 /** Gets the label going from the root node to the nodes associated with the given entry point. */
11241213 LabelEntryPoint entryPoint ( EntryPoint ep ) { result = MkLabelEntryPoint ( ep ) }
11251214 }
0 commit comments