Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 38 additions & 3 deletions javascript/ql/src/semmle/javascript/frameworks/React.qll
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,15 @@ abstract class ReactComponent extends ASTNode {
abstract AbstractValue getAbstractComponent();

/**
* Gets the value that instantiates this component when invoked.
* Gets the function that instantiates this component when invoked.
*/
abstract DataFlow::SourceNode getComponentCreatorSource();

/**
* Gets a reference to the function that instantiates this component when invoked.
*/
abstract DataFlow::SourceNode getAComponentCreatorReference();

/**
* Gets a reference to this component.
*/
Expand Down Expand Up @@ -181,7 +186,7 @@ abstract class ReactComponent extends ASTNode {
* constructor of this component.
*/
DataFlow::SourceNode getACandidatePropsSource() {
result.flowsTo(getComponentCreatorSource().getAnInvocation().getArgument(0))
result.flowsTo(getAComponentCreatorReference().getAnInvocation().getArgument(0))
or
result = getADefaultPropsSource()
or
Expand Down Expand Up @@ -269,6 +274,19 @@ class FunctionalComponent extends ReactComponent, Function {

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

private DataFlow::SourceNode getAComponentCreatorReference(DataFlow::TypeTracker t) {
t.start() and
result = DataFlow::valueNode(this)
or
exists(DataFlow::TypeTracker t2 |
result = getAComponentCreatorReference(t2).track(t2, t)
)
}

override DataFlow::SourceNode getAComponentCreatorReference() {
result = getAComponentCreatorReference(DataFlow::TypeTracker::end())
}

override DataFlow::SourceNode getComponentCreatorSource() { result = DataFlow::valueNode(this) }

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

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

override DataFlow::SourceNode getAComponentCreatorReference() {
result = DataFlow::valueNode(this).(DataFlow::ClassNode).getAClassReference()
}

override DataFlow::SourceNode getComponentCreatorSource() { result = DataFlow::valueNode(this) }

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

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

private DataFlow::SourceNode getAComponentCreatorReference(DataFlow::TypeTracker t) {
t.start() and
result = create
or
exists(DataFlow::TypeTracker t2 |
result = getAComponentCreatorReference(t2).track(t2, t)
)
}

override DataFlow::SourceNode getAComponentCreatorReference() {
result = getAComponentCreatorReference(DataFlow::TypeTracker::end())
}

override DataFlow::SourceNode getComponentCreatorSource() { result = create }

override DataFlow::SourceNode getACandidateStateSource() {
Expand Down Expand Up @@ -517,7 +552,7 @@ private class AnalyzedThisInBoundCallback extends AnalyzedNode, DataFlow::ThisNo
private class ReactJSXElement extends JSXElement {
ReactComponent component;

ReactJSXElement() { component.getComponentCreatorSource().flowsToExpr(getNameExpr()) }
ReactJSXElement() { component.getAComponentCreatorReference().flowsToExpr(getNameExpr()) }

/**
* Gets the component this element instantiates.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ typeInferenceMismatch
| exceptions.js:144:9:144:16 | source() | exceptions.js:132:8:132:27 | returnThrownSource() |
| exceptions.js:150:13:150:20 | source() | exceptions.js:153:10:153:10 | e |
| exceptions.js:158:13:158:20 | source() | exceptions.js:161:10:161:10 | e |
| importedReactComponent.jsx:4:40:4:47 | source() | exportedReactComponent.jsx:2:10:2:19 | props.text |
| indexOf.js:4:11:4:18 | source() | indexOf.js:9:10:9:10 | x |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:17:14:17:14 | x |
| partialCalls.js:4:17:4:24 | source() | partialCalls.js:20:14:20:14 | y |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export function UnsafeReactComponent(props) {
sink(props.text);
return <div/>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { UnsafeReactComponent } from "./exportedReactComponent";

export function render() {
return <UnsafeReactComponent text={source()}/>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function MyComponent(props) {
return <div style={{color: props.color}}/>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { MyComponent } from "./exportedComponent";

export function render(color) {
return <MyComponent color={color}/>
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ test_ReactComponent_getInstanceMethod
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | render | es5.js:3:11:5:3 | functio ... v>;\\n } |
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | render | es5.js:19:11:21:3 | functio ... 1>;\\n } |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | render | es6.js:2:9:4:3 | () {\\n ... v>;\\n } |
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | render | exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} |
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | render | plainfn.js:1:1:3:1 | functio ... div>;\\n} |
| plainfn.js:5:1:7:1 | functio ... iv");\\n} | render | plainfn.js:5:1:7:1 | functio ... iv");\\n} |
| plainfn.js:9:1:12:1 | functio ... rn x;\\n} | render | plainfn.js:9:1:12:1 | functio ... rn x;\\n} |
Expand Down Expand Up @@ -93,6 +94,7 @@ test_ReactComponent_ref
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:12 | this |
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:12 | this |
| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:12 | this |
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | exportedComponent.jsx:1:8:1:7 | this |
| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | this |
| namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | this |
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | plainfn.js:1:1:1:0 | this |
Expand Down Expand Up @@ -189,6 +191,7 @@ test_ReactComponent_getADirectPropsSource
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | es5.js:20:24:20:33 | this.props |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | args |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:3:24:3:33 | this.props |
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | exportedComponent.jsx:1:29:1:33 | props |
| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | args |
| namedImport.js:5:1:5:20 | class D extends C {} | namedImport.js:5:19:5:18 | args |
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | plainfn.js:1:16:1:20 | props |
Expand All @@ -208,6 +211,7 @@ test_ReactComponent_getADirectPropsSource
| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
test_ReactComponent_getACandidatePropsValue
| es5.js:8:13:8:19 | 'world' |
| importedComponent.jsx:4:32:4:36 | color |
| props.js:5:46:5:67 | "propFr ... tProps" |
| props.js:7:22:7:34 | "propFromJSX" |
| props.js:9:33:9:53 | "propFr ... ructor" |
Expand All @@ -222,6 +226,7 @@ test_ReactComponent
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} |
| es6.js:14:1:20:1 | class H ... }\\n} |
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} |
| namedImport.js:3:1:3:28 | class C ... nent {} |
| namedImport.js:5:1:5:20 | class D extends C {} |
| plainfn.js:1:1:3:1 | functio ... div>;\\n} |
Expand All @@ -248,6 +253,7 @@ test_ReactComponent_getAPropRead
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | name | es5.js:4:24:4:38 | this.props.name |
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | name | es5.js:20:24:20:38 | this.props.name |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | name | es6.js:3:24:3:38 | this.props.name |
| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | color | exportedComponent.jsx:2:32:2:42 | props.color |
| plainfn.js:1:1:3:1 | functio ... div>;\\n} | name | plainfn.js:2:22:2:31 | props.name |
| preact.js:1:1:7:1 | class H ... }\\n} | name | preact.js:3:9:3:18 | props.name |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | name | probably-a-component.js:3:9:3:23 | this.props.name |
Expand Down