Unauthenticated single-request DoS: unhandled exception in path validation crashes the entire process
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 109
- Forks
- 20
- Avg merge
- 1m
- Merged PRs (30d)
- 1
Description
Summary
A single crafted HTTP GET request containing a percent-encoded path-traversal attempt (%2e%2e%2f) causes EleventyDevServer.getOutputDirFilePath() to throw an uncaught Error, which propagates all the way out of the raw http.Server request handler and crashes the entire Node.js process. No authentication, no special conditions — one request from anyone able to reach the dev server kills it for every connected user.
CWE: CWE-248 (Uncaught Exception) / CWE-400 (Uncontrolled Resource Consumption)
Severity: High
CVSS: 7.5 — CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
Root Cause
server.js:317, inside getOutputDirFilePath():
if(!this.isFileInDirectory(this.dir, computedPath)) {
throw new Error("Invalid path");
}
This is the correct rejection path for an out-of-bounds request — but nothing between here and the raw HTTP request event catches it: getOutputDirFilePath() → mapUrlToFilePath() → eleventyProjectMiddleware() → eleventyDevServerMiddleware() → onRequestHandler() → Server.emit("request"). The exception is never wrapped in a try/catch anywhere in that chain, so it reaches Node's own uncaught-exception handling and aborts the process.
Reproduction
mkdir -p /tmp/poc/_site && echo "<h1>hi</h1>" > /tmp/poc/_site/index.html
node --input-type=module -e '
import EleventyDevServer from "./server.js";
const server = EleventyDevServer.getServer("poc", "/tmp/poc/_site", { port: 9799 });
server.serve(9799);
'
$ curl -o /dev/null -w "%{http_code}\n" http://localhost:9799/index.html
200
$ curl -o /dev/null -w "%{http_code}\n" "http://localhost:9799/%2e%2e%2fsecret.txt"
# server crashes:
Error: Invalid path
at EleventyDevServer.getOutputDirFilePath (server.js:317:13)
at EleventyDevServer.mapUrlToFilePath (server.js:363:24)
at EleventyDevServer.eleventyProjectMiddleware (server.js:698:24)
at EleventyDevServer.eleventyDevServerMiddleware (server.js:650:5)
at EleventyDevServer.onRequestHandler (server.js:854:11)
Node.js v22.23.2
$ curl -o /dev/null -w "%{http_code}\n" http://localhost:9799/index.html --max-time 3
000 # process is dead, connection refused
Impact
Anyone who can send an HTTP request to a running eleventy --serve instance can kill it instantly — including a malicious page open in another browser tab making a simple fetch()/<img> request to localhost:PORT, or anyone on the same network if the dev server is bound beyond localhost (a supported, documented option). No exploitation skill required beyond a single crafted URL.
Recommended Fix
Wrap the request-handling chain (or at minimum the path-resolution call) in a try/catch that returns a normal 400/403 HTTP response instead of throwing past the request handler:
onRequestHandler(req, res) {
try {
// existing logic
} catch (e) {
res.statusCode = 400;
res.end("Bad Request");
return;
}
}
Verification
Dynamically confirmed on v3.0.0-alpha.11 via the real getServer()/serve() public API and real HTTP requests (curl), as shown above — reproduced independently twice.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in server.js at getOutputDirFilePath() around line 317 and follow the request path through onRequestHandler() around line 854. Run the provided public-API reproduction with curl, then verify that the traversal request receives a normal 400/403 response and a later request still succeeds without terminating the process.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript, node.js
- Domain
- backend, security
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 76/100