Skip to content

Commit ad26740

Browse files
authored
Merge pull request github#5137 from asgerf/js/redux-less
Approved by erik-krogh
2 parents 8adaee0 + 7d300b5 commit ad26740

File tree

19 files changed

+1813
-32
lines changed

19 files changed

+1813
-32
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
lgtm,codescanning
2+
* Support for Redux has improved. The security queries can now track taint through reducer functions and state managed by Redux.
3+
Affected packages are `redux`, `react-redux`, `@reduxjs/toolkit`, `redux-actions`, `redux-persist`, `reduce-reducers`, `redux-immutable`, and `immer`.

javascript/ql/src/javascript.qll

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ import semmle.javascript.frameworks.PropertyProjection
105105
import semmle.javascript.frameworks.Puppeteer
106106
import semmle.javascript.frameworks.React
107107
import semmle.javascript.frameworks.ReactNative
108+
import semmle.javascript.frameworks.Redux
108109
import semmle.javascript.frameworks.Request
109110
import semmle.javascript.frameworks.RxJS
110111
import semmle.javascript.frameworks.ServerLess

javascript/ql/src/semmle/javascript/BasicBlocks.qll

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -307,34 +307,30 @@ class ReachableBasicBlock extends BasicBlock {
307307
/**
308308
* Holds if this basic block strictly dominates `bb`.
309309
*/
310-
cached
310+
pragma[inline]
311311
predicate strictlyDominates(ReachableBasicBlock bb) { bbIDominates+(this, bb) }
312312

313313
/**
314314
* Holds if this basic block dominates `bb`.
315315
*
316316
* This predicate is reflexive: each reachable basic block dominates itself.
317317
*/
318-
predicate dominates(ReachableBasicBlock bb) {
319-
bb = this or
320-
strictlyDominates(bb)
321-
}
318+
pragma[inline]
319+
predicate dominates(ReachableBasicBlock bb) { bbIDominates*(this, bb) }
322320

323321
/**
324322
* Holds if this basic block strictly post-dominates `bb`.
325323
*/
326-
cached
324+
pragma[inline]
327325
predicate strictlyPostDominates(ReachableBasicBlock bb) { bbIPostDominates+(this, bb) }
328326

329327
/**
330328
* Holds if this basic block post-dominates `bb`.
331329
*
332330
* This predicate is reflexive: each reachable basic block post-dominates itself.
333331
*/
334-
predicate postDominates(ReachableBasicBlock bb) {
335-
bb = this or
336-
strictlyPostDominates(bb)
337-
}
332+
pragma[inline]
333+
predicate postDominates(ReachableBasicBlock bb) { bbIPostDominates*(this, bb) }
338334
}
339335

340336
/**

javascript/ql/src/semmle/javascript/RangeAnalysis.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -606,10 +606,10 @@ module RangeAnalysis {
606606
cfg2BB = cfg2.getBasicBlock() and
607607
cfg2RBB = cfg2BB.(ReachableBasicBlock) and
608608
(
609-
cfg1RBB.strictlyDominates(cfg2BB) and
609+
cfg2BB.getImmediateDominator+() = cfg1RBB and
610610
cfg = cfg2
611611
or
612-
cfg2RBB.strictlyDominates(cfg1RBB) and
612+
cfg1BB.getImmediateDominator+() = cfg2BB and
613613
cfg = cfg1
614614
)
615615
)
@@ -681,7 +681,7 @@ module RangeAnalysis {
681681
midBB = midcfg.getBasicBlock() and
682682
midRBB = midBB.(ReachableBasicBlock) and
683683
cfgBB = cfg.getBasicBlock() and
684-
midRBB.strictlyDominates(cfgBB)
684+
cfgBB.getImmediateDominator+() = midRBB
685685
)
686686
}
687687

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/** Provides the `Unit` class. */
2+
3+
/** The unit type. */
4+
private newtype TUnit = TMkUnit()
5+
6+
/** The trivial type with a single element. */
7+
class Unit extends TUnit {
8+
/** Gets a textual representation of this element. */
9+
string toString() { result = "Unit" }
10+
}

javascript/ql/src/semmle/javascript/dataflow/Configuration.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ private import javascript
7272
private import internal.FlowSteps
7373
private import internal.AccessPaths
7474
private import internal.CallGraphs
75-
private import internal.Unit
75+
private import semmle.javascript.Unit
7676
private import semmle.javascript.internal.CachedStages
7777

7878
/**

javascript/ql/src/semmle/javascript/dataflow/DataFlow.qll

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,4 +1683,59 @@ module DataFlow {
16831683
import TypeTracking
16841684

16851685
predicate localTaintStep = TaintTracking::localTaintStep/2;
1686+
1687+
/**
1688+
* Holds if the function in `succ` forwards all its arguments to a call to `pred` and returns
1689+
* its result. This can thus be seen as a step `pred -> succ` used for tracking function values
1690+
* through "wrapper functions", since the `succ` function partially replicates behavior of `pred`.
1691+
*
1692+
* Examples:
1693+
* ```js
1694+
* function f(x) {
1695+
* return g(x); // step: g -> f
1696+
* }
1697+
*
1698+
* function doExec(x) {
1699+
* console.log(x);
1700+
* return exec(x); // step: exec -> doExec
1701+
* }
1702+
*
1703+
* function doEither(x, y) {
1704+
* if (x > y) {
1705+
* return foo(x, y); // step: foo -> doEither
1706+
* } else {
1707+
* return bar(x, y); // step: bar -> doEither
1708+
* }
1709+
* }
1710+
*
1711+
* function wrapWithLogging(f) {
1712+
* return (x) => {
1713+
* console.log(x);
1714+
* return f(x); // step: f -> anonymous function
1715+
* }
1716+
* }
1717+
* wrapWithLogging(g); // step: g -> wrapWithLogging(g)
1718+
* ```
1719+
*/
1720+
predicate functionForwardingStep(DataFlow::Node pred, DataFlow::Node succ) {
1721+
exists(DataFlow::FunctionNode function, DataFlow::CallNode call |
1722+
call.flowsTo(function.getReturnNode()) and
1723+
forall(int i | exists([call.getArgument(i), function.getParameter(i)]) |
1724+
function.getParameter(i).flowsTo(call.getArgument(i))
1725+
) and
1726+
pred = call.getCalleeNode() and
1727+
succ = function
1728+
)
1729+
or
1730+
// Given a generic wrapper function like,
1731+
//
1732+
// function wrap(f) { return (x, y) => f(x, y) };
1733+
//
1734+
// add steps through calls to that function: `g -> wrap(g)`
1735+
exists(DataFlow::FunctionNode wrapperFunction, SourceNode param, Node paramUse |
1736+
FlowSteps::argumentPassing(succ, pred, wrapperFunction.getFunction(), param) and
1737+
param.flowsTo(paramUse) and
1738+
functionForwardingStep(paramUse, wrapperFunction.getReturnNode().getALocalSource())
1739+
)
1740+
}
16861741
}

javascript/ql/src/semmle/javascript/dataflow/Sources.qll

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ class SourceNode extends DataFlow::Node {
5252
*/
5353
predicate flowsToExpr(Expr sink) { flowsTo(DataFlow::valueNode(sink)) }
5454

55+
/**
56+
* Gets a node into which data may flow from this node in zero or more local steps.
57+
*/
58+
DataFlow::Node getALocalUse() { flowsTo(result) }
59+
5560
/**
5661
* Gets a reference (read or write) of property `propName` on this node.
5762
*/

javascript/ql/src/semmle/javascript/dataflow/TaintTracking.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import javascript
1717
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
18-
private import semmle.javascript.dataflow.internal.Unit
18+
private import semmle.javascript.Unit
1919
private import semmle.javascript.dataflow.InferredTypes
2020
private import semmle.javascript.internal.CachedStages
2121

javascript/ql/src/semmle/javascript/dataflow/TypeTracking.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
private import javascript
1010
private import internal.FlowSteps
1111
private import internal.StepSummary
12-
private import internal.Unit
12+
private import semmle.javascript.Unit
1313
private import semmle.javascript.internal.CachedStages
1414

1515
private newtype TTypeTracker = MkTypeTracker(Boolean hasCall, OptionalPropertyName prop)

0 commit comments

Comments
 (0)