Skip to content

Commit adb40f9

Browse files
authored
Merge pull request github#9289 from erik-krogh/es2022
JS: Support the remaining of the finished ES2022 proposals
2 parents ab28b0a + 82c6c22 commit adb40f9

File tree

20 files changed

+390
-111
lines changed

20 files changed

+390
-111
lines changed

docs/codeql/support/reusables/versions-compilers.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
Java,"Java 7 to 18 [4]_","javac (OpenJDK and Oracle JDK),
2121

2222
Eclipse compiler for Java (ECJ) [5]_",``.java``
23-
JavaScript,ECMAScript 2021 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
23+
JavaScript,ECMAScript 2022 or lower,Not applicable,"``.js``, ``.jsx``, ``.mjs``, ``.es``, ``.es6``, ``.htm``, ``.html``, ``.xhtm``, ``.xhtml``, ``.vue``, ``.hbs``, ``.ejs``, ``.njk``, ``.json``, ``.yaml``, ``.yml``, ``.raml``, ``.xml`` [6]_"
2424
Python,"2.7, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10",Not applicable,``.py``
2525
Ruby [7]_,"up to 3.0.2",Not applicable,"``.rb``, ``.erb``, ``.gemspec``, ``Gemfile``"
2626
TypeScript [8]_,"2.6-4.7",Standard TypeScript compiler,"``.ts``, ``.tsx``, ``.mts``, ``.cts``"
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* All new ECMAScript 2022 features are now supported.

javascript/ql/lib/semmle/javascript/Arrays.qll

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ module ArrayTaintTracking {
7575
succ.(DataFlow::SourceNode).getAMethodCall("splice") = call
7676
or
7777
// `e = array.pop()`, `e = array.shift()`, or similar: if `array` is tainted, then so is `e`.
78-
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice"]) and
78+
call.(DataFlow::MethodCallNode).calls(pred, ["pop", "shift", "slice", "splice", "at"]) and
7979
succ = call
8080
or
8181
// `e = Array.from(x)`: if `x` is tainted, then so is `e`.
@@ -199,13 +199,13 @@ private module ArrayDataFlow {
199199
}
200200

201201
/**
202-
* A step for retrieving an element from an array using `.pop()` or `.shift()`.
202+
* A step for retrieving an element from an array using `.pop()`, `.shift()`, or `.at()`.
203203
* E.g. `array.pop()`.
204204
*/
205205
private class ArrayPopStep extends DataFlow::SharedFlowStep {
206206
override predicate loadStep(DataFlow::Node obj, DataFlow::Node element, string prop) {
207207
exists(DataFlow::MethodCallNode call |
208-
call.getMethodName() = ["pop", "shift"] and
208+
call.getMethodName() = ["pop", "shift", "at"] and
209209
prop = arrayElement() and
210210
obj = call.getReceiver() and
211211
element = call

javascript/ql/lib/semmle/javascript/MembershipCandidates.qll

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@ module MembershipCandidate {
229229
membersNode = inExpr.getRightOperand()
230230
)
231231
or
232-
exists(MethodCallExpr hasOwn |
233-
this = hasOwn.getArgument(0).flow() and
234-
test = hasOwn and
235-
hasOwn.calls(membersNode, "hasOwnProperty")
232+
exists(HasOwnPropertyCall hasOwn |
233+
this = hasOwn.getProperty() and
234+
test = hasOwn.asExpr() and
235+
membersNode = hasOwn.getObject().asExpr()
236236
)
237237
}
238238

javascript/ql/lib/semmle/javascript/StandardLibrary.qll

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,35 @@ class StringSplitCall extends DataFlow::MethodCallNode {
192192
bindingset[i]
193193
DataFlow::Node getASubstringRead(int i) { result = this.getAPropertyRead(i.toString()) }
194194
}
195+
196+
/**
197+
* A call to `Object.prototype.hasOwnProperty`, `Object.hasOwn`, or a library that implements
198+
* the same functionality.
199+
*/
200+
class HasOwnPropertyCall extends DataFlow::Node instanceof DataFlow::CallNode {
201+
DataFlow::Node object;
202+
DataFlow::Node property;
203+
204+
HasOwnPropertyCall() {
205+
// Make sure we handle reflective calls since libraries love to do that.
206+
super.getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() =
207+
"hasOwnProperty" and
208+
object = super.getReceiver() and
209+
property = super.getArgument(0)
210+
or
211+
this =
212+
[
213+
DataFlow::globalVarRef("Object").getAMemberCall("hasOwn"), //
214+
DataFlow::moduleImport("has").getACall(), //
215+
LodashUnderscore::member("has").getACall()
216+
] and
217+
object = super.getArgument(0) and
218+
property = super.getArgument(1)
219+
}
220+
221+
/** Gets the object whose property is being checked. */
222+
DataFlow::Node getObject() { result = object }
223+
224+
/** Gets the property being checked. */
225+
DataFlow::Node getProperty() { result = property }
226+
}

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

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,18 +1027,16 @@ module TaintTracking {
10271027
class WhitelistContainmentCallSanitizer extends AdditionalSanitizerGuardNode,
10281028
DataFlow::MethodCallNode {
10291029
WhitelistContainmentCallSanitizer() {
1030-
exists(string name |
1031-
name = "contains" or
1032-
name = "has" or
1033-
name = "hasOwnProperty"
1034-
|
1035-
this.getMethodName() = name
1036-
)
1030+
this.getMethodName() = ["contains", "has", "hasOwnProperty", "hasOwn"]
10371031
}
10381032

10391033
override predicate sanitizes(boolean outcome, Expr e) {
1040-
outcome = true and
1041-
e = this.getArgument(0).asExpr()
1034+
exists(int propertyIndex |
1035+
if this.getMethodName() = "hasOwn" then propertyIndex = 1 else propertyIndex = 0
1036+
|
1037+
outcome = true and
1038+
e = this.getArgument(propertyIndex).asExpr()
1039+
)
10421040
}
10431041

10441042
override predicate appliesTo(Configuration cfg) { any() }

javascript/ql/src/Declarations/UnusedProperty.ql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ predicate hasUnknownPropertyRead(LocalObject obj) {
2727
or
2828
exists(obj.getAPropertyRead("hasOwnProperty"))
2929
or
30+
obj.flowsTo(DataFlow::globalVarRef("Object").getAMemberCall("hasOwn").getArgument(0))
31+
or
3032
exists(obj.getAPropertyRead("propertyIsEnumerable"))
3133
}
3234

javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,19 +339,16 @@ class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNod
339339
* but the destination object generally doesn't. It is therefore only a sanitizer when
340340
* used on the destination object.
341341
*/
342-
class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode, CallNode {
342+
class HasOwnPropertyGuard extends DataFlow::BarrierGuardNode instanceof HasOwnPropertyCall {
343343
HasOwnPropertyGuard() {
344-
// Make sure we handle reflective calls since libraries love to do that.
345-
getCalleeNode().getALocalSource().(DataFlow::PropRead).getPropertyName() = "hasOwnProperty" and
346-
exists(getReceiver()) and
347344
// Try to avoid `src.hasOwnProperty` by requiring that the receiver
348345
// does not locally have its properties enumerated. Typically there is no
349346
// reason to enumerate the properties of the destination object.
350-
not arePropertiesEnumerated(getReceiver().getALocalSource())
347+
not arePropertiesEnumerated(super.getObject().getALocalSource())
351348
}
352349

353350
override predicate blocks(boolean outcome, Expr e) {
354-
e = getArgument(0).asExpr() and outcome = true
351+
e = super.getProperty().asExpr() and outcome = true
355352
}
356353
}
357354

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
| arrays.js:2:16:2:23 | "source" | arrays.js:74:8:74:29 | arr.fin ... llback) |
1212
| arrays.js:2:16:2:23 | "source" | arrays.js:77:8:77:35 | arrayFi ... llback) |
1313
| arrays.js:2:16:2:23 | "source" | arrays.js:81:10:81:10 | x |
14+
| arrays.js:2:16:2:23 | "source" | arrays.js:84:8:84:17 | arr.at(-1) |
1415
| arrays.js:18:22:18:29 | "source" | arrays.js:18:50:18:50 | e |
1516
| arrays.js:22:15:22:22 | "source" | arrays.js:23:8:23:17 | arr2.pop() |
1617
| arrays.js:25:15:25:22 | "source" | arrays.js:26:8:26:17 | arr3.pop() |

javascript/ql/test/library-tests/Arrays/arrays.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,6 @@
8080
for (const x of uniq(arr)) {
8181
sink(x); // NOT OK
8282
}
83+
84+
sink(arr.at(-1)); // NOT OK
8385
});

0 commit comments

Comments
 (0)