JS: Add model of react hooks and react-router

This commit is contained in:
Asger Feldthaus
2020-10-26 15:09:53 +00:00
parent 42c03ab2fd
commit d116b424f4
12 changed files with 432 additions and 3 deletions

View File

@@ -307,7 +307,8 @@ module SourceNode {
astNode instanceof FunctionBindExpr or astNode instanceof FunctionBindExpr or
astNode instanceof DynamicImportExpr or astNode instanceof DynamicImportExpr or
astNode instanceof ImportSpecifier or astNode instanceof ImportSpecifier or
astNode instanceof ImportMetaExpr astNode instanceof ImportMetaExpr or
astNode instanceof TaggedTemplateExpr
) )
or or
DataFlow::parameterNode(this, _) DataFlow::parameterNode(this, _)

View File

@@ -327,7 +327,7 @@ module TaintTracking {
* taint to flow from `v` to any read of `c2.state.p`, where `c2` * taint to flow from `v` to any read of `c2.state.p`, where `c2`
* also is an instance of `C`. * also is an instance of `C`.
*/ */
private class ReactComponentStateTaintStep extends AdditionalTaintStep, DataFlow::ValueNode { private class ReactComponentStateTaintStep extends AdditionalTaintStep {
DataFlow::Node source; DataFlow::Node source;
ReactComponentStateTaintStep() { ReactComponentStateTaintStep() {
@@ -358,7 +358,7 @@ module TaintTracking {
* taint to flow from `v` to any read of `c2.props.p`, where `c2` * taint to flow from `v` to any read of `c2.props.p`, where `c2`
* also is an instance of `C`. * also is an instance of `C`.
*/ */
private class ReactComponentPropsTaintStep extends AdditionalTaintStep, DataFlow::ValueNode { private class ReactComponentPropsTaintStep extends AdditionalTaintStep {
DataFlow::Node source; DataFlow::Node source;
ReactComponentPropsTaintStep() { ReactComponentPropsTaintStep() {

View File

@@ -3,6 +3,8 @@
*/ */
import javascript import javascript
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
private import semmle.javascript.dataflow.internal.PreCallGraphStep
/** /**
* Gets a reference to the 'React' object. * Gets a reference to the 'React' object.
@@ -548,3 +550,228 @@ private class ReactJSXElement extends JSXElement {
*/ */
ReactComponent getComponent() { result = component } ReactComponent getComponent() { result = component }
} }
/**
* Step through the state variable of a `useState` call.
*
* It returns a pair of the current state, and a callback to change the state.
*
* For example:
* ```js
* let [state, setState] = useState(initialValue);
* let [state, setState] = useState(() => initialValue); // lazy initial state
*
* setState(newState);
* setState(prevState => { ... });
* ```
*/
private class UseStateStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call | call = react().getAMemberCall("useState") |
pred =
[call.getArgument(0), // initial state
call.getCallback(0).getReturnNode(), // lazy initial state
call.getAPropertyRead("1").getACall().getArgument(0), // setState invocation
call.getAPropertyRead("1").getACall().getCallback(0).getReturnNode()] and // setState with callback
succ = call.getAPropertyRead("0")
or
// Propagate current state into the callback argument of `setState(prevState => { ... })`
pred = call.getAPropertyRead("0") and
succ = call.getAPropertyRead("1").getACall().getCallback(0).getParameter(0)
)
}
}
/**
* A step through a React context object.
*
* For example:
* ```js
* let MyContext = React.createContext('foo');
*
* <MyContext.Provider value={pred}>
* <Foo/>
* </MyContext.Provider>
*
* function Foo() {
* let succ = useContext(MyContext);
* }
*/
private class UseContextStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode context |
pred = getAContextInput(context) and
succ = getAContextOutput(context)
)
}
}
/**
* Gets a data flow node referring to the result of the given `createContext` call.
*/
private DataFlow::SourceNode getAContextRef(DataFlow::CallNode createContext) {
createContext = react().getAMemberCall("createContext") and
result = createContext
or
// Track through imports/exports, but not full type tracking, so this can be used as a PreCallGraphStep.
exists(DataFlow::Node mid |
getAContextRef(createContext).flowsTo(mid) and
FlowSteps::propertyFlowStep(mid, result)
)
}
/**
* Gets a data flow node whose value is provided to the given context object.
*
* For example:
* ```jsx
* React.createContext(x);
* <MyContext.Provider value={x}>
* ```
*/
pragma[nomagic]
private DataFlow::Node getAContextInput(DataFlow::CallNode createContext) {
result = createContext.getArgument(0) // initial value
or
exists(JSXElement provider |
getAContextRef(createContext)
.getAPropertyRead("Provider")
.flowsTo(provider.getNameExpr().flow()) and
result = provider.getAttributeByName("value").getValue().flow()
)
}
/**
* Gets a data flow node whose value is obtained from the given context object.
*
* For example:
* ```js
* let value = useContext(MyContext);
* ```
*/
pragma[nomagic]
private DataFlow::CallNode getAContextOutput(DataFlow::CallNode createContext) {
result = react().getAMemberCall("useContext") and
getAContextRef(createContext).flowsTo(result.getArgument(0))
or
exists(DataFlow::ClassNode cls |
getAContextRef(createContext).flowsTo(cls.getAPropertyWrite("contextType").getRhs()) and
result = cls.getAReceiverNode().getAPropertyRead("context")
)
}
/**
* A step through a `useMemo` call; for example:
* ```js
* let succ = useMemo(() => pred, []);
* ```
*/
private class UseMemoStep extends PreCallGraphStep {
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
exists(DataFlow::CallNode call |
call = react().getAMemberCall("useMemo")
|
pred = call.getCallback(0).getReturnNode() and
succ = call
)
}
}
private DataFlow::SourceNode reactRouterDom() {
result = DataFlow::moduleImport("react-router-dom")
}
private class ReactRouterSource extends RemoteFlowSource {
ReactRouterSource() {
this = reactRouterDom().getAMemberCall("useParams")
or
this = reactRouterDom().getAMemberCall("useRouteMatch").getAPropertyRead(["params", "url"])
}
override string getSourceType() {
result = "react-router path parameters"
}
}
/**
* Holds if `mod` transitively depends on `react-router-dom`.
*
* We assume any React component in such a file may be used in a context where react-router
* injects the `location` property in its `props` object.
*/
private predicate dependsOnReactRouter(Module mod) {
mod.getAnImport().getImportedPath().getValue() = "react-router-dom"
or
dependsOnReactRouter(mod.getAnImportedModule())
}
/**
* A reference to the DOM location obtained through `react-router-dom`
*
* For example:
* ```js
* let location = useLocation();
*
* function MyComponent(props) {
* props.location;
* }
* export default withRouter(MyComponent);
*/
private class ReactRouterLocationSource extends DOM::LocationSource::Range {
ReactRouterLocationSource() {
this = reactRouterDom().getAMemberCall("useLocation")
or
exists(ReactComponent component |
dependsOnReactRouter(component.getTopLevel()) and
this = component.getAPropRead("location")
)
}
}
/**
* Gets a reference to a function which, if called with a React component, returns wrapped
* version of that component, which we model as a direct reference to the underlying component.
*/
private DataFlow::SourceNode higherOrderComponentBuilder() {
result = react().getAPropertyRead("memo")
or
result = DataFlow::moduleMember("react-redux", "connect").getACall()
or
result = reactRouterDom().getAPropertyRead("withRouter")
or
exists(FunctionCompositionCall compose |
higherOrderComponentBuilder().flowsTo(compose.getAnOperandNode()) and
result = compose
)
}
private class HigherOrderComponentStep extends PreCallGraphStep {
override predicate loadStep(DataFlow::Node pred, DataFlow::Node succ, string prop) {
// `lazy(() => P)` returns a proxy for the component eventually returned by
// the promise P. We model this call as simply returning the value in P.
// It is primarily used for lazy-loading of React components.
exists(DataFlow::CallNode call |
call = react().getAMemberCall("lazy") and
pred = call.getCallback(0).getReturnNode() and
succ = call and
prop = Promises::valueProp()
)
}
override predicate step(DataFlow::Node pred, DataFlow::Node succ) {
// `memo(f)` returns a function behaves as `f` but caches results
// It is sometimes used to wrap an entire functional component.
exists(DataFlow::CallNode call |
call = higherOrderComponentBuilder().getACall() and
pred = call.getArgument(0) and
succ = call
)
or
exists(TaggedTemplateExpr expr, DataFlow::CallNode call |
call = DataFlow::moduleImport("styled-components").getACall() and
pred = call.getArgument(0) and
call.flowsTo(expr.getTag().flow()) and
succ = expr.flow()
)
}
}

View File

@@ -0,0 +1,24 @@
import { memo } from 'react';
import { connect } from 'react-redux';
import { compose } from 'redux';
import styled from 'styled-components';
import unknownFunction from 'somewhere';
import { MyComponent } from './exportedComponent';
const StyledComponent = styled(MyComponent)`
color: red;
`;
function mapStateToProps(x) {
return x;
}
function mapDispatchToProps(x) {
return x;
}
const withConnect = connect(mapStateToProps, mapDispatchToProps);
const ConnectedComponent = compose(withConnect, unknownFunction)(StyledComponent);
export default memo(ConnectedComponent);

View File

@@ -227,6 +227,9 @@ test_ReactComponent_getACandidatePropsValue
| props.js:30:46:30:67 | "propFr ... tProps" | | props.js:30:46:30:67 | "propFr ... tProps" |
| props.js:32:22:32:34 | "propFromJSX" | | props.js:32:22:32:34 | "propFromJSX" |
| props.js:34:33:34:53 | "propFr ... ructor" | | props.js:34:33:34:53 | "propFr ... ructor" |
| useHigherOrderComponent.jsx:5:33:5:37 | "red" |
| useHigherOrderComponent.jsx:11:39:11:44 | "lazy" |
| useHigherOrderComponent.jsx:17:40:17:46 | "lazy2" |
test_ReactComponent test_ReactComponent
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} |
| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | | es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} |
@@ -285,6 +288,9 @@ test_JSXname
| thisAccesses.js:60:19:60:41 | <this.n ... s.name> | thisAccesses.js:60:20:60:28 | this.name | this.name | dot | | thisAccesses.js:60:19:60:41 | <this.n ... s.name> | thisAccesses.js:60:20:60:28 | this.name | this.name | dot |
| thisAccesses.js:61:19:61:41 | <this.t ... s.this> | thisAccesses.js:61:20:61:28 | this.this | this.this | dot | | thisAccesses.js:61:19:61:41 | <this.t ... s.this> | thisAccesses.js:61:20:61:28 | this.this | this.this | dot |
| thisAccesses_importedMappers.js:13:16:13:21 | <div/> | thisAccesses_importedMappers.js:13:17:13:19 | div | div | Identifier | | thisAccesses_importedMappers.js:13:16:13:21 | <div/> | thisAccesses_importedMappers.js:13:17:13:19 | div | div | Identifier |
| useHigherOrderComponent.jsx:5:12:5:39 | <SomeCo ... "red"/> | useHigherOrderComponent.jsx:5:13:5:25 | SomeComponent | SomeComponent | Identifier |
| useHigherOrderComponent.jsx:11:12:11:46 | <LazyLo ... lazy"/> | useHigherOrderComponent.jsx:11:13:11:31 | LazyLoadedComponent | LazyLoadedComponent | Identifier |
| useHigherOrderComponent.jsx:17:12:17:48 | <LazyLo ... azy2"/> | useHigherOrderComponent.jsx:17:13:17:32 | LazyLoadedComponent2 | LazyLoadedComponent2 | Identifier |
test_JSXName_this test_JSXName_this
| es5.js:4:12:4:45 | <div>He ... }</div> | es5.js:4:24:4:27 | this | | es5.js:4:12:4:45 | <div>He ... }</div> | es5.js:4:24:4:27 | this |
| es5.js:20:12:20:44 | <h1>Hel ... e}</h1> | es5.js:20:24:20:27 | this | | es5.js:20:12:20:44 | <h1>Hel ... e}</h1> | es5.js:20:24:20:27 | this |

View File

@@ -0,0 +1,18 @@
import SomeComponent from './higherOrderComponent';
import { lazy } from 'react';
function foo() {
return <SomeComponent color="red"/>
}
const LazyLoadedComponent = lazy(() => import('./higherOrderComponent'));
function bar() {
return <LazyLoadedComponent color="lazy"/>
}
const LazyLoadedComponent2 = lazy(() => import('./exportedComponent').then(m => m.MyComponent));
function barz() {
return <LazyLoadedComponent2 color="lazy2"/>
}

View File

@@ -144,6 +144,33 @@ nodes
| react-native.js:8:18:8:24 | tainted | | react-native.js:8:18:8:24 | tainted |
| react-native.js:9:27:9:33 | tainted | | react-native.js:9:27:9:33 | tainted |
| react-native.js:9:27:9:33 | tainted | | react-native.js:9:27:9:33 | tainted |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:10:4:14 | state |
| react-use-state.js:4:38:4:48 | window.name |
| react-use-state.js:4:38:4:48 | window.name |
| react-use-state.js:5:51:5:55 | state |
| react-use-state.js:5:51:5:55 | state |
| react-use-state.js:9:9:9:43 | state |
| react-use-state.js:9:10:9:14 | state |
| react-use-state.js:10:14:10:24 | window.name |
| react-use-state.js:10:14:10:24 | window.name |
| react-use-state.js:11:51:11:55 | state |
| react-use-state.js:11:51:11:55 | state |
| react-use-state.js:15:9:15:43 | state |
| react-use-state.js:15:10:15:14 | state |
| react-use-state.js:16:20:16:30 | window.name |
| react-use-state.js:16:20:16:30 | window.name |
| react-use-state.js:17:51:17:55 | state |
| react-use-state.js:17:51:17:55 | state |
| react-use-state.js:21:10:21:14 | state |
| react-use-state.js:22:14:22:17 | prev |
| react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:25:20:25:30 | window.name |
| react-use-state.js:25:20:25:30 | window.name |
| sanitiser.js:16:7:16:27 | tainted | | sanitiser.js:16:7:16:27 | tainted |
| sanitiser.js:16:17:16:27 | window.name | | sanitiser.js:16:17:16:27 | window.name |
| sanitiser.js:16:17:16:27 | window.name | | sanitiser.js:16:17:16:27 | window.name |
@@ -683,6 +710,27 @@ edges
| react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:10:4:14 | state | react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:38:4:48 | window.name | react-use-state.js:4:10:4:14 | state |
| react-use-state.js:4:38:4:48 | window.name | react-use-state.js:4:10:4:14 | state |
| react-use-state.js:9:9:9:43 | state | react-use-state.js:11:51:11:55 | state |
| react-use-state.js:9:9:9:43 | state | react-use-state.js:11:51:11:55 | state |
| react-use-state.js:9:10:9:14 | state | react-use-state.js:9:9:9:43 | state |
| react-use-state.js:10:14:10:24 | window.name | react-use-state.js:9:10:9:14 | state |
| react-use-state.js:10:14:10:24 | window.name | react-use-state.js:9:10:9:14 | state |
| react-use-state.js:15:9:15:43 | state | react-use-state.js:17:51:17:55 | state |
| react-use-state.js:15:9:15:43 | state | react-use-state.js:17:51:17:55 | state |
| react-use-state.js:15:10:15:14 | state | react-use-state.js:15:9:15:43 | state |
| react-use-state.js:16:20:16:30 | window.name | react-use-state.js:15:10:15:14 | state |
| react-use-state.js:16:20:16:30 | window.name | react-use-state.js:15:10:15:14 | state |
| react-use-state.js:21:10:21:14 | state | react-use-state.js:22:14:22:17 | prev |
| react-use-state.js:22:14:22:17 | prev | react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:22:14:22:17 | prev | react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:25:20:25:30 | window.name | react-use-state.js:21:10:21:14 | state |
| react-use-state.js:25:20:25:30 | window.name | react-use-state.js:21:10:21:14 | state |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted |
@@ -1080,6 +1128,11 @@ edges
| optionalSanitizer.js:45:18:45:56 | sanitiz ... target | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value | | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | optionalSanitizer.js:26:16:26:32 | document.location | optionalSanitizer.js:45:18:45:56 | sanitiz ... target | Cross-site scripting vulnerability due to $@. | optionalSanitizer.js:26:16:26:32 | document.location | user-provided value |
| react-native.js:8:18:8:24 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:18:8:24 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | react-native.js:8:18:8:24 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:18:8:24 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
| react-native.js:9:27:9:33 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:9:27:9:33 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value | | react-native.js:9:27:9:33 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:9:27:9:33 | tainted | Cross-site scripting vulnerability due to $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name | Cross-site scripting vulnerability due to $@. | react-use-context.js:10:22:10:32 | window.name | user-provided value |
| react-use-state.js:5:51:5:55 | state | react-use-state.js:4:38:4:48 | window.name | react-use-state.js:5:51:5:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:4:38:4:48 | window.name | user-provided value |
| react-use-state.js:11:51:11:55 | state | react-use-state.js:10:14:10:24 | window.name | react-use-state.js:11:51:11:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:10:14:10:24 | window.name | user-provided value |
| react-use-state.js:17:51:17:55 | state | react-use-state.js:16:20:16:30 | window.name | react-use-state.js:17:51:17:55 | state | Cross-site scripting vulnerability due to $@. | react-use-state.js:16:20:16:30 | window.name | user-provided value |
| react-use-state.js:23:35:23:38 | prev | react-use-state.js:25:20:25:30 | window.name | react-use-state.js:23:35:23:38 | prev | Cross-site scripting vulnerability due to $@. | react-use-state.js:25:20:25:30 | window.name | user-provided value |
| sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value | | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:23:21:23:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
| sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value | | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:30:21:30:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |
| sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value | | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | sanitiser.js:16:17:16:27 | window.name | sanitiser.js:33:21:33:44 | '<b>' + ... '</b>' | Cross-site scripting vulnerability due to $@. | sanitiser.js:16:17:16:27 | window.name | user-provided value |

View File

@@ -144,6 +144,33 @@ nodes
| react-native.js:8:18:8:24 | tainted | | react-native.js:8:18:8:24 | tainted |
| react-native.js:9:27:9:33 | tainted | | react-native.js:9:27:9:33 | tainted |
| react-native.js:9:27:9:33 | tainted | | react-native.js:9:27:9:33 | tainted |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-context.js:10:22:10:32 | window.name |
| react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:10:4:14 | state |
| react-use-state.js:4:38:4:48 | window.name |
| react-use-state.js:4:38:4:48 | window.name |
| react-use-state.js:5:51:5:55 | state |
| react-use-state.js:5:51:5:55 | state |
| react-use-state.js:9:9:9:43 | state |
| react-use-state.js:9:10:9:14 | state |
| react-use-state.js:10:14:10:24 | window.name |
| react-use-state.js:10:14:10:24 | window.name |
| react-use-state.js:11:51:11:55 | state |
| react-use-state.js:11:51:11:55 | state |
| react-use-state.js:15:9:15:43 | state |
| react-use-state.js:15:10:15:14 | state |
| react-use-state.js:16:20:16:30 | window.name |
| react-use-state.js:16:20:16:30 | window.name |
| react-use-state.js:17:51:17:55 | state |
| react-use-state.js:17:51:17:55 | state |
| react-use-state.js:21:10:21:14 | state |
| react-use-state.js:22:14:22:17 | prev |
| react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:25:20:25:30 | window.name |
| react-use-state.js:25:20:25:30 | window.name |
| sanitiser.js:16:7:16:27 | tainted | | sanitiser.js:16:7:16:27 | tainted |
| sanitiser.js:16:17:16:27 | window.name | | sanitiser.js:16:17:16:27 | window.name |
| sanitiser.js:16:17:16:27 | window.name | | sanitiser.js:16:17:16:27 | window.name |
@@ -687,6 +714,27 @@ edges
| react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted | | react-native.js:7:7:7:33 | tainted | react-native.js:9:27:9:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | | react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted |
| react-use-context.js:10:22:10:32 | window.name | react-use-context.js:10:22:10:32 | window.name |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:9:4:49 | state | react-use-state.js:5:51:5:55 | state |
| react-use-state.js:4:10:4:14 | state | react-use-state.js:4:9:4:49 | state |
| react-use-state.js:4:38:4:48 | window.name | react-use-state.js:4:10:4:14 | state |
| react-use-state.js:4:38:4:48 | window.name | react-use-state.js:4:10:4:14 | state |
| react-use-state.js:9:9:9:43 | state | react-use-state.js:11:51:11:55 | state |
| react-use-state.js:9:9:9:43 | state | react-use-state.js:11:51:11:55 | state |
| react-use-state.js:9:10:9:14 | state | react-use-state.js:9:9:9:43 | state |
| react-use-state.js:10:14:10:24 | window.name | react-use-state.js:9:10:9:14 | state |
| react-use-state.js:10:14:10:24 | window.name | react-use-state.js:9:10:9:14 | state |
| react-use-state.js:15:9:15:43 | state | react-use-state.js:17:51:17:55 | state |
| react-use-state.js:15:9:15:43 | state | react-use-state.js:17:51:17:55 | state |
| react-use-state.js:15:10:15:14 | state | react-use-state.js:15:9:15:43 | state |
| react-use-state.js:16:20:16:30 | window.name | react-use-state.js:15:10:15:14 | state |
| react-use-state.js:16:20:16:30 | window.name | react-use-state.js:15:10:15:14 | state |
| react-use-state.js:21:10:21:14 | state | react-use-state.js:22:14:22:17 | prev |
| react-use-state.js:22:14:22:17 | prev | react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:22:14:22:17 | prev | react-use-state.js:23:35:23:38 | prev |
| react-use-state.js:25:20:25:30 | window.name | react-use-state.js:21:10:21:14 | state |
| react-use-state.js:25:20:25:30 | window.name | react-use-state.js:21:10:21:14 | state |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:23:29:23:35 | tainted |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:30:29:30:35 | tainted |
| sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted | | sanitiser.js:16:7:16:27 | tainted | sanitiser.js:33:29:33:35 | tainted |

View File

@@ -0,0 +1,3 @@
import { createContext } from 'react';
export let MyContext = createContext({root: null});

View File

@@ -0,0 +1,5 @@
import { MyContext } from './react-create-context';
export function renderMain() {
return <MyContext.Provider value={{root: document.body}}></MyContext.Provider>
}

View File

@@ -0,0 +1,11 @@
import { useContext } from 'react';
import { MyContext } from './react-create-context';
function useMyContext() {
return useContext(MyContext);
}
export function useDoc1() {
let { root } = useMyContext();
root.appendChild(window.name); // NOT OK
}

View File

@@ -0,0 +1,33 @@
import { useState } from 'react';
function initialState() {
let [state, setState] = useState(window.name);
return <div dangerouslySetInnerHTML={{__html: state}}></div>; // NOT OK
}
function setStateValue() {
let [state, setState] = useState('foo');
setState(window.name);
return <div dangerouslySetInnerHTML={{__html: state}}></div>; // NOT OK
}
function setStateValueLazy() {
let [state, setState] = useState('foo');
setState(() => window.name);
return <div dangerouslySetInnerHTML={{__html: state}}></div>; // NOT OK
}
function setStateValueLazy() {
let [state, setState] = useState('foo');
setState(prev => {
document.body.innerHTML = prev; // NOT OK
})
setState(() => window.name);
}
function setStateValueSafe() {
let [state, setState] = useState('foo');
setState('safe');
setState(() => 'also safe');
return <div dangerouslySetInnerHTML={{__html: state}}></div>; // OK
}