1414import javascript
1515import DataFlow
1616import PathGraph
17- import semmle.javascript.dataflow.InferredTypes
18- import semmle.javascript.dataflow.internal.FlowSteps
19-
20- /**
21- * Gets a node that refers to an element of `array`, likely obtained
22- * as a result of enumerating the elements of the array.
23- */
24- SourceNode getAnEnumeratedArrayElement ( SourceNode array ) {
25- exists ( MethodCallNode call , string name |
26- call = array .getAMethodCall ( name ) and
27- ( name = "forEach" or name = "map" ) and
28- result = call .getCallback ( 0 ) .getParameter ( 0 )
29- )
30- or
31- exists ( DataFlow:: PropRead read |
32- read = array .getAPropertyRead ( ) and
33- not exists ( read .getPropertyName ( ) ) and
34- not read .getPropertyNameExpr ( ) .analyze ( ) .getAType ( ) = TTString ( ) and
35- result = read
36- )
37- }
38-
39- /**
40- * A data flow node that refers to the name of a property obtained by enumerating
41- * the properties of some object.
42- */
43- abstract class EnumeratedPropName extends DataFlow:: Node {
44- /**
45- * Gets the data flow node holding the object whose properties are being enumerated.
46- *
47- * For example, gets `src` in `for (var key in src)`.
48- */
49- abstract DataFlow:: Node getSourceObject ( ) ;
50-
51- /**
52- * Gets a source node that refers to the object whose properties are being enumerated.
53- */
54- DataFlow:: SourceNode getASourceObjectRef ( ) {
55- result = AccessPath:: getAnAliasedSourceNode ( getSourceObject ( ) )
56- }
57-
58- /**
59- * Gets a property read that accesses the corresponding property value in the source object.
60- *
61- * For example, gets `src[key]` in `for (var key in src) { src[key]; }`.
62- */
63- SourceNode getASourceProp ( ) {
64- exists ( Node base , Node key |
65- dynamicPropReadStep ( base , key , result ) and
66- getASourceObjectRef ( ) .flowsTo ( base ) and
67- key .getImmediatePredecessor * ( ) = this
68- )
69- }
70- }
71-
72- /**
73- * Property enumeration through `for-in` for `Object.keys` or similar.
74- */
75- class ForInEnumeratedPropName extends EnumeratedPropName {
76- DataFlow:: Node object ;
77-
78- ForInEnumeratedPropName ( ) {
79- exists ( ForInStmt stmt |
80- this = DataFlow:: lvalueNode ( stmt .getLValue ( ) ) and
81- object = stmt .getIterationDomain ( ) .flow ( )
82- )
83- or
84- exists ( CallNode call |
85- call = globalVarRef ( "Object" ) .getAMemberCall ( "keys" )
86- or
87- call = globalVarRef ( "Object" ) .getAMemberCall ( "getOwnPropertyNames" )
88- or
89- call = globalVarRef ( "Reflect" ) .getAMemberCall ( "ownKeys" )
90- |
91- object = call .getArgument ( 0 ) and
92- this = getAnEnumeratedArrayElement ( call )
93- )
94- }
95-
96- override Node getSourceObject ( ) { result = object }
97- }
98-
99- /**
100- * Property enumeration through `Object.entries`.
101- */
102- class EntriesEnumeratedPropName extends EnumeratedPropName {
103- CallNode entries ;
104- SourceNode entry ;
105-
106- EntriesEnumeratedPropName ( ) {
107- entries = globalVarRef ( "Object" ) .getAMemberCall ( "entries" ) and
108- entry = getAnEnumeratedArrayElement ( entries ) and
109- this = entry .getAPropertyRead ( "0" )
110- }
111-
112- override DataFlow:: Node getSourceObject ( ) {
113- result = entries .getArgument ( 0 )
114- }
115-
116- override SourceNode getASourceProp ( ) {
117- result = super .getASourceProp ( )
118- or
119- result = entry .getAPropertyRead ( "1" )
120- }
121- }
122-
123- /**
124- * Gets a function that enumerates object properties when invoked.
125- *
126- * Invocations takes the following form:
127- * ```js
128- * fn(obj, (value, key, o) => { ... })
129- * ```
130- */
131- SourceNode propertyEnumerator ( ) {
132- result = moduleImport ( "for-own" ) or
133- result = moduleImport ( "for-in" ) or
134- result = moduleMember ( "ramda" , "forEachObjIndexed" ) or
135- result = LodashUnderscore:: member ( "forEach" ) or
136- result = LodashUnderscore:: member ( "each" )
137- }
138-
139- /**
140- * Property enumeration through a library function taking a callback.
141- */
142- class LibraryCallbackEnumeratedPropName extends EnumeratedPropName {
143- CallNode call ;
144- FunctionNode callback ;
145-
146- LibraryCallbackEnumeratedPropName ( ) {
147- call = propertyEnumerator ( ) .getACall ( ) and
148- callback = call .getCallback ( 1 ) and
149- this = callback .getParameter ( 1 )
150- }
151-
152- override Node getSourceObject ( ) {
153- result = call .getArgument ( 0 )
154- }
155-
156- override SourceNode getASourceObjectRef ( ) {
157- result = super .getASourceObjectRef ( )
158- or
159- result = callback .getParameter ( 2 )
160- }
161-
162- override SourceNode getASourceProp ( ) {
163- result = super .getASourceProp ( )
164- or
165- result = callback .getParameter ( 0 )
166- }
167- }
17+ import semmle.javascript.DynamicPropertyAccess
16818
16919/**
17020 * Holds if the properties of `node` are enumerated locally.
@@ -173,72 +23,6 @@ predicate arePropertiesEnumerated(DataFlow::SourceNode node) {
17323 node = any ( EnumeratedPropName name ) .getASourceObjectRef ( )
17424}
17525
176- /**
177- * A dynamic property access that is not obviously an array access.
178- */
179- class DynamicPropRead extends DataFlow:: SourceNode , DataFlow:: ValueNode {
180- // Use IndexExpr instead of PropRead as we're not interested in implicit accesses like
181- // rest-patterns and for-of loops.
182- override IndexExpr astNode ;
183-
184- DynamicPropRead ( ) {
185- not exists ( astNode .getPropertyName ( ) ) and
186- // Exclude obvious array access
187- astNode .getPropertyNameExpr ( ) .analyze ( ) .getAType ( ) = TTString ( )
188- }
189-
190- /** Gets the base of the dynamic read. */
191- DataFlow:: Node getBase ( ) { result = astNode .getBase ( ) .flow ( ) }
192-
193- /** Gets the node holding the name of the property. */
194- DataFlow:: Node getPropertyNameNode ( ) { result = astNode .getIndex ( ) .flow ( ) }
195-
196- /**
197- * Holds if the value of this read was assigned to earlier in the same basic block.
198- *
199- * For example, this is true for `dst[x]` on line 2 below:
200- * ```js
201- * dst[x] = {};
202- * dst[x][y] = src[y];
203- * ```
204- */
205- predicate hasDominatingAssignment ( ) {
206- exists ( DataFlow:: PropWrite write , BasicBlock bb , int i , int j , SsaVariable ssaVar |
207- write = getBase ( ) .getALocalSource ( ) .getAPropertyWrite ( ) and
208- bb .getNode ( i ) = write .getWriteNode ( ) and
209- bb .getNode ( j ) = astNode and
210- i < j and
211- write .getPropertyNameExpr ( ) = ssaVar .getAUse ( ) and
212- astNode .getIndex ( ) = ssaVar .getAUse ( )
213- )
214- }
215- }
216-
217- /**
218- * Holds if `output` is the result of `base[key]`, either directly or through
219- * one or more function calls, ignoring reads that can't access the prototype chain.
220- */
221- predicate dynamicPropReadStep ( Node base , Node key , SourceNode output ) {
222- exists ( DynamicPropRead read |
223- not read .hasDominatingAssignment ( ) and
224- base = read .getBase ( ) and
225- key = read .getPropertyNameNode ( ) and
226- output = read
227- )
228- or
229- // Summarize functions returning a dynamic property read of two parameters, such as `function getProp(obj, prop) { return obj[prop]; }`.
230- exists ( CallNode call , Function callee , ParameterNode baseParam , ParameterNode keyParam , Node innerBase , Node innerKey , SourceNode innerOutput |
231- dynamicPropReadStep ( innerBase , innerKey , innerOutput ) and
232- baseParam .flowsTo ( innerBase ) and
233- keyParam .flowsTo ( innerKey ) and
234- innerOutput .flowsTo ( callee .getAReturnedExpr ( ) .flow ( ) ) and
235- call .getACallee ( ) = callee and
236- argumentPassingStep ( call , base , callee , baseParam ) and
237- argumentPassingStep ( call , key , callee , keyParam ) and
238- output = call
239- )
240- }
241-
24226/**
24327 * Holds if `node` may flow from an enumerated prop name, possibly
24428 * into function calls (but not returns).
0 commit comments