Skip to content

Commit 120b50f

Browse files
authored
Merge pull request #2708 from asger-semmle/js/react-flow-through-imports
Approved by esbena
2 parents d0ac846 + b306571 commit 120b50f

File tree

7 files changed

+62
-3
lines changed

7 files changed

+62
-3
lines changed

javascript/ql/src/semmle/javascript/frameworks/React.qll

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,15 @@ abstract class ReactComponent extends ASTNode {
3838
abstract AbstractValue getAbstractComponent();
3939

4040
/**
41-
* Gets the value that instantiates this component when invoked.
41+
* Gets the function that instantiates this component when invoked.
4242
*/
4343
abstract DataFlow::SourceNode getComponentCreatorSource();
4444

45+
/**
46+
* Gets a reference to the function that instantiates this component when invoked.
47+
*/
48+
abstract DataFlow::SourceNode getAComponentCreatorReference();
49+
4550
/**
4651
* Gets a reference to this component.
4752
*/
@@ -181,7 +186,7 @@ abstract class ReactComponent extends ASTNode {
181186
* constructor of this component.
182187
*/
183188
DataFlow::SourceNode getACandidatePropsSource() {
184-
result.flowsTo(getComponentCreatorSource().getAnInvocation().getArgument(0))
189+
result.flowsTo(getAComponentCreatorReference().getAnInvocation().getArgument(0))
185190
or
186191
result = getADefaultPropsSource()
187192
or
@@ -269,6 +274,19 @@ class FunctionalComponent extends ReactComponent, Function {
269274

270275
override AbstractValue getAbstractComponent() { result = AbstractInstance::of(this) }
271276

277+
private DataFlow::SourceNode getAComponentCreatorReference(DataFlow::TypeTracker t) {
278+
t.start() and
279+
result = DataFlow::valueNode(this)
280+
or
281+
exists(DataFlow::TypeTracker t2 |
282+
result = getAComponentCreatorReference(t2).track(t2, t)
283+
)
284+
}
285+
286+
override DataFlow::SourceNode getAComponentCreatorReference() {
287+
result = getAComponentCreatorReference(DataFlow::TypeTracker::end())
288+
}
289+
272290
override DataFlow::SourceNode getComponentCreatorSource() { result = DataFlow::valueNode(this) }
273291

274292
override DataFlow::SourceNode getADefaultPropsSource() {
@@ -302,6 +320,10 @@ abstract private class SharedReactPreactClassComponent extends ReactComponent, C
302320

303321
override AbstractValue getAbstractComponent() { result = AbstractInstance::of(this) }
304322

323+
override DataFlow::SourceNode getAComponentCreatorReference() {
324+
result = DataFlow::valueNode(this).(DataFlow::ClassNode).getAClassReference()
325+
}
326+
305327
override DataFlow::SourceNode getComponentCreatorSource() { result = DataFlow::valueNode(this) }
306328

307329
override DataFlow::SourceNode getACandidateStateSource() {
@@ -420,6 +442,19 @@ class ES5Component extends ReactComponent, ObjectExpr {
420442

421443
override AbstractValue getAbstractComponent() { result = TAbstractObjectLiteral(this) }
422444

445+
private DataFlow::SourceNode getAComponentCreatorReference(DataFlow::TypeTracker t) {
446+
t.start() and
447+
result = create
448+
or
449+
exists(DataFlow::TypeTracker t2 |
450+
result = getAComponentCreatorReference(t2).track(t2, t)
451+
)
452+
}
453+
454+
override DataFlow::SourceNode getAComponentCreatorReference() {
455+
result = getAComponentCreatorReference(DataFlow::TypeTracker::end())
456+
}
457+
423458
override DataFlow::SourceNode getComponentCreatorSource() { result = create }
424459

425460
override DataFlow::SourceNode getACandidateStateSource() {
@@ -517,7 +552,7 @@ private class AnalyzedThisInBoundCallback extends AnalyzedNode, DataFlow::ThisNo
517552
private class ReactJSXElement extends JSXElement {
518553
ReactComponent component;
519554

520-
ReactJSXElement() { component.getComponentCreatorSource().flowsToExpr(getNameExpr()) }
555+
ReactJSXElement() { component.getAComponentCreatorReference().flowsToExpr(getNameExpr()) }
521556

522557
/**
523558
* Gets the component this element instantiates.

javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ typeInferenceMismatch
5959
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
6060
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
6161
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
62+
| importedReactComponent.jsx:4:40:4:47 | source() | exportedReactComponent.jsx:2:10:2:19 | props.text |
6263
| indexOf.js:4:11:4:18 | source() | indexOf.js:9:10:9:10 | x |
6364
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:17:14:17:14 | x |
6465
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:20:14:20:14 | y |
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export function UnsafeReactComponent(props) {
2+
sink(props.text);
3+
return <div/>
4+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { UnsafeReactComponent } from "./exportedReactComponent";
2+
3+
export function render() {
4+
return <UnsafeReactComponent text={source()}/>
5+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function MyComponent(props) {
2+
return <div style={{color: props.color}}/>
3+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { MyComponent } from "./exportedComponent";
2+
3+
export function render(color) {
4+
return <MyComponent color={color}/>
5+
}

javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ test_ReactComponent_getInstanceMethod
1515
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | render | es5.js:3:11:5:3 | functio ... v>;\\n } |
1616
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | render | es5.js:19:11:21:3 | functio ... 1>;\\n } |
1717
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | render | es6.js:2:9:4:3 | () {\\n ... v>;\\n } |
18+
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | render | exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} |
1819
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | render | plainfn.js:1:1:3:1 | functio ... div>;\\n} |
1920
| plainfn.js:5:1:7:1 | functio ... iv");\\n} | render | plainfn.js:5:1:7:1 | functio ... iv");\\n} |
2021
| plainfn.js:9:1:12:1 | functio ... rn x;\\n} | render | plainfn.js:9:1:12:1 | functio ... rn x;\\n} |
@@ -93,6 +94,7 @@ test_ReactComponent_ref
9394
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:12 | this |
9495
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:12 | this |
9596
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:12 | this |
97+
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | exportedComponent.jsx:1:8:1:7 | this |
9698
| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | this |
9799
| namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | this |
98100
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | plainfn.js:1:1:1:0 | this |
@@ -189,6 +191,7 @@ test_ReactComponent_getADirectPropsSource
189191
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | es5.js:20:24:20:33 | this.props |
190192
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | args |
191193
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:3:24:3:33 | this.props |
194+
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | exportedComponent.jsx:1:29:1:33 | props |
192195
| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | args |
193196
| namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | args |
194197
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | plainfn.js:1:16:1:20 | props |
@@ -208,6 +211,7 @@ test_ReactComponent_getADirectPropsSource
208211
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
209212
test_ReactComponent_getACandidatePropsValue
210213
| es5.js:8:13:8:19 | 'world' |
214+
| importedComponent.jsx:4:32:4:36 | color |
211215
| props.js:5:46:5:67 | "propFr ... tProps" |
212216
| props.js:7:22:7:34 | "propFromJSX" |
213217
| props.js:9:33:9:53 | "propFr ... ructor" |
@@ -222,6 +226,7 @@ test_ReactComponent
222226
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} |
223227
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} |
224228
| es6.js:14:1:20:1 | class H ... }\\n} |
229+
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} |
225230
| namedImport.js:3:1:3:28 | class C ... nent {} |
226231
| namedImport.js:5:1:5:20 | class D extends C {} |
227232
| plainfn.js:1:1:3:1 | functio ... div>;\\n} |
@@ -248,6 +253,7 @@ test_ReactComponent_getAPropRead
248253
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | name | es5.js:4:24:4:38 | this.props.name |
249254
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | name | es5.js:20:24:20:38 | this.props.name |
250255
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | name | es6.js:3:24:3:38 | this.props.name |
256+
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | color | exportedComponent.jsx:2:32:2:42 | props.color |
251257
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | name | plainfn.js:2:22:2:31 | props.name |
252258
| preact.js:1:1:7:1 | class H ... }\\n} | name | preact.js:3:9:3:18 | props.name |
253259
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | name | probably-a-component.js:3:9:3:23 | this.props.name |

0 commit comments

Comments
 (0)