different-ai/openwork · warning · GatewayHttpError
path_escape
path_escape
Error message
Path escapes web root
What it means
The Den gateway serves static files from a configured web root. resolveWithinRoot resolves requested path segments against the root, follows symlinks via realpath, and throws a 400 GatewayHttpError (code 'path_escape', message 'Path escapes web root') when the resolved candidate is not the root itself and does not remain inside it. This blocks path-traversal requests like /../../etc/passwd.
Source
Thrown at ee/apps/den-gateway/src/app.ts:291
function requestPathToRelativePath(pathname: string): string | null {
try {
const decoded = decodeURIComponent(pathname)
return decoded.replace(/^\/+/, "") || "index.html"
} catch {
return null
}
}
async function resolveWithinRoot(root: string, ...segments: string[]) {
const resolvedRoot = await realpath(root)
const candidate = resolve(resolvedRoot, ...segments)
const resolvedCandidate = await realpath(candidate).catch(() => candidate)
if (resolvedCandidate === resolvedRoot) {
return candidate
}
if (!resolvedCandidate.startsWith(resolvedRoot + sep)) {
throw new GatewayHttpError(400, "path_escape", "Path escapes web root")
}
return candidate
}
function contentType(extension: string) {
if (extension === ".html") {
return "text/html; charset=utf-8"
}
if (extension === ".js" || extension === ".mjs") {
return "text/javascript; charset=utf-8"
}
if (extension === ".css") {
return "text/css; charset=utf-8"
}
if (extension === ".json" || extension === ".map") {
return "application/json; charset=utf-8"
}
if (extension === ".svg") {View on GitHub (pinned to 2b7df46e8a)
Solutions
- Remove or rewrite request paths containing '..' or encoded traversal segments before they reach the gateway
- Ensure DEN_GATEWAY_WEB_ROOT points at the directory actually containing the assets being requested
- Move or re-point any symlink inside the web root so targets live under the root
- Serve additional content by copying it into the web root or adding an explicit proxied route, not via symlinks
Example fix
// before
fetch('/static/../../etc/passwd') // path_escape
// after
const safe = requestedPath.replaceAll(/(\.\.\/)+/g, '')
fetch(`/static/${encodeURIComponent(safe)}`) Defensive patterns
Strategy: try-catch
Validate before calling
function staysWithinRoot(root: string, segments: string[]) {
const resolved = resolve(resolve(root), ...segments)
return resolved === resolve(root) || resolved.startsWith(resolve(root) + sep)
} Type guard
null
Try / catch
import { GatewayHttpError } from './http-error.js'
try {
const file = await filePath(segments)
} catch (err) {
if (err instanceof GatewayHttpError && err.code === 'path_escape') {
return new Response('Not Found', { status: 404 }) // don't leak traversal attempts
}
throw err
} Prevention
- Normalize/reject URL segments containing '..' at the proxy or router edge
- Keep all served assets physically inside DEN_GATEWAY_WEB_ROOT — avoid outbound symlinks
- Log path_escape occurrences; bursts indicate probing
- Return 404 (not 400 details) to external clients to avoid leaking filesystem layout
When it happens
Trigger: An HTTP request whose URL-encoded segments (../, %2e%2e/, absolute paths) resolve outside DEN_GATEWAY_WEB_ROOT, or a symlink inside the web root pointing to a file outside it.
Common situations: Attackers probing for traversal vulnerabilities; misconfigured reverse proxies forwarding raw '..' segments; a developer symlinking a shared asset directory that lives outside the web root; Windows/macOS case or symlink surprises after deployment.
Related errors
- path_escape
- Archive contains an unsafe path
- External URL protocol "${parsed.protocol}" is not allowed.
- ${result}
- Failed to write .opencode/openwork.json
AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01).
Data as JSON: /api/errors/d539bfc3478e3d5a.
Report an issue: GitHub.