Skip to content

Commit 75451e3

Browse files
committed
JS: teach the dataflow library identity functions Object.freeze/seal
1 parent 33c5276 commit 75451e3

File tree

5 files changed

+26
-1
lines changed

5 files changed

+26
-1
lines changed

javascript/ql/src/semmle/javascript/GlobalAccessPaths.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import javascript
66
private import semmle.javascript.dataflow.InferredTypes
7+
private import semmle.javascript.dataflow.internal.FlowSteps as FlowSteps
78

89
deprecated module GlobalAccessPath {
910
/**
@@ -58,7 +59,8 @@ module AccessPath {
5859
not this instanceof DataFlow::PropRead and
5960
not this instanceof PropertyProjection and
6061
not this instanceof Closure::ClosureNamespaceAccess and
61-
not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter())
62+
not this = DataFlow::parameterNode(any(ImmediatelyInvokedFunctionExpr iife).getAParameter()) and
63+
not this = FlowSteps::IdentityCalls::syntactic(_)
6264
}
6365

6466
/** Holds if this represents the root of the global access path. */

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ module DataFlow {
198198
result = unique(Expr ret | ret = fun.getAReturnedExpr()).flow() and
199199
not fun.getExit().isJoin() // can only reach exit by the return statement
200200
)
201+
or
202+
this = FlowSteps::IdentityCalls::syntactic(result)
201203
}
202204

203205
/**

javascript/ql/src/semmle/javascript/dataflow/internal/FlowSteps.qll

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,3 +529,19 @@ module PathSummary {
529529
*/
530530
PathSummary return() { exists(FlowLabel lbl | result = MkPathSummary(true, false, lbl, lbl)) }
531531
}
532+
533+
/**
534+
* Provides predicates for reasoning about calls to identity functions.
535+
*/
536+
module IdentityCalls {
537+
/**
538+
* Gets an identity call for `input` that can be recognized syntactically.
539+
*/
540+
DataFlow::CallNode syntactic(DataFlow::Node input) {
541+
exists(DataFlow::GlobalVarRefNode global |
542+
global.getName() = "Object" and
543+
result.(DataFlow::MethodCallNode).calls(global, ["freeze", "seal"]) and
544+
input = result.getArgument(0)
545+
)
546+
}
547+
}

javascript/ql/test/library-tests/DataFlow/getImmediatePredecessor.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,4 @@
151151
| tst.js:111:7:111:9 | v2a | tst.js:111:6:111:38 | v2a |
152152
| tst.js:111:36:111:38 | o2d | tst.js:111:6:111:32 | [v2a, v ... = o2c] |
153153
| tst.js:115:1:115:12 | reflective call | tst.js:115:1:115:12 | Array.call() |
154+
| tst.js:117:22:117:23 | x1 | tst.js:117:10:117:24 | Object.seal(x1) |

javascript/ql/test/library-tests/GlobalAccessPaths/GlobalAccessPaths.expected

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,14 @@ test_getAReferenceTo
6767
| test.js:56:7:56:12 | random | random |
6868
| test.js:67:11:67:16 | Object | Object |
6969
| test.js:67:11:67:23 | Object.freeze | Object.freeze |
70+
| test.js:67:11:67:32 | Object. ... oo.bar) | foo.bar |
71+
| test.js:67:11:67:36 | Object. ... ar).baz | foo.bar.baz |
7072
| test.js:67:25:67:27 | foo | foo |
7173
| test.js:67:25:67:31 | foo.bar | foo.bar |
7274
| test.js:68:11:68:16 | Object | Object |
7375
| test.js:68:11:68:21 | Object.seal | Object.seal |
76+
| test.js:68:11:68:30 | Object.seal(foo.bar) | foo.bar |
77+
| test.js:68:11:68:34 | Object. ... ar).baz | foo.bar.baz |
7478
| test.js:68:23:68:25 | foo | foo |
7579
| test.js:68:23:68:29 | foo.bar | foo.bar |
7680
| test.js:69:6:69:15 | O | Object |

0 commit comments

Comments
 (0)