From 93882263f934cf1c717b259b72e99ae747b8e749 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 12:32:18 +0200 Subject: [PATCH 01/14] Added test case for `Uint8Array` and `TypedArray.prototype.buffer` --- .../TaintTracking/BasicTaintTracking.expected | 3 +++ .../test/library-tests/TaintTracking/typed-arrays.js | 12 ++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 javascript/ql/test/library-tests/TaintTracking/typed-arrays.js diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 4a0575eb73e1..b16953795b1d 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -40,6 +40,9 @@ consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:5 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:7 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:11 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js new file mode 100644 index 000000000000..c29c7bef9548 --- /dev/null +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -0,0 +1,12 @@ +function test() { + let x = source(); + + let y = new Uint8Array(x); + sink(y); // NOT OK + + sink(y.buffer); // NOT OK + sink(y.length); + + var arr = new Uint8Array(y.buffer, y.byteOffset, y.byteLength); + sink(arr); // NOT OK +} From e23ff9cf3eeca835094d64da502ace1b63403b2a Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 12:55:58 +0200 Subject: [PATCH 02/14] Add TypedArrays flow summaries for `Uint8Array` and buffer property --- .../flow_summaries/AllFlowSummaries.qll | 1 + .../internal/flow_summaries/TypedArrays.qll | 38 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 9 +++-- .../DecompressionBombs.expected | 5 +++ 4 files changed, 50 insertions(+), 3 deletions(-) create mode 100644 javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll index 940180d80cb4..20247b7e2681 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll @@ -12,3 +12,4 @@ private import Sets private import Strings private import DynamicImportStep private import UrlSearchParams +private import TypedArrays diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll new file mode 100644 index 000000000000..788992d7c1ca --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll @@ -0,0 +1,38 @@ +private import javascript +private import semmle.javascript.dataflow.FlowSummary +private import semmle.javascript.dataflow.InferredTypes +private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private +private import FlowSummaryUtil + +private class TypedArrayEntryPoint extends API::EntryPoint { + TypedArrayEntryPoint() { this = "global.Uint8Array" } + + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("Uint8Array") } +} + +pragma[nomagic] +API::Node typedArrayConstructorRef() { result = any(TypedArrayEntryPoint e).getANode() } + +class TypedArrayConstructorSummary extends SummarizedCallable { + TypedArrayConstructorSummary() { this = "TypedArray constructor" } + + override DataFlow::InvokeNode getACall() { + result = typedArrayConstructorRef().getAnInstantiation() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[0].ArrayElement" and + output = "ReturnValue.ArrayElement" + } +} + +class BufferTypedArray extends DataFlow::AdditionalFlowStep { + override predicate step(DataFlow::Node pred, DataFlow::Node succ) { + exists(DataFlow::PropRead p | + p = typedArrayConstructorRef().getInstance().getMember("buffer").asSource() and + pred = p.getBase() and + succ = p + ) + } +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index b16953795b1d..14ae297b4fd1 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -35,14 +35,14 @@ legacyDataFlowDifference | spread.js:4:15:4:22 | source() | spread.js:18:8:18:8 | y | only flow with NEW data flow library | | spread.js:4:15:4:22 | source() | spread.js:24:8:24:8 | y | only flow with NEW data flow library | | tst.js:2:13:2:20 | source() | tst.js:17:10:17:10 | a | only flow with OLD data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:5:10:5:10 | y | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:7:10:7:17 | y.buffer | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:5 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:7 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:11 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -328,6 +328,9 @@ flow | tst.js:87:22:87:29 | source() | tst.js:90:14:90:25 | taintedValue | | tst.js:93:22:93:29 | source() | tst.js:96:14:96:25 | taintedValue | | tst.js:93:22:93:29 | source() | tst.js:97:14:97:26 | map.get(true) | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:5:10:5:10 | y | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:7:10:7:17 | y.buffer | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | diff --git a/javascript/ql/test/query-tests/Security/CWE-522-DecompressionBombs/DecompressionBombs.expected b/javascript/ql/test/query-tests/Security/CWE-522-DecompressionBombs/DecompressionBombs.expected index 56acd5390128..11c63c257e83 100644 --- a/javascript/ql/test/query-tests/Security/CWE-522-DecompressionBombs/DecompressionBombs.expected +++ b/javascript/ql/test/query-tests/Security/CWE-522-DecompressionBombs/DecompressionBombs.expected @@ -81,9 +81,12 @@ edges | pako.js:18:48:18:66 | zipFile.data.buffer | pako.js:18:33:18:67 | new Uin ... buffer) | provenance | Config | | pako.js:28:19:28:25 | zipFile | pako.js:29:36:29:42 | zipFile | provenance | | | pako.js:29:11:29:62 | myArray | pako.js:32:31:32:37 | myArray | provenance | | +| pako.js:29:11:29:62 | myArray [ArrayElement] | pako.js:32:31:32:37 | myArray | provenance | | | pako.js:29:21:29:55 | new Uin ... buffer) | pako.js:29:11:29:62 | myArray | provenance | | +| pako.js:29:21:29:55 | new Uin ... buffer) [ArrayElement] | pako.js:29:11:29:62 | myArray [ArrayElement] | provenance | | | pako.js:29:36:29:42 | zipFile | pako.js:29:36:29:54 | zipFile.data.buffer | provenance | | | pako.js:29:36:29:54 | zipFile.data.buffer | pako.js:29:21:29:55 | new Uin ... buffer) | provenance | Config | +| pako.js:29:36:29:54 | zipFile.data.buffer | pako.js:29:21:29:55 | new Uin ... buffer) [ArrayElement] | provenance | | | unbzip2.js:12:5:12:43 | fs.crea ... lePath) | unbzip2.js:12:50:12:54 | bz2() | provenance | Config | | unbzip2.js:12:25:12:42 | req.query.FilePath | unbzip2.js:12:5:12:43 | fs.crea ... lePath) | provenance | Config | | unzipper.js:13:26:13:62 | Readabl ... e.data) | unzipper.js:16:23:16:63 | unzippe ... ath' }) | provenance | Config | @@ -183,7 +186,9 @@ nodes | pako.js:21:31:21:37 | myArray | semmle.label | myArray | | pako.js:28:19:28:25 | zipFile | semmle.label | zipFile | | pako.js:29:11:29:62 | myArray | semmle.label | myArray | +| pako.js:29:11:29:62 | myArray [ArrayElement] | semmle.label | myArray [ArrayElement] | | pako.js:29:21:29:55 | new Uin ... buffer) | semmle.label | new Uin ... buffer) | +| pako.js:29:21:29:55 | new Uin ... buffer) [ArrayElement] | semmle.label | new Uin ... buffer) [ArrayElement] | | pako.js:29:36:29:42 | zipFile | semmle.label | zipFile | | pako.js:29:36:29:54 | zipFile.data.buffer | semmle.label | zipFile.data.buffer | | pako.js:32:31:32:37 | myArray | semmle.label | myArray | From d689a55229615798a3a03d2c067dc659cf44e9fa Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 13:12:00 +0200 Subject: [PATCH 03/14] Added test cases for `TypedArray` methods --- .../TaintTracking/BasicTaintTracking.expected | 3 +++ .../test/library-tests/TaintTracking/typed-arrays.js | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 14ae297b4fd1..3856dfe15f8d 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -43,6 +43,9 @@ consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:15 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:18 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:22 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index c29c7bef9548..4ccf64131b60 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -9,4 +9,15 @@ function test() { var arr = new Uint8Array(y.buffer, y.byteOffset, y.byteLength); sink(arr); // NOT OK + + const z = new Uint8Array([1, 2, 3]); + z.set(y, 3); + sink(z); // NOT OK + + const sub = y.subarray(1, 3) + sink(sub); // NOT OK + + const clone = new y.constructor(y.length); + clone.set(y); + sink(clone); // NOT OK } From ff07ec8d8c58749dded7417c17585f7dfdb80d73 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 13:43:50 +0200 Subject: [PATCH 04/14] Add flow summaries for TypedArray methods `set` and `subarray` --- .../internal/flow_summaries/TypedArrays.qll | 29 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 7 +++-- .../TaintTracking/typed-arrays.js | 4 --- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll index 788992d7c1ca..43c2a5e07e5a 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll @@ -36,3 +36,32 @@ class BufferTypedArray extends DataFlow::AdditionalFlowStep { ) } } + +class SetLike extends SummarizedCallable { + SetLike() { this = "TypedArray#set" } + + override InstanceCall getACall() { + result = typedArrayConstructorRef().getAnInstantiation().getReturn().getMember("set").getACall() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[0].ArrayElement" and + output = "Argument[this].ArrayElement" + } +} + +class SubArrayLike extends SummarizedCallable { + SubArrayLike() { this = "TypedArray#subarray" } + + override InstanceCall getACall() { + result = + typedArrayConstructorRef().getAnInstantiation().getReturn().getMember("subarray").getACall() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[this].ArrayElement" and + output = "ReturnValue.ArrayElement" + } +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 3856dfe15f8d..78dbc3dc5dc7 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -38,14 +38,13 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:5:10:5:10 | y | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:7:10:7:17 | y.buffer | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:15 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:18 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:22 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -334,6 +333,8 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:5:10:5:10 | y | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:7:10:7:17 | y.buffer | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index 4ccf64131b60..1b66d5491d90 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -16,8 +16,4 @@ function test() { const sub = y.subarray(1, 3) sink(sub); // NOT OK - - const clone = new y.constructor(y.length); - clone.set(y); - sink(clone); // NOT OK } From 0e099474c56b9157905478c7cb0881e28124ec41 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 13:50:46 +0200 Subject: [PATCH 05/14] Added test cases for `ArrayBuffer` and `SharedArrayBuffer` --- .../TaintTracking/BasicTaintTracking.expected | 4 ++++ .../library-tests/TaintTracking/typed-arrays.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index 78dbc3dc5dc7..c51eb4793134 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -45,6 +45,10 @@ consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:22 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:26 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:30 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:34 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index 1b66d5491d90..9b85e8f2804d 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -16,4 +16,20 @@ function test() { const sub = y.subarray(1, 3) sink(sub); // NOT OK + + const buffer = new ArrayBuffer(x); + const view = new Uint8Array(buffer); + sink(view); // NOT OK + + const sharedBuffer = new SharedArrayBuffer(x); + const view1 = new Uint8Array(sharedBuffer); + sink(view1); // NOT OK + + const transfered = buffer.transfer(); + const transferedView = new Uint8Array(transfered); + sink(transferedView); // NOT OK + + const transfered2 = buffer.transferToFixedLength(); + const transferedView2 = new Uint8Array(transfered2); + sink(transferedView2); // NOT OK } From f4277204b7fb664be85d6c8cb80ddb6d84da1bd1 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 13:56:13 +0200 Subject: [PATCH 06/14] Add flow summaries and entry points for `ArrayBuffer` and `SharedArrayBuffer` --- .../internal/flow_summaries/TypedArrays.qll | 44 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 12 +++-- 2 files changed, 52 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll index 43c2a5e07e5a..81e84d806fd4 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll @@ -65,3 +65,47 @@ class SubArrayLike extends SummarizedCallable { output = "ReturnValue.ArrayElement" } } + +private class ArrayBufferEntryPoint extends API::EntryPoint { + ArrayBufferEntryPoint() { this = ["global.ArrayBuffer", "global.SharedArrayBuffer"] } + + override DataFlow::SourceNode getASource() { + result = DataFlow::globalVarRef(["ArrayBuffer", "SharedArrayBuffer"]) + } +} + +pragma[nomagic] +API::Node arrayBufferConstructorRef() { result = any(ArrayBufferEntryPoint a).getANode() } + +class ArrayBufferConstructorSummary extends SummarizedCallable { + ArrayBufferConstructorSummary() { this = "ArrayBuffer constructor" } + + override DataFlow::InvokeNode getACall() { + result = arrayBufferConstructorRef().getAnInstantiation() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[0].ArrayElement" and + output = "ReturnValue.ArrayElement" + } +} + +class TransferLike extends SummarizedCallable { + TransferLike() { this = "ArrayBuffer#transfer" } + + override InstanceCall getACall() { + result = + arrayBufferConstructorRef() + .getAnInstantiation() + .getReturn() + .getMember(["transfer", "transferToFixedLength"]) + .getACall() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[this].ArrayElement" and + output = "ReturnValue.ArrayElement" + } +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index c51eb4793134..fa33cf231254 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -40,15 +40,15 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:22:10:22:13 | view | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:22 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:26 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:30 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:34 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -339,6 +339,10 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:22:10:22:13 | view | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | From f28478e87668c644e3990254c4b8e1a921ee7878 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 14:23:44 +0200 Subject: [PATCH 07/14] Add test cases from `TypedArrays` to strings. --- .../TaintTracking/BasicTaintTracking.expected | 4 ++++ .../library-tests/TaintTracking/typed-arrays.js | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index fa33cf231254..b20e00624110 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -44,11 +44,14 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | +| typed-arrays.js:40 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:50 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -343,6 +346,7 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index 9b85e8f2804d..d2dc8b2168c4 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -32,4 +32,20 @@ function test() { const transfered2 = buffer.transferToFixedLength(); const transferedView2 = new Uint8Array(transfered2); sink(transferedView2); // NOT OK + + var typedArrayToString = (function () { + return function (a) { return String.fromCharCode.apply(null, a); }; + })(); + + sink(typedArrayToString(y)); // NOT OK -- Should be flagged but it is not. + + let str = ''; + for (let i = 0; i < y.length; i++) + str += String.fromCharCode(y[i]); + + sink(str); // NOT OK + + const decoder = new TextDecoder('utf-8'); + const str2 = decoder.decode(y); + sink(str2); // NOT OK } From b97c61864e904d0cde359132e600f899374deb94 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 14:37:52 +0200 Subject: [PATCH 08/14] Add flow summaries and entry points for `TextDecoder` --- .../flow_summaries/AllFlowSummaries.qll | 1 + .../internal/flow_summaries/Decoders.qll | 29 +++++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 3 +- 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll index 20247b7e2681..dfbe9ef7da31 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/AllFlowSummaries.qll @@ -13,3 +13,4 @@ private import Strings private import DynamicImportStep private import UrlSearchParams private import TypedArrays +private import Decoders diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll new file mode 100644 index 000000000000..567a0403bcf5 --- /dev/null +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll @@ -0,0 +1,29 @@ +private import javascript +private import semmle.javascript.dataflow.FlowSummary +private import semmle.javascript.dataflow.InferredTypes +private import semmle.javascript.dataflow.internal.DataFlowPrivate as Private +private import FlowSummaryUtil + +private class TextDecoderEntryPoint extends API::EntryPoint { + TextDecoderEntryPoint() { this = "global.TextDecoder" } + + override DataFlow::SourceNode getASource() { result = DataFlow::globalVarRef("TextDecoder") } +} + +pragma[nomagic] +API::Node textDecoderConstructorRef() { result = any(TextDecoderEntryPoint e).getANode() } + +class DecodeLike extends SummarizedCallable { + DecodeLike() { this = "TextDecoder#decode" } + + override InstanceCall getACall() { + result = + textDecoderConstructorRef().getAnInstantiation().getReturn().getMember("decode").getACall() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + input = "Argument[0]" and + output = "ReturnValue" + } +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index b20e00624110..e0cbc7b3b2eb 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -45,13 +45,13 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:50:10:50:13 | str2 | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | | typed-arrays.js:40 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | -| typed-arrays.js:50 | expected an alert, but found none | NOT OK | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -347,6 +347,7 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:50:10:50:13 | str2 | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | From 873db7c1215e8fcee250c9f55a7c9061bed0e286 Mon Sep 17 00:00:00 2001 From: Napalys Date: Mon, 7 Apr 2025 15:16:22 +0200 Subject: [PATCH 09/14] Added change note --- javascript/ql/lib/change-notes/2025-04-07-typed-arrays.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 javascript/ql/lib/change-notes/2025-04-07-typed-arrays.md diff --git a/javascript/ql/lib/change-notes/2025-04-07-typed-arrays.md b/javascript/ql/lib/change-notes/2025-04-07-typed-arrays.md new file mode 100644 index 000000000000..f09e6831743b --- /dev/null +++ b/javascript/ql/lib/change-notes/2025-04-07-typed-arrays.md @@ -0,0 +1,4 @@ +--- +category: minorAnalysis +--- +* Added taint propagation for `Uint8Array`, `ArrayBuffer`, `SharedArrayBuffer` and `TextDecoder.decode()`. From 4bc3e9e736e112c7d2950857b0f768b904bce388 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 9 Apr 2025 12:16:53 +0200 Subject: [PATCH 10/14] Addressed comments Co-authored-by: Asgerf --- .../internal/flow_summaries/Decoders.qll | 3 +-- .../internal/flow_summaries/TypedArrays.qll | 22 ++++++------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll index 567a0403bcf5..7e75d6482c5c 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll @@ -17,8 +17,7 @@ class DecodeLike extends SummarizedCallable { DecodeLike() { this = "TextDecoder#decode" } override InstanceCall getACall() { - result = - textDecoderConstructorRef().getAnInstantiation().getReturn().getMember("decode").getACall() + result = textDecoderConstructorRef().getInstance().getMember("decode").getACall() } override predicate propagatesFlow(string input, string output, boolean preservesValue) { diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll index 81e84d806fd4..9f3140497ab2 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll @@ -37,11 +37,11 @@ class BufferTypedArray extends DataFlow::AdditionalFlowStep { } } -class SetLike extends SummarizedCallable { - SetLike() { this = "TypedArray#set" } +class TypedArraySet extends SummarizedCallable { + TypedArraySet() { this = "TypedArray#set" } override InstanceCall getACall() { - result = typedArrayConstructorRef().getAnInstantiation().getReturn().getMember("set").getACall() + result = typedArrayConstructorRef().getInstance().getMember("set").getACall() } override predicate propagatesFlow(string input, string output, boolean preservesValue) { @@ -51,13 +51,10 @@ class SetLike extends SummarizedCallable { } } -class SubArrayLike extends SummarizedCallable { - SubArrayLike() { this = "TypedArray#subarray" } +class TypedArraySubarray extends SummarizedCallable { + TypedArraySubarray() { this = "TypedArray#subarray" } - override InstanceCall getACall() { - result = - typedArrayConstructorRef().getAnInstantiation().getReturn().getMember("subarray").getACall() - } + override InstanceCall getACall() { result.getMethodName() = "subarray" } override predicate propagatesFlow(string input, string output, boolean preservesValue) { preservesValue = true and @@ -95,12 +92,7 @@ class TransferLike extends SummarizedCallable { TransferLike() { this = "ArrayBuffer#transfer" } override InstanceCall getACall() { - result = - arrayBufferConstructorRef() - .getAnInstantiation() - .getReturn() - .getMember(["transfer", "transferToFixedLength"]) - .getACall() + result.getMethodName() = ["transfer", "transferToFixedLength"] } override predicate propagatesFlow(string input, string output, boolean preservesValue) { From a3e4e62eacb3279bf8b30b6087bca0e023365bd3 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 9 Apr 2025 13:27:13 +0200 Subject: [PATCH 11/14] Removed taint from `ArrayBuffer` constructor as it accepts `length` --- .../internal/flow_summaries/TypedArrays.qll | 14 ------------ .../TaintTracking/BasicTaintTracking.expected | 22 ++++++++----------- .../TaintTracking/typed-arrays.js | 14 +++++++----- 3 files changed, 17 insertions(+), 33 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll index 9f3140497ab2..19a28036db49 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/TypedArrays.qll @@ -74,20 +74,6 @@ private class ArrayBufferEntryPoint extends API::EntryPoint { pragma[nomagic] API::Node arrayBufferConstructorRef() { result = any(ArrayBufferEntryPoint a).getANode() } -class ArrayBufferConstructorSummary extends SummarizedCallable { - ArrayBufferConstructorSummary() { this = "ArrayBuffer constructor" } - - override DataFlow::InvokeNode getACall() { - result = arrayBufferConstructorRef().getAnInstantiation() - } - - override predicate propagatesFlow(string input, string output, boolean preservesValue) { - preservesValue = true and - input = "Argument[0].ArrayElement" and - output = "ReturnValue.ArrayElement" - } -} - class TransferLike extends SummarizedCallable { TransferLike() { this = "ArrayBuffer#transfer" } diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index e0cbc7b3b2eb..b82fdc8d1ee5 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -40,18 +40,18 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:22:10:22:13 | view | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | only flow with NEW data flow library | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:50:10:50:13 | str2 | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:48:10:48:12 | str | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:52:10:52:13 | str2 | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | consistencyIssue | nested-props.js:20 | expected an alert, but found none | NOT OK - but not found | Consistency | | stringification-read-steps.js:17 | expected an alert, but found none | NOT OK | Consistency | | stringification-read-steps.js:25 | expected an alert, but found none | NOT OK | Consistency | -| typed-arrays.js:40 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:23 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:28 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:32 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:36 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | +| typed-arrays.js:42 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -342,12 +342,8 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:22:10:22:13 | view | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:26:10:26:14 | view1 | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:30:10:30:23 | transferedView | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:34:10:34:24 | transferedView2 | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:46:10:46:12 | str | -| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:50:10:50:13 | str2 | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:48:10:48:12 | str | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:52:10:52:13 | str2 | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | | xml.js:5:18:5:25 | source() | xml.js:8:14:8:17 | text | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index d2dc8b2168c4..e3eed25dd87f 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -17,21 +17,23 @@ function test() { const sub = y.subarray(1, 3) sink(sub); // NOT OK - const buffer = new ArrayBuffer(x); + const buffer = new ArrayBuffer(8); const view = new Uint8Array(buffer); - sink(view); // NOT OK + view.set(x, 3); + sink(buffer); // NOT OK -- Should be flagged but it is not. - const sharedBuffer = new SharedArrayBuffer(x); + const sharedBuffer = new SharedArrayBuffer(8); const view1 = new Uint8Array(sharedBuffer); - sink(view1); // NOT OK + view1.set(x, 3); + sink(sharedBuffer); // NOT OK -- Should be flagged but it is not. const transfered = buffer.transfer(); const transferedView = new Uint8Array(transfered); - sink(transferedView); // NOT OK + sink(transferedView); // NOT OK -- Should be flagged but it is not. const transfered2 = buffer.transferToFixedLength(); const transferedView2 = new Uint8Array(transfered2); - sink(transferedView2); // NOT OK + sink(transferedView2); // NOT OK -- Should be flagged but it is not. var typedArrayToString = (function () { return function (a) { return String.fromCharCode.apply(null, a); }; From 0c52b5ad9596b6bc8d5e4227f6205991e3672628 Mon Sep 17 00:00:00 2001 From: Napalys Date: Wed, 9 Apr 2025 14:24:43 +0200 Subject: [PATCH 12/14] Added summary flow for `StringFromCharCode` --- .../internal/flow_summaries/Strings.qll | 16 ++++++++++++++++ .../TaintTracking/BasicTaintTracking.expected | 3 ++- .../library-tests/TaintTracking/typed-arrays.js | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll index 154668cde080..8c8ab1ac4acf 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll @@ -99,3 +99,19 @@ class StringSplitHashOrQuestionMark extends SummarizedCallable { ) } } + +class StringFromCharCode extends SummarizedCallable { + StringFromCharCode() { this = "String#fromCharCode" } + + override DataFlow::CallNode getACall() { + result = DataFlow::globalVarRef("String").getAPropertyRead("fromCharCode").getACall() + } + + override predicate propagatesFlow(string input, string output, boolean preservesValue) { + preservesValue = true and + ( + input = "Argument[0..]" and + output = "ReturnValue" + ) + } +} diff --git a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected index b82fdc8d1ee5..0083e55e642e 100644 --- a/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected +++ b/javascript/ql/test/library-tests/TaintTracking/BasicTaintTracking.expected @@ -40,6 +40,7 @@ legacyDataFlowDifference | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | only flow with NEW data flow library | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:42:10:42:30 | typedAr ... ring(y) | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:48:10:48:12 | str | only flow with NEW data flow library | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:52:10:52:13 | str2 | only flow with NEW data flow library | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:15:10:15:10 | x | only flow with NEW data flow library | @@ -51,7 +52,6 @@ consistencyIssue | typed-arrays.js:28 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | | typed-arrays.js:32 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | | typed-arrays.js:36 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | -| typed-arrays.js:42 | expected an alert, but found none | NOT OK -- Should be flagged but it is not. | Consistency | flow | access-path-sanitizer.js:2:18:2:25 | source() | access-path-sanitizer.js:4:8:4:12 | obj.x | | addexpr.js:4:10:4:17 | source() | addexpr.js:7:8:7:8 | x | @@ -342,6 +342,7 @@ flow | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:11:10:11:12 | arr | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:15:10:15:10 | z | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:18:10:18:12 | sub | +| typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:42:10:42:30 | typedAr ... ring(y) | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:48:10:48:12 | str | | typed-arrays.js:2:13:2:20 | source() | typed-arrays.js:52:10:52:13 | str2 | | use-use-after-implicit-read.js:7:17:7:24 | source() | use-use-after-implicit-read.js:8:10:8:17 | captured | diff --git a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js index e3eed25dd87f..0118c2ae6904 100644 --- a/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js +++ b/javascript/ql/test/library-tests/TaintTracking/typed-arrays.js @@ -39,7 +39,7 @@ function test() { return function (a) { return String.fromCharCode.apply(null, a); }; })(); - sink(typedArrayToString(y)); // NOT OK -- Should be flagged but it is not. + sink(typedArrayToString(y)); // NOT OK let str = ''; for (let i = 0; i < y.length; i++) From e3f1720f9c532d4afd24cb8de2beb5f173b890c1 Mon Sep 17 00:00:00 2001 From: Napalys Date: Fri, 11 Apr 2025 10:04:09 +0200 Subject: [PATCH 13/14] Renamed`DecodeLike` to `Decode` and updated `propagatesFlow` --- .../javascript/internal/flow_summaries/Decoders.qll | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll index 7e75d6482c5c..2866c8926087 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Decoders.qll @@ -13,16 +13,16 @@ private class TextDecoderEntryPoint extends API::EntryPoint { pragma[nomagic] API::Node textDecoderConstructorRef() { result = any(TextDecoderEntryPoint e).getANode() } -class DecodeLike extends SummarizedCallable { - DecodeLike() { this = "TextDecoder#decode" } +class Decode extends SummarizedCallable { + Decode() { this = "TextDecoder#decode" } override InstanceCall getACall() { result = textDecoderConstructorRef().getInstance().getMember("decode").getACall() } override predicate propagatesFlow(string input, string output, boolean preservesValue) { - preservesValue = true and - input = "Argument[0]" and + preservesValue = false and + input = "Argument[0].ArrayElement" and output = "ReturnValue" } } From d0dcf897cb82b4f44ccfd3edebeb048346b551cc Mon Sep 17 00:00:00 2001 From: Napalys Klicius Date: Fri, 11 Apr 2025 11:04:08 +0200 Subject: [PATCH 14/14] Update javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll Co-authored-by: Asger F --- .../lib/semmle/javascript/internal/flow_summaries/Strings.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll index 8c8ab1ac4acf..d18e21819653 100644 --- a/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll +++ b/javascript/ql/lib/semmle/javascript/internal/flow_summaries/Strings.qll @@ -108,7 +108,7 @@ class StringFromCharCode extends SummarizedCallable { } override predicate propagatesFlow(string input, string output, boolean preservesValue) { - preservesValue = true and + preservesValue = false and ( input = "Argument[0..]" and output = "ReturnValue"