Unauthenticated single-request DoS: unhandled exception in path validation crashes the entire process
Nobody has claimed this yet.
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 76/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- javascript, node.js
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.
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.
- Dominant language
- JavaScript
- Stars
- 109
- Forks
- 20
- Avg merge
- 1m
- Merged PRs (30d)
- 1
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.
More from 11ty/dev-server
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
11ty/dev-server#152 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
11ty/dev-server#147 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
11ty/dev-server#128 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
11ty/dev-server#95 ·
-
Difficulty 4/5 3-5 days Newbie friendliness 48/100
11ty/dev-server#151 ·
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
HarperFast/skills#96 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
Automattic/studio#4908 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
sugarlabs/musicblocks#8847 ·
-
client-controller-update ta-bot-triage team-money-movement
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
MetaMask/metamask-mobile#36594 ·