22
33private import TypeTrackerPrivate
44
5- /** Any string that may appear as the name of a piece of content. */
5+ /**
6+ * Any string that may appear as the name of a piece of content. This will usually include things like:
7+ * - Attribute names (in Python)
8+ * - Property names (in JavaScript)
9+ *
10+ * In general, this can also be used to model things like stores to specific list indices. To ensure
11+ * correctness, it is important that
12+ *
13+ * - different types of content do not have overlapping names, and
14+ * - the empty string `""` is not a valid piece of content, as it is used to indicate the absence of
15+ * content instead.
16+ */
617class ContentName extends string {
718 ContentName ( ) { this = getPossibleContentName ( ) }
819}
@@ -70,14 +81,41 @@ module StepSummary {
7081 summary = ReturnStep ( )
7182 or
7283 exists ( string content |
73- basicStoreStep ( nodeFrom , nodeTo , content ) and
84+ localSourceStoreStep ( nodeFrom , nodeTo , content ) and
7485 summary = StoreStep ( content )
7586 or
7687 basicLoadStep ( nodeFrom , nodeTo , content ) and summary = LoadStep ( content )
7788 )
7889 }
79- }
8090
91+ /**
92+ * Holds if `nodeFrom` is being written to the `content` content of the object in `nodeTo`.
93+ *
94+ * Note that `nodeTo` will always be a local source node that flows to the place where the content
95+ * is written in `basicStoreStep`. This may lead to the flow of information going "back in time"
96+ * from the point of view of the execution of the program.
97+ *
98+ * For instance, if we interpret attribute writes in Python as writing to content with the same
99+ * name as the attribute and consider the following snippet
100+ *
101+ * ```python
102+ * def foo(y):
103+ * x = Foo()
104+ * bar(x)
105+ * x.attr = y
106+ * baz(x)
107+ *
108+ * def bar(x):
109+ * z = x.attr
110+ * ```
111+ * for the attribute write `x.attr = y`, we will have `content` being the literal string `"attr"`,
112+ * `nodeFrom` will be `y`, and `nodeTo` will be the object `Foo()` created on the first line of the
113+ * function. This means we will track the fact that `x.attr` can have the type of `y` into the
114+ * assignment to `z` inside `bar`, even though this attribute write happens _after_ `bar` is called.
115+ */
116+ predicate localSourceStoreStep ( Node nodeFrom , LocalSourceNode nodeTo , string content ) {
117+ exists ( Node obj | nodeTo .flowsTo ( obj ) and basicStoreStep ( nodeFrom , obj , content ) )
118+ }
81119}
82120
83121private newtype TTypeTracker = MkTypeTracker ( Boolean hasCall , OptionalContentName content )
@@ -92,7 +130,7 @@ private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalContentNam
92130 *
93131 * It is recommended that all uses of this type are written in the following form,
94132 * for tracking some type `myType`:
95- * ```
133+ * ```ql
96134 * DataFlow::LocalSourceNode myType(DataFlow::TypeTracker t) {
97135 * t.start() and
98136 * result = < source of myType >
@@ -253,7 +291,7 @@ private newtype TTypeBackTracker = MkTypeBackTracker(Boolean hasReturn, Optional
253291 * It is recommended that all uses of this type are written in the following form,
254292 * for back-tracking some callback type `myCallback`:
255293 *
256- * ```
294+ * ```ql
257295 * DataFlow::LocalSourceNode myCallback(DataFlow::TypeBackTracker t) {
258296 * t.start() and
259297 * result = (< some API call >).getArgument(< n >).getALocalSource()
0 commit comments