diff --git a/javascript/ql/lib/ext/react.model.yml b/javascript/ql/lib/ext/react.model.yml
new file mode 100644
index 000000000000..51b6aaefc46d
--- /dev/null
+++ b/javascript/ql/lib/ext/react.model.yml
@@ -0,0 +1,6 @@
+extensions:
+ - addsTo:
+ pack: codeql/javascript-all
+ extensible: summaryModel
+ data:
+ - ["react", "Member[use]", "Argument[0].Awaited", "ReturnValue", "value"]
diff --git a/javascript/ql/lib/semmle/javascript/frameworks/React.qll b/javascript/ql/lib/semmle/javascript/frameworks/React.qll
index 4d126b888290..3a361e705940 100644
--- a/javascript/ql/lib/semmle/javascript/frameworks/React.qll
+++ b/javascript/ql/lib/semmle/javascript/frameworks/React.qll
@@ -875,3 +875,22 @@ private class ReactPropAsViewComponentInput extends ViewComponentInput {
override string getSourceType() { result = "React props" }
}
+
+private predicate isServerFunction(DataFlow::FunctionNode func) {
+ exists(Directive::UseServerDirective useServer |
+ useServer.getContainer() = func.getFunction()
+ or
+ useServer.getContainer().(Module).getAnExportedValue(_).getAFunctionValue() = func
+ )
+}
+
+private class ServerFunctionRemoteFlowSource extends RemoteFlowSource {
+ ServerFunctionRemoteFlowSource() {
+ exists(DataFlow::FunctionNode func |
+ isServerFunction(func) and
+ this = func.getAParameter()
+ )
+ }
+
+ override string getSourceType() { result = "React server function parameter" }
+}
diff --git a/javascript/ql/src/change-notes/2025-06-23-react-use-server.md b/javascript/ql/src/change-notes/2025-06-23-react-use-server.md
new file mode 100644
index 000000000000..b3d3088b640e
--- /dev/null
+++ b/javascript/ql/src/change-notes/2025-06-23-react-use-server.md
@@ -0,0 +1,5 @@
+---
+category: majorAnalysis
+---
+* Taint is now tracked through the React `use` function.
+* Parameters of React server functions, marked with the `"use server"` directive, are now seen as taint sources.
diff --git a/javascript/ql/test/library-tests/TripleDot/react-use.js b/javascript/ql/test/library-tests/TripleDot/react-use.js
new file mode 100644
index 000000000000..1691a7fbea46
--- /dev/null
+++ b/javascript/ql/test/library-tests/TripleDot/react-use.js
@@ -0,0 +1,12 @@
+import { use } from "react";
+
+async function fetchData() {
+ return new Promise((resolve) => {
+ resolve(source("fetchedData"));
+ });
+}
+
+function Component() {
+ const data = use(fetchData());
+ sink(data); // $ hasValueFlow=fetchedData
+}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent.qll
deleted file mode 100644
index a48c6b8f1c94..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent.qll
+++ /dev/null
@@ -1,3 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent(ReactComponent c) { any() }
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidatePropsValue.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidatePropsValue.qll
deleted file mode 100644
index a551aa457eca..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidatePropsValue.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import javascript
-
-query predicate test_ReactComponent_getACandidatePropsValue(DataFlow::Node res) {
- exists(ReactComponent c | res = c.getACandidatePropsValue(_))
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidateStateSource.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidateStateSource.qll
deleted file mode 100644
index af6597ab96cf..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getACandidateStateSource.qll
+++ /dev/null
@@ -1,7 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_getACandidateStateSource(
- ReactComponent c, DataFlow::SourceNode res
-) {
- res = c.getACandidateStateSource()
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getADirectPropsSource.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getADirectPropsSource.qll
deleted file mode 100644
index 9f9ebd89f8de..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getADirectPropsSource.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_getADirectPropsSource(ReactComponent c, DataFlow::SourceNode res) {
- res = c.getADirectPropsAccess()
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPreviousStateSource.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPreviousStateSource.qll
deleted file mode 100644
index b1bfe312dae2..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPreviousStateSource.qll
+++ /dev/null
@@ -1,7 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_getAPreviousStateSource(
- ReactComponent c, DataFlow::SourceNode res
-) {
- res = c.getAPreviousStateSource()
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPropRead.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPropRead.qll
deleted file mode 100644
index 0ff2a588a029..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getAPropRead.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_getAPropRead(ReactComponent c, string n, DataFlow::PropRead res) {
- res = c.getAPropRead(n)
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getInstanceMethod.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getInstanceMethod.qll
deleted file mode 100644
index b813e19539b5..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_getInstanceMethod.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_getInstanceMethod(ReactComponent c, string n, Function res) {
- res = c.getInstanceMethod(n)
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_ref.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_ref.qll
deleted file mode 100644
index a017a0715feb..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactComponent_ref.qll
+++ /dev/null
@@ -1,3 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_ReactComponent_ref(ReactComponent c, DataFlow::Node res) { res = c.ref() }
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactName.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/ReactName.qll
deleted file mode 100644
index 885f1f38a57f..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/ReactName.qll
+++ /dev/null
@@ -1,17 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_JSXname(JsxElement element, JsxName jsxname, string name, string type) {
- name = jsxname.getValue() and
- (
- jsxname instanceof Identifier and type = "Identifier"
- or
- jsxname instanceof ThisExpr and type = "thisExpr"
- or
- jsxname.(DotExpr).getBase() instanceof JsxName and type = "dot"
- or
- jsxname instanceof JsxQualifiedName and type = "qualifiedName"
- ) and
- element.getNameExpr() = jsxname
-}
-
-query ThisExpr test_JsxName_this(JsxElement element) { result.getParentExpr+() = element }
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/es5.js b/javascript/ql/test/library-tests/frameworks/ReactJS/es5.js
index abef8d7d8bbd..8d1eb47b55f6 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/es5.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/es5.js
@@ -1,14 +1,14 @@
var Hello = React.createClass({
displayName: 'Hello',
render: function() {
- return
Hello {this.props.name}
;
+ return Hello {this.props.name}
; // $ threatModelSource=view-component-input
},
getDefaultProps: function() {
return {
- name: 'world'
+ name: 'world' // $ getACandidatePropsValue
};
}
-});
+}); // $ reactComponent
Hello.info = function() {
return "Nothing to see here.";
@@ -17,6 +17,6 @@ Hello.info = function() {
var createReactClass = require('create-react-class');
var Greeting = createReactClass({
render: function() {
- return Hello, {this.props.name}
;
+ return Hello, {this.props.name}
; // $ threatModelSource=view-component-input
}
-});
+}); // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/es6.js b/javascript/ql/test/library-tests/frameworks/ReactJS/es6.js
index 2991888354c8..333ac1a943f5 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/es6.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/es6.js
@@ -1,11 +1,11 @@
-class Hello extends React.Component {
+class Hello extends React.Component { // $ threatModelSource=view-component-input
render() {
- return Hello {this.props.name}
;
+ return Hello {this.props.name}
; // $ threatModelSource=view-component-input
}
static info() {
return "Nothing to see here.";
}
-}
+} // $ reactComponent
Hello.displayName = 'Hello';
Hello.defaultProps = {
name: 'world'
@@ -17,4 +17,4 @@ class Hello2 extends React.Component {
this.state.bar.foo = 42;
this.state = { baz: 42};
}
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/exportedComponent.jsx b/javascript/ql/test/library-tests/frameworks/ReactJS/exportedComponent.jsx
index 4335b4bc3081..9e2d5580228f 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/exportedComponent.jsx
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/exportedComponent.jsx
@@ -1,3 +1,3 @@
-export function MyComponent(props) {
+export function MyComponent(props) { // $ threatModelSource=view-component-input
return
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/getADirectStateAccess.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/getADirectStateAccess.qll
deleted file mode 100644
index b43f3e487d77..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/getADirectStateAccess.qll
+++ /dev/null
@@ -1,5 +0,0 @@
-import semmle.javascript.frameworks.React
-
-query predicate test_getADirectStateAccess(ReactComponent c, DataFlow::SourceNode res) {
- res = c.getADirectStateAccess()
-}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/importedComponent.jsx b/javascript/ql/test/library-tests/frameworks/ReactJS/importedComponent.jsx
index d94acf59abee..ed04d4bec889 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/importedComponent.jsx
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/importedComponent.jsx
@@ -1,5 +1,5 @@
import { MyComponent } from "./exportedComponent";
-export function render({color, location}) {
- return
-}
+export function render({color, location}) { // $ threatModelSource=view-component-input locationSource threatModelSource=remote
+ return // $ getACandidatePropsValue
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/namedImport.js b/javascript/ql/test/library-tests/frameworks/ReactJS/namedImport.js
index 3c5a7182d65e..c29160c8ed5b 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/namedImport.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/namedImport.js
@@ -1,5 +1,5 @@
import { Component } from "react";
-class C extends Component {}
+class C extends Component {} // $ threatModelSource=view-component-input reactComponent
-class D extends C {}
+class D extends C {} // $ threatModelSource=view-component-input reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/plainfn.js b/javascript/ql/test/library-tests/frameworks/ReactJS/plainfn.js
index 7f5995b9fdb1..c5d029a44d13 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/plainfn.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/plainfn.js
@@ -1,15 +1,15 @@
-function Hello(props) {
+function Hello(props) { // $ threatModelSource=view-component-input
return Hello {props.name}
;
-}
+} // $ reactComponent
-function Hello2(props) {
+function Hello2(props) { // $ threatModelSource=view-component-input
return React.createElement("div");
-}
+} // $ reactComponent
-function Hello3(props) {
+function Hello3(props) { // $ threatModelSource=view-component-input
var x = React.createElement("div");
return x;
-}
+} // $ reactComponent
function NotAComponent(props) {
if (y)
@@ -17,8 +17,8 @@ function NotAComponent(props) {
return g();
}
-function SpuriousComponent(props) {
+function SpuriousComponent(props) { // $ threatModelSource=view-component-input
if (y)
return React.createElement("div");
return 42;
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/preact.js b/javascript/ql/test/library-tests/frameworks/ReactJS/preact.js
index 787064397f0d..ced8ae6be303 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/preact.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/preact.js
@@ -1,11 +1,11 @@
-class Hello extends Preact.Component {
- render(props, state) {
+class Hello extends Preact.Component { // $ threatModelSource=view-component-input
+ render(props, state) { // $ threatModelSource=view-component-input
props.name;
state.name;
return ;
}
-}
+} // $ reactComponent
-class Hello extends preact.Component {
+class Hello extends preact.Component { // $ threatModelSource=view-component-input
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/probably-a-component.js b/javascript/ql/test/library-tests/frameworks/ReactJS/probably-a-component.js
index a8205039b8e7..c82188beb02a 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/probably-a-component.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/probably-a-component.js
@@ -1,6 +1,6 @@
-class Hello extends Component {
+class Hello extends Component { // $ threatModelSource=view-component-input
render() {
- this.props.name;
+ this.props.name; // $ threatModelSource=view-component-input
return ;
}
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/props.js b/javascript/ql/test/library-tests/frameworks/ReactJS/props.js
index c1cce38a040c..153ee1473428 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/props.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/props.js
@@ -1,36 +1,36 @@
function ES2015() {
- class C extends React.Component {
- }
+ class C extends React.Component { // $ threatModelSource=view-component-input
+ } // $ reactComponent
- C.defaultProps = { propFromDefaultProps: "propFromDefaultProps" };
+ C.defaultProps = { propFromDefaultProps: "propFromDefaultProps" }; // $ getACandidatePropsValue
- ();
+ (); // $ getACandidatePropsValue
- new C({propFromConstructor: "propFromConstructor"});
+ new C({propFromConstructor: "propFromConstructor"}); // $ getACandidatePropsValue
}
function ES5() {
var C = React.createClass({
getDefaultProps() {
- return { propFromDefaultProps: "propFromDefaultProps" };
+ return { propFromDefaultProps: "propFromDefaultProps" }; // $ getACandidatePropsValue
}
- });
+ }); // $ reactComponent
- ();
+ (); // $ getACandidatePropsValue
- C({propFromConstructor: "propFromConstructor"});
+ C({propFromConstructor: "propFromConstructor"}); // $ getACandidatePropsValue
}
function Functional() {
- function C(props) {
+ function C(props) { // $ threatModelSource=view-component-input
return ;
- }
+ } // $ reactComponent
- C.defaultProps = { propFromDefaultProps: "propFromDefaultProps" };
+ C.defaultProps = { propFromDefaultProps: "propFromDefaultProps" }; // $ getACandidatePropsValue
- ();
+ (); // $ getACandidatePropsValue
- new C({propFromConstructor: "propFromConstructor"});
+ new C({propFromConstructor: "propFromConstructor"}); // $ getACandidatePropsValue
}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/rare-lifecycle-methods.js b/javascript/ql/test/library-tests/frameworks/ReactJS/rare-lifecycle-methods.js
index c3f7c13b1623..b4943ea66c30 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/rare-lifecycle-methods.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/rare-lifecycle-methods.js
@@ -1,4 +1,4 @@
-class C extends React.Component {
+class C extends React.Component { // $ threatModelSource=view-component-input
static getDerivedStateFromProps(props, state) {
return {};
}
@@ -8,4 +8,4 @@ class C extends React.Component {
getSnapshotBeforeUpdate(prevProps, prevState) {
return {};
}
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/react.qll b/javascript/ql/test/library-tests/frameworks/ReactJS/react.qll
deleted file mode 100644
index a5f254b79707..000000000000
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/react.qll
+++ /dev/null
@@ -1,3 +0,0 @@
-import javascript
-
-query predicate test_react(DataFlow::ValueNode nd) { react().flowsTo(nd) }
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyReads.js b/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyReads.js
index 697bc35c1505..93a120937d76 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyReads.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyReads.js
@@ -10,4 +10,4 @@ class Reads extends React.Component {
componentDidUpdate(prevProps, prevState) {
prevState.p4;
}
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyWrites.js b/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyWrites.js
index 692400c7381a..27e02bc6f665 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyWrites.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/statePropertyWrites.js
@@ -31,15 +31,15 @@ class Writes extends React.Component {
state = {
p7: 42
};
-}
+} // $ reactComponent
React.createClass({
render: function() {
- return Hello {this.props.name}
;
+ return Hello {this.props.name}
; // $ threatModelSource=view-component-input
},
getInitialState: function() {
return {
p8: 42
};
}
-});
+}); // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected
index 9a5c38ddbf90..9b453989bb80 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.expected
@@ -1,42 +1,112 @@
-test_react
-| es5.js:1:13:1:17 | React |
-| es6.js:1:21:1:25 | React |
-| es6.js:14:22:14:26 | React |
-| globalReactRefs.js:1:1:1:5 | React |
-| globalReactRefs.js:4:5:4:9 | React |
-| globalReactRefs.js:7:1:7:5 | React |
-| importedReactRefs.js:1:8:1:12 | React |
-| importedReactRefs.js:3:1:3:5 | React |
-| importedReactRefs.js:6:5:6:9 | React |
-| importedReactRefs.js:9:1:9:5 | React |
-| plainfn.js:6:12:6:16 | React |
-| plainfn.js:10:13:10:17 | React |
-| plainfn.js:16:16:16:20 | React |
-| plainfn.js:22:16:22:20 | React |
-| props.js:2:21:2:25 | React |
-| props.js:13:13:13:17 | React |
-| rare-lifecycle-methods.js:1:17:1:21 | React |
-| requiredReactRefs.js:1:13:1:28 | require("react") |
-| requiredReactRefs.js:3:1:3:5 | React |
-| requiredReactRefs.js:6:5:6:9 | React |
-| requiredReactRefs.js:9:1:9:5 | React |
-| requiredReactRefs.js:12:17:12:32 | require("react") |
-| requiredReactRefs.js:14:5:14:9 | React |
-| requiredReactRefs.js:17:9:17:13 | React |
-| requiredReactRefs.js:20:5:20:9 | React |
-| statePropertyReads.js:1:21:1:25 | React |
-| statePropertyWrites.js:1:22:1:26 | React |
-| statePropertyWrites.js:36:1:36:5 | React |
-| thisAccesses.js:1:17:1:21 | React |
-| thisAccesses.js:18:1:18:5 | React |
-| thisAccesses.js:38:1:38:5 | React |
-| thisAccesses.js:40:9:40:13 | React |
-| thisAccesses.js:47:18:47:22 | React |
-| thisAccesses.js:54:18:54:22 | React |
-| thisAccesses_importedMappers.js:1:8:1:12 | React |
-| thisAccesses_importedMappers.js:4:1:4:5 | React |
-| thisAccesses_importedMappers.js:6:9:6:13 | React |
-test_JSXname
+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" |
+| props.js:15:44:15:65 | "propFr ... tProps" |
+| props.js:19:22:19:34 | "propFromJSX" |
+| props.js:21:29:21:49 | "propFr ... ructor" |
+| props.js:30:46:30:67 | "propFr ... tProps" |
+| props.js:32:22:32:34 | "propFromJSX" |
+| 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" |
+getACandidateStateSource
+| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:22:18:31 | { baz: 42} |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:3:16:3:17 | {} |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:5:38:5:46 | nextState |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:45:7:56 | prevState.p3 |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:8:18:8:19 | {} |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:12:18:12:19 | {} |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:16:18:16:19 | {} |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:20:18:20:19 | {} |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:31:13:33:5 | {\\n ... 2\\n } |
+| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:41:12:43:5 | {\\n p8: 42\\n } |
+| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
+| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:22:49:22 | x |
+getADirectPropsSource
+| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:4:24:4:33 | this.props |
+| es5.js:18:33:22:1 | {\\n ren ... t\\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 |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | importedComponent.jsx:3:24:3:40 | {color, location} |
+| 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 |
+| plainfn.js:5:1:7:1 | functio ... iv");\\n} | plainfn.js:5:17:5:21 | props |
+| plainfn.js:9:1:12:1 | functio ... rn x;\\n} | plainfn.js:9:17:9:21 | props |
+| plainfn.js:20:1:24:1 | functio ... n 42;\\n} | plainfn.js:20:28:20:32 | props |
+| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | args |
+| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:12:2:16 | props |
+| preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | args |
+| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | args |
+| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:18 | this.props |
+| props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | args |
+| props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:16:26:20 | props |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | args |
+| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:38:24:38:33 | this.props |
+| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:31:12:31:16 | props |
+| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
+getADirectStateAccess
+| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:18 | this.state |
+| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:18 | this.state |
+| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:18 | this.state |
+| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:19:2:23 | state |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:3:9:3:18 | this.state |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:5:9:5:18 | this.state |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:4:9:4:17 | cmp.state |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:6:9:6:17 | cmp.state |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:10:9:10:17 | cmp.state |
+| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:9:49:18 | this.state |
+| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:50:9:50:18 | this.state |
+getAPreviousStateSource
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:2:44:2:48 | state |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:8:40:8:48 | prevState |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:24:7:32 | prevState |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:10:35:10:43 | prevState |
+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 ... t\\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 |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | color | importedComponent.jsx:3:25:3:29 | color |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | location | importedComponent.jsx:3:32:3:39 | location |
+| 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 |
+| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | name | statePropertyWrites.js:38:24:38:38 | this.props.name |
+getInstanceMethod
+| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | getDefaultProps | es5.js:6:20:10:3 | functio ... };\\n } |
+| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | render | es5.js:3:11:5:3 | functio ... put\\n } |
+| es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | render | es5.js:19:11:21:3 | functio ... put\\n } |
+| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | render | es6.js:2:9:4:3 | () {\\n ... put\\n } |
+| exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} | render | exportedComponent.jsx:1:8:3:1 | functio ... r}}/>\\n} |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | render | importedComponent.jsx:3:8:5:1 | functio ... Value\\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} |
+| plainfn.js:20:1:24:1 | functio ... n 42;\\n} | render | plainfn.js:20:1:24:1 | functio ... n 42;\\n} |
+| preact.js:1:1:7:1 | class H ... }\\n} | render | preact.js:2:11:6:5 | (props, ... ;\\n } |
+| probably-a-component.js:1:1:6:1 | class H ... }\\n} | render | probably-a-component.js:2:11:5:5 | () {\\n ... ;\\n } |
+| props.js:13:31:17:5 | {\\n ... }\\n } | getDefaultProps | props.js:14:24:16:9 | () {\\n ... } |
+| props.js:26:5:28:5 | functio ... ;\\n } | render | props.js:26:5:28:5 | functio ... ;\\n } |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | getSnapshotBeforeUpdate | rare-lifecycle-methods.js:8:28:10:5 | (prevPr ... ;\\n } |
+| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | shouldComponentUpdate | rare-lifecycle-methods.js:5:26:7:5 | (nextPr ... ;\\n } |
+| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | componentDidUpdate | statePropertyReads.js:10:23:12:5 | (prevPr ... ;\\n } |
+| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | getInitialState | statePropertyWrites.js:25:20:29:5 | () { // ... ;\\n } |
+| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | getInitialState | statePropertyWrites.js:40:20:44:3 | functio ... };\\n } |
+| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | render | statePropertyWrites.js:37:11:39:3 | functio ... put\\n } |
+| thisAccesses.js:1:1:16:1 | class C ... }\\n} | someInstanceMethod | thisAccesses.js:13:23:15:5 | () {\\n ... ;\\n } |
+| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | render | thisAccesses.js:19:13:24:5 | functio ... ;\\n } |
+| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | someInstanceMethod | thisAccesses.js:26:25:28:5 | functio ... ;\\n } |
+| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | render | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} |
+| thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | render | thisAccesses.js:39:13:44:5 | functio ... ;\\n } |
+| thisAccesses.js:54:1:63:1 | class C ... }\\n} | render | thisAccesses.js:59:11:62:5 | () {\\n ... ;\\n } |
+| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | render | thisAccesses_importedMappers.js:5:13:14:5 | functio ... ;\\n } |
+jsxName
| es5.js:4:12:4:45 | He ... }
| es5.js:4:13:4:15 | div | div | Identifier |
| es5.js:20:12:20:44 | Hel ... e}
| es5.js:20:13:20:14 | h1 | h1 | Identifier |
| es6.js:3:12:3:45 | He ... }
| es6.js:3:13:3:15 | div | div | Identifier |
@@ -62,13 +132,22 @@ test_JSXname
| useHigherOrderComponent.jsx:5:12:5:39 | | useHigherOrderComponent.jsx:5:13:5:25 | SomeComponent | SomeComponent | Identifier |
| useHigherOrderComponent.jsx:11:12:11:46 | | useHigherOrderComponent.jsx:11:13:11:31 | LazyLoadedComponent | LazyLoadedComponent | Identifier |
| useHigherOrderComponent.jsx:17:12:17:48 | | useHigherOrderComponent.jsx:17:13:17:32 | LazyLoadedComponent2 | LazyLoadedComponent2 | Identifier |
-test_ReactComponent
+jsxNameThis
+| es5.js:4:12:4:45 | He ... }
| es5.js:4:24:4:27 | this |
+| es5.js:20:12:20:44 | Hel ... e}
| es5.js:20:24:20:27 | this |
+| es6.js:3:12:3:45 | He ... }
| es6.js:3:24:3:27 | this |
+| statePropertyWrites.js:38:12:38:45 | He ... }
| statePropertyWrites.js:38:24:38:27 | this |
+| thisAccesses.js:60:19:60:41 | | thisAccesses.js:60:20:60:23 | this |
+| thisAccesses.js:61:19:61:41 | | thisAccesses.js:61:20:61:23 | this |
+locationSource
+| importedComponent.jsx:3:32:3:39 | location |
+reactComponent
| 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 ... t\\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} |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\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} |
@@ -76,9 +155,9 @@ test_ReactComponent
| plainfn.js:9:1:12:1 | functio ... rn x;\\n} |
| plainfn.js:20:1:24:1 | functio ... n 42;\\n} |
| preact.js:1:1:7:1 | class H ... }\\n} |
-| preact.js:9:1:11:1 | class H ... nt {\\n\\n} |
+| preact.js:9:1:11:1 | class H ... nput\\n\\n} |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} |
-| props.js:2:5:3:5 | class C ... {\\n } |
+| props.js:2:5:3:5 | class C ... t\\n } |
| props.js:13:31:17:5 | {\\n ... }\\n } |
| props.js:26:5:28:5 | functio ... ;\\n } |
| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} |
@@ -92,14 +171,14 @@ test_ReactComponent
| thisAccesses.js:47:1:52:1 | class C ... }\\n} |
| thisAccesses.js:54:1:63:1 | class C ... }\\n} |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} |
-test_ReactComponent_ref
+reactComponentRef
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} |
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:3:11:3:10 | this |
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:4:24:4:27 | this |
| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:6:20:6:19 | this |
-| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} |
-| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | es5.js:19:11:19:10 | this |
-| es5.js:18:33:22:1 | {\\n ren ... ;\\n }\\n} | es5.js:20:24:20:27 | this |
+| es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} |
+| es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:19:11:19:10 | this |
+| es5.js:18:33:22:1 | {\\n ren ... t\\n }\\n} | es5.js:20:24:20:27 | this |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | implicit 'this' |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:1:37:1:36 | this |
| es6.js:1:1:8:1 | class H ... ;\\n }\\n} | es6.js:2:9:2:8 | this |
@@ -110,7 +189,7 @@ test_ReactComponent_ref
| 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 |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} | importedComponent.jsx:3:8:3:7 | this |
+| importedComponent.jsx:3:8:5:1 | functio ... Value\\n} | importedComponent.jsx:3:8:3:7 | this |
| namedImport.js:3:1:3:28 | class C ... nent {} | namedImport.js:3:27:3:26 | implicit '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 | implicit 'this' |
@@ -122,15 +201,15 @@ test_ReactComponent_ref
| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | implicit 'this' |
| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | this |
| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:11:2:10 | this |
-| preact.js:9:1:11:1 | class H ... nt {\\n\\n} | preact.js:9:38:9:37 | implicit 'this' |
-| preact.js:9:1:11:1 | class H ... nt {\\n\\n} | preact.js:9:38:9:37 | this |
+| preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | implicit 'this' |
+| preact.js:9:1:11:1 | class H ... nput\\n\\n} | preact.js:9:38:9:37 | this |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | implicit 'this' |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | this |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:2:11:2:10 | this |
| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:12 | this |
-| props.js:2:5:3:5 | class C ... {\\n } | props.js:2:37:2:36 | implicit 'this' |
-| props.js:2:5:3:5 | class C ... {\\n } | props.js:2:37:2:36 | this |
-| props.js:2:5:3:5 | class C ... {\\n } | props.js:9:5:9:55 | new C({ ... ctor"}) |
+| props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | implicit 'this' |
+| props.js:2:5:3:5 | class C ... t\\n } | props.js:2:37:2:36 | this |
+| props.js:2:5:3:5 | class C ... t\\n } | props.js:9:5:9:55 | new C({ ... ctor"}) |
| props.js:13:31:17:5 | {\\n ... }\\n } | props.js:13:31:17:5 | {\\n ... }\\n } |
| props.js:13:31:17:5 | {\\n ... }\\n } | props.js:14:24:14:23 | this |
| props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:5:26:4 | this |
@@ -201,123 +280,6 @@ test_ReactComponent_ref
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:9:25:9:24 | this |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:10:13:10:16 | this |
| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | thisAccesses_importedMappers.js:11:12:11:15 | this |
-test_getADirectStateAccess
-| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:16:9:16:18 | this.state |
-| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:17:9:17:18 | this.state |
-| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:9:18:18 | this.state |
-| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:19:2:23 | state |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:3:9:3:18 | this.state |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:5:9:5:18 | this.state |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:4:9:4:17 | cmp.state |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:6:9:6:17 | cmp.state |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:10:9:10:17 | cmp.state |
-| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:9:49:18 | this.state |
-| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:50:9:50:18 | this.state |
-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 |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} | color | importedComponent.jsx:3:25:3:29 | color |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} | location | importedComponent.jsx:3:32:3:39 | location |
-| 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 |
-| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | name | statePropertyWrites.js:38:24:38:38 | this.props.name |
-test_ReactComponent_getInstanceMethod
-| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | getDefaultProps | es5.js:6:20:10:3 | functio ... };\\n } |
-| 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} |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} | render | importedComponent.jsx:3:8:5:1 | functio ... or}/>\\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} |
-| plainfn.js:20:1:24:1 | functio ... n 42;\\n} | render | plainfn.js:20:1:24:1 | functio ... n 42;\\n} |
-| preact.js:1:1:7:1 | class H ... }\\n} | render | preact.js:2:11:6:5 | (props, ... ;\\n } |
-| probably-a-component.js:1:1:6:1 | class H ... }\\n} | render | probably-a-component.js:2:11:5:5 | () {\\n ... ;\\n } |
-| props.js:13:31:17:5 | {\\n ... }\\n } | getDefaultProps | props.js:14:24:16:9 | () {\\n ... } |
-| props.js:26:5:28:5 | functio ... ;\\n } | render | props.js:26:5:28:5 | functio ... ;\\n } |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | getSnapshotBeforeUpdate | rare-lifecycle-methods.js:8:28:10:5 | (prevPr ... ;\\n } |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | shouldComponentUpdate | rare-lifecycle-methods.js:5:26:7:5 | (nextPr ... ;\\n } |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | componentDidUpdate | statePropertyReads.js:10:23:12:5 | (prevPr ... ;\\n } |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | getInitialState | statePropertyWrites.js:25:20:29:5 | () { // ... ;\\n } |
-| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | getInitialState | statePropertyWrites.js:40:20:44:3 | functio ... };\\n } |
-| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | render | statePropertyWrites.js:37:11:39:3 | functio ... v>;\\n } |
-| thisAccesses.js:1:1:16:1 | class C ... }\\n} | someInstanceMethod | thisAccesses.js:13:23:15:5 | () {\\n ... ;\\n } |
-| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | render | thisAccesses.js:19:13:24:5 | functio ... ;\\n } |
-| thisAccesses.js:18:19:29:1 | {\\n r ... }\\n} | someInstanceMethod | thisAccesses.js:26:25:28:5 | functio ... ;\\n } |
-| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | render | thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} |
-| thisAccesses.js:38:19:45:1 | {\\n r ... },\\n} | render | thisAccesses.js:39:13:44:5 | functio ... ;\\n } |
-| thisAccesses.js:54:1:63:1 | class C ... }\\n} | render | thisAccesses.js:59:11:62:5 | () {\\n ... ;\\n } |
-| thisAccesses_importedMappers.js:4:19:15:1 | {\\n r ... },\\n} | render | thisAccesses_importedMappers.js:5:13:14:5 | functio ... ;\\n } |
-test_ReactComponent_getADirectPropsSource
-| es5.js:1:31:11:1 | {\\n dis ... ;\\n }\\n} | es5.js:4:24:4:33 | this.props |
-| 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 |
-| importedComponent.jsx:3:8:5:1 | functio ... or}/>\\n} | importedComponent.jsx:3:24:3:40 | {color, location} |
-| 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 |
-| plainfn.js:5:1:7:1 | functio ... iv");\\n} | plainfn.js:5:17:5:21 | props |
-| plainfn.js:9:1:12:1 | functio ... rn x;\\n} | plainfn.js:9:17:9:21 | props |
-| plainfn.js:20:1:24:1 | functio ... n 42;\\n} | plainfn.js:20:28:20:32 | props |
-| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:1:38:1:37 | args |
-| preact.js:1:1:7:1 | class H ... }\\n} | preact.js:2:12:2:16 | props |
-| preact.js:9:1:11:1 | class H ... nt {\\n\\n} | preact.js:9:38:9:37 | args |
-| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:1:31:1:30 | args |
-| probably-a-component.js:1:1:6:1 | class H ... }\\n} | probably-a-component.js:3:9:3:18 | this.props |
-| props.js:2:5:3:5 | class C ... {\\n } | props.js:2:37:2:36 | args |
-| props.js:26:5:28:5 | functio ... ;\\n } | props.js:26:16:26:20 | props |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:1:33:1:32 | args |
-| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:38:24:38:33 | this.props |
-| thisAccesses.js:31:2:36:1 | functio ... iv/>;\\n} | thisAccesses.js:31:12:31:16 | props |
-| 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" |
-| props.js:15:44:15:65 | "propFr ... tProps" |
-| props.js:19:22:19:34 | "propFromJSX" |
-| props.js:21:29:21:49 | "propFr ... ructor" |
-| props.js:30:46:30:67 | "propFr ... tProps" |
-| props.js:32:22:32:34 | "propFromJSX" |
-| 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_getAPreviousStateSource
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:2:44:2:48 | state |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:8:40:8:48 | prevState |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:24:7:32 | prevState |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:10:35:10:43 | prevState |
-test_ReactComponent_getACandidateStateSource
-| es6.js:14:1:20:1 | class H ... }\\n} | es6.js:18:22:18:31 | { baz: 42} |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:3:16:3:17 | {} |
-| rare-lifecycle-methods.js:1:1:11:1 | class C ... }\\n} | rare-lifecycle-methods.js:5:38:5:46 | nextState |
-| statePropertyReads.js:1:1:13:1 | class R ... }\\n} | statePropertyReads.js:7:45:7:56 | prevState.p3 |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:8:18:8:19 | {} |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:12:18:12:19 | {} |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:16:18:16:19 | {} |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:20:18:20:19 | {} |
-| statePropertyWrites.js:1:1:34:1 | class W ... };\\n} | statePropertyWrites.js:31:13:33:5 | {\\n ... 2\\n } |
-| statePropertyWrites.js:36:19:45:1 | {\\n ren ... ;\\n }\\n} | statePropertyWrites.js:41:12:43:5 | {\\n p8: 42\\n } |
-| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:48:18:48:18 | y |
-| thisAccesses.js:47:1:52:1 | class C ... }\\n} | thisAccesses.js:49:22:49:22 | x |
-test_JsxName_this
-| es5.js:4:12:4:45 | He ... }
| es5.js:4:24:4:27 | this |
-| es5.js:20:12:20:44 | Hel ... e}
| es5.js:20:24:20:27 | this |
-| es6.js:3:12:3:45 | He ... }
| es6.js:3:24:3:27 | this |
-| statePropertyWrites.js:38:12:38:45 | He ... }
| statePropertyWrites.js:38:24:38:27 | this |
-| thisAccesses.js:60:19:60:41 | | thisAccesses.js:60:20:60:23 | this |
-| thisAccesses.js:61:19:61:41 | | thisAccesses.js:61:20:61:23 | this |
-locationSource
-| importedComponent.jsx:3:32:3:39 | location |
threatModelSource
| es5.js:4:24:4:33 | this.props | view-component-input |
| es5.js:20:24:20:33 | this.props | view-component-input |
@@ -343,3 +305,7 @@ threatModelSource
| statePropertyWrites.js:38:24:38:33 | this.props | view-component-input |
| thisAccesses.js:31:12:31:16 | props | view-component-input |
| thisAccesses.js:48:18:48:18 | y | view-component-input |
+| use-server1.js:2:5:2:5 | x | remote |
+| use-server1.js:3:5:3:5 | y | remote |
+| use-server2.js:4:5:4:5 | x | remote |
+| use-server2.js:5:5:5:5 | y | remote |
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.ql b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.ql
index 4d20306d4ed4..6de8091b010b 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.ql
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.ql
@@ -1,14 +1,53 @@
-import getADirectStateAccess
-import ReactComponent_getInstanceMethod
-import react
-import ReactComponent_getAPreviousStateSource
-import ReactComponent_ref
-import ReactComponent_getACandidateStateSource
-import ReactComponent_getADirectPropsSource
-import ReactComponent_getACandidatePropsValue
-import ReactComponent
-import ReactComponent_getAPropRead
-import ReactName
+import javascript
+import semmle.javascript.frameworks.React
+
+query predicate getADirectStateAccess(ReactComponent c, DataFlow::SourceNode res) {
+ res = c.getADirectStateAccess()
+}
+
+query predicate getInstanceMethod(ReactComponent c, string n, Function res) {
+ res = c.getInstanceMethod(n)
+}
+
+query predicate getAPreviousStateSource(ReactComponent c, DataFlow::SourceNode res) {
+ res = c.getAPreviousStateSource()
+}
+
+query predicate reactComponentRef(ReactComponent c, DataFlow::Node res) { res = c.ref() }
+
+query predicate getACandidateStateSource(ReactComponent c, DataFlow::SourceNode res) {
+ res = c.getACandidateStateSource()
+}
+
+query predicate getADirectPropsSource(ReactComponent c, DataFlow::SourceNode res) {
+ res = c.getADirectPropsAccess()
+}
+
+query predicate getACandidatePropsValue(DataFlow::Node res) {
+ exists(ReactComponent c | res = c.getACandidatePropsValue(_))
+}
+
+query predicate reactComponent(ReactComponent c) { any() }
+
+query predicate getAPropRead(ReactComponent c, string n, DataFlow::PropRead res) {
+ res = c.getAPropRead(n)
+}
+
+query predicate jsxName(JsxElement element, JsxName jsxname, string name, string type) {
+ name = jsxname.getValue() and
+ (
+ jsxname instanceof Identifier and type = "Identifier"
+ or
+ jsxname instanceof ThisExpr and type = "thisExpr"
+ or
+ jsxname.(DotExpr).getBase() instanceof JsxName and type = "dot"
+ or
+ jsxname instanceof JsxQualifiedName and type = "qualifiedName"
+ ) and
+ element.getNameExpr() = jsxname
+}
+
+query ThisExpr jsxNameThis(JsxElement element) { result.getParentExpr+() = element }
query DataFlow::SourceNode locationSource() { result = DOM::locationSource() }
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/tests.qlref b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.qlref
new file mode 100644
index 000000000000..8581a3f8b748
--- /dev/null
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/tests.qlref
@@ -0,0 +1,2 @@
+query: tests.ql
+postprocess: utils/test/InlineExpectationsTestQuery.ql
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses.js b/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses.js
index b662b7ca53bf..c30509974d4e 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses.js
@@ -13,7 +13,7 @@ class C extends React.Component {
someInstanceMethod() {
this;
}
-}
+} // $ reactComponent
React.createClass({
render: function() {
@@ -26,14 +26,14 @@ React.createClass({
someInstanceMethod: function() {
this;
}
-});
+}); // $ reactComponent
-(function (props) {
+(function (props) { // $ threatModelSource=view-component-input
(function () {
this; props;
}).bind(this);
return ;
-})
+}) // $ reactComponent
React.createClass({
render: function() {
@@ -42,14 +42,14 @@ React.createClass({
}, this)
return ;
},
-});
+}); // $ reactComponent
class C2 extends React.Component {
- constructor (y) {
+ constructor (y) { // $ threatModelSource=view-component-input
this.state = x;
this.state = y;
}
-}
+} // $ reactComponent
class C3 extends React.Component {
constructor() {
@@ -60,4 +60,4 @@ class C3 extends React.Component {
var foo = ;
var bar = ;
}
-}
+} // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses_importedMappers.js b/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses_importedMappers.js
index 06e5e1149fda..abbbdd844ed0 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses_importedMappers.js
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/thisAccesses_importedMappers.js
@@ -12,4 +12,4 @@ React.createClass({
return ;
},
-});
+}); // $ reactComponent
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/use-server1.js b/javascript/ql/test/library-tests/frameworks/ReactJS/use-server1.js
new file mode 100644
index 000000000000..1625ff23d1c0
--- /dev/null
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/use-server1.js
@@ -0,0 +1,10 @@
+async function getData(
+ x, // $ threatModelSource=remote
+ y) { // $ threatModelSource=remote
+ "use server";
+}
+
+async function getData2(
+ x, // should not be remote flow sources (because the function does not have "use server")
+ y) {
+}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/use-server2.js b/javascript/ql/test/library-tests/frameworks/ReactJS/use-server2.js
new file mode 100644
index 000000000000..fa0bbab05527
--- /dev/null
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/use-server2.js
@@ -0,0 +1,11 @@
+"use server";
+
+export async function getData(
+ x, // $ threatModelSource=remote
+ y) { // $ threatModelSource=remote
+}
+
+async function getData2(
+ x, // should not be remote flow sources (because the function is not exported)
+ y) {
+}
diff --git a/javascript/ql/test/library-tests/frameworks/ReactJS/useHigherOrderComponent.jsx b/javascript/ql/test/library-tests/frameworks/ReactJS/useHigherOrderComponent.jsx
index a57c5aa70bab..dba28fd1c6c5 100644
--- a/javascript/ql/test/library-tests/frameworks/ReactJS/useHigherOrderComponent.jsx
+++ b/javascript/ql/test/library-tests/frameworks/ReactJS/useHigherOrderComponent.jsx
@@ -2,17 +2,17 @@ import SomeComponent from './higherOrderComponent';
import { lazy } from 'react';
function foo() {
- return
+ return // $ getACandidatePropsValue
}
const LazyLoadedComponent = lazy(() => import('./higherOrderComponent'));
function bar() {
- return
+ return // $ getACandidatePropsValue
}
const LazyLoadedComponent2 = lazy(() => import('./exportedComponent').then(m => m.MyComponent));
function barz() {
- return
+ return // $ getACandidatePropsValue
}
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
index 4d54adb27249..412f0a5c5fa5 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/CodeInjection.expected
@@ -65,7 +65,8 @@
| module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | module.js:11:17:11:30 | req.query.code | This code execution depends on a $@. | module.js:11:17:11:30 | req.query.code | user-provided value |
| react-native.js:8:32:8:38 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:8:32:8:38 | tainted | This code execution depends on a $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
| react-native.js:10:23:10:29 | tainted | react-native.js:7:17:7:33 | req.param("code") | react-native.js:10:23:10:29 | tainted | This code execution depends on a $@. | react-native.js:7:17:7:33 | req.param("code") | user-provided value |
-| react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | react.js:10:56:10:77 | documen ... on.hash | This code execution depends on a $@. | react.js:10:56:10:77 | documen ... on.hash | user-provided value |
+| react.js:11:56:11:77 | documen ... on.hash | react.js:11:56:11:77 | documen ... on.hash | react.js:11:56:11:77 | documen ... on.hash | This code execution depends on a $@. | react.js:11:56:11:77 | documen ... on.hash | user-provided value |
+| react.js:25:8:25:11 | data | react-server-function.js:3:35:3:35 | x | react.js:25:8:25:11 | data | This code execution depends on a $@. | react-server-function.js:3:35:3:35 | x | user-provided value |
| template-sinks.js:20:17:20:23 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:20:17:20:23 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value |
| template-sinks.js:21:16:21:22 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:21:16:21:22 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value |
| template-sinks.js:22:18:22:24 | tainted | template-sinks.js:18:19:18:31 | req.query.foo | template-sinks.js:22:18:22:24 | tainted | Template, which may contain code, depends on a $@. | template-sinks.js:18:19:18:31 | req.query.foo | user-provided value |
@@ -156,6 +157,12 @@ edges
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | provenance | |
| react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | provenance | |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | provenance | |
+| react-server-function.js:3:35:3:35 | x | react-server-function.js:4:12:4:12 | x | provenance | |
+| react-server-function.js:4:12:4:12 | x | react-server-function.js:4:12:4:29 | x + " from server" | provenance | |
+| react-server-function.js:4:12:4:29 | x + " from server" | react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | provenance | |
+| react.js:24:9:24:45 | data | react.js:25:8:25:11 | data | provenance | |
+| react.js:24:16:24:45 | use(ech ... alue")) | react.js:24:9:24:45 | data | provenance | |
+| react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | react.js:24:16:24:45 | use(ech ... alue")) | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | provenance | |
@@ -287,7 +294,14 @@ nodes
| react-native.js:7:17:7:33 | req.param("code") | semmle.label | req.param("code") |
| react-native.js:8:32:8:38 | tainted | semmle.label | tainted |
| react-native.js:10:23:10:29 | tainted | semmle.label | tainted |
-| react.js:10:56:10:77 | documen ... on.hash | semmle.label | documen ... on.hash |
+| react-server-function.js:3:35:3:35 | x | semmle.label | x |
+| react-server-function.js:4:12:4:12 | x | semmle.label | x |
+| react-server-function.js:4:12:4:29 | x + " from server" | semmle.label | x + " from server" |
+| react.js:11:56:11:77 | documen ... on.hash | semmle.label | documen ... on.hash |
+| react.js:24:9:24:45 | data | semmle.label | data |
+| react.js:24:16:24:45 | use(ech ... alue")) | semmle.label | use(ech ... alue")) |
+| react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | semmle.label | echoSer ... value") [PromiseValue] |
+| react.js:25:8:25:11 | data | semmle.label | data |
| template-sinks.js:18:9:18:31 | tainted | semmle.label | tainted |
| template-sinks.js:18:19:18:31 | req.query.foo | semmle.label | req.query.foo |
| template-sinks.js:20:17:20:23 | tainted | semmle.label | tainted |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
index a1c8354ecf71..5a249b086b97 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/HeuristicSourceCodeInjection.expected
@@ -58,6 +58,12 @@ edges
| react-native.js:7:7:7:33 | tainted | react-native.js:8:32:8:38 | tainted | provenance | |
| react-native.js:7:7:7:33 | tainted | react-native.js:10:23:10:29 | tainted | provenance | |
| react-native.js:7:17:7:33 | req.param("code") | react-native.js:7:7:7:33 | tainted | provenance | |
+| react-server-function.js:3:35:3:35 | x | react-server-function.js:4:12:4:12 | x | provenance | |
+| react-server-function.js:4:12:4:12 | x | react-server-function.js:4:12:4:29 | x + " from server" | provenance | |
+| react-server-function.js:4:12:4:29 | x + " from server" | react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | provenance | |
+| react.js:24:9:24:45 | data | react.js:25:8:25:11 | data | provenance | |
+| react.js:24:16:24:45 | use(ech ... alue")) | react.js:24:9:24:45 | data | provenance | |
+| react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | react.js:24:16:24:45 | use(ech ... alue")) | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:20:17:20:23 | tainted | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:21:16:21:22 | tainted | provenance | |
| template-sinks.js:18:9:18:31 | tainted | template-sinks.js:22:18:22:24 | tainted | provenance | |
@@ -191,7 +197,14 @@ nodes
| react-native.js:7:17:7:33 | req.param("code") | semmle.label | req.param("code") |
| react-native.js:8:32:8:38 | tainted | semmle.label | tainted |
| react-native.js:10:23:10:29 | tainted | semmle.label | tainted |
-| react.js:10:56:10:77 | documen ... on.hash | semmle.label | documen ... on.hash |
+| react-server-function.js:3:35:3:35 | x | semmle.label | x |
+| react-server-function.js:4:12:4:12 | x | semmle.label | x |
+| react-server-function.js:4:12:4:29 | x + " from server" | semmle.label | x + " from server" |
+| react.js:11:56:11:77 | documen ... on.hash | semmle.label | documen ... on.hash |
+| react.js:24:9:24:45 | data | semmle.label | data |
+| react.js:24:16:24:45 | use(ech ... alue")) | semmle.label | use(ech ... alue")) |
+| react.js:24:20:24:44 | echoSer ... value") [PromiseValue] | semmle.label | echoSer ... value") [PromiseValue] |
+| react.js:25:8:25:11 | data | semmle.label | data |
| template-sinks.js:18:9:18:31 | tainted | semmle.label | tainted |
| template-sinks.js:18:19:18:31 | req.query.foo | semmle.label | req.query.foo |
| template-sinks.js:20:17:20:23 | tainted | semmle.label | tainted |
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react-server-function.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react-server-function.js
new file mode 100644
index 000000000000..9c6bf514201a
--- /dev/null
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react-server-function.js
@@ -0,0 +1,5 @@
+"use server";
+
+export async function echoService(x) { // $ Source[js/code-injection]
+ return x + " from server";
+}
diff --git a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react.js b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react.js
index 32db7a3f621a..ab6ff096af43 100644
--- a/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react.js
+++ b/javascript/ql/test/query-tests/Security/CWE-094/CodeInjection/react.js
@@ -1,6 +1,7 @@
-import React from "react";
+import React, { use } from "react";
import {Helmet} from "react-helmet";
-
+import { echoService } from "./react-server-function";
+
class Application extends React.Component {
render () {
return (
@@ -14,4 +15,12 @@ class Application extends React.Component {
}
};
-export default Application
\ No newline at end of file
+export default Application
+
+export function Component() {
+ // We currently get false-positive flow through server functions in cases where a safe value
+ // is passed as the argument, which flows to the return value. In this case, the tainted parameter
+ // flows out of the return value regardless.
+ const data = use(echoService("safe value"));
+ eval(data); // $ SPURIOUS: Alert[js/code-injection]
+}