{"record":{"id":"50b84f11743d443f","repo":"can1357/oh-my-pi","slug":"path-traversal-is-not-allowed-in-scheme","errorCode":null,"errorMessage":"Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath}","messagePattern":"Path traversal \\(\\.\\.\\) is not allowed in (.+?):// URLs: (.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/helpers.ts","lineNumber":137,"sourceCode":"}\n\n/** Resolve an internal-URL relative path under its root, mirroring the host\n *  local-protocol handler: decode, reject absolute/traversal, confine to root. */\nfunction resolveUnderRoot(scheme: string, root: string, rawRelative: string, rawPath: string): string {\n\tlet relative: string;\n\ttry {\n\t\trelative = decodeURIComponent(rawRelative.replaceAll(\"\\\\\", \"/\"));\n\t} catch {\n\t\tthrow new ToolError(`Invalid URL encoding in ${scheme}:// path: ${rawPath}`);\n\t}\n\tconst rootPath = path.resolve(root);\n\tif (relative === \"\") return rootPath;\n\tif (path.isAbsolute(relative)) {\n\t\tthrow new ToolError(`Absolute paths are not allowed in ${scheme}:// URLs: ${rawPath}`);\n\t}\n\tconst normalized = path.normalize(relative);\n\tif (normalized.startsWith(\"..\") || normalized.includes(\"/../\") || normalized.includes(\"/..\")) {\n\t\tthrow new ToolError(`Path traversal (..) is not allowed in ${scheme}:// URLs: ${rawPath}`);\n\t}\n\tconst resolved = path.resolve(rootPath, normalized);\n\tif (resolved !== rootPath && !resolved.startsWith(`${rootPath}${path.sep}`)) {\n\t\tthrow new ToolError(`${scheme}:// path escapes its root: ${rawPath}`);\n\t}\n\treturn resolved;\n}\n\nasync function resolveRegularFile(\n\tctx: HelperContext,\n\trawPath: string,\n): Promise<{ filePath: string; file: Bun.BunFile; size: number }> {\n\tconst filePath = resolveHelperPath(ctx, rawPath, \"read\");\n\tconst file = Bun.file(filePath);\n\tconst stat = await file.stat();\n\tif (stat.isDirectory()) {\n\t\tthrow new ToolError(`Directory paths are not supported by read(): ${filePath}`);\n\t}","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/helpers.ts#L119-L155","documentation":"`resolveUnderRoot` normalizes the decoded relative path and rejects any result containing `..` segments (leading `..`, `/../`, or trailing `/..`). This blocks parent-directory traversal inside `scheme://` URLs, keeping sandbox file access confined to the mounted root — a deliberate security guard, not a bug.","triggerScenarios":"Calling read()/write() with traversal paths such as `local://../secrets.txt`, `local://a/../../etc/passwd`, or `local://dir/..%2f..%2fx` (decodes to `..` segments).","commonSituations":"LLM-generated eval code attempting to reach files outside the artifacts root; constructing paths by concatenating untrusted input; attempting to walk up from the session's mounted directory.","solutions":["Reference files within the mounted root using paths with no `..` segments.","Ask the host to mount the directory you actually need as an additional localRoot instead of traversing up.","Use absolute plain paths only if the environment permits them and drop the scheme:// form."],"exampleFix":"// before\nconst s = await read(\"local://../project/config.json\");\n// after\nconst s = await read(\"/abs/path/project/config.json\"); // or mount project as a root and use local://project/config.json","handlingStrategy":"validation","validationCode":"const rel = decodeURIComponent(url.replace(/^\\w+:\\/\\//, \"\").replaceAll(\"\\\\\", \"/\"));\nif (rel.split(\"/\").includes(\"..\")) throw new Error(\"traversal not allowed\");","typeGuard":null,"tryCatchPattern":"try {\n\treturn await read(url);\n} catch (err) {\n\tif (String(err?.message).includes(\"Path traversal\")) {\n\t\t// do not sanitize-and-retry silently: log and surface to the user\n\t\tthrow new Error(`blocked traversal attempt: ${url}`);\n\t}\n\tthrow err;\n}","preventionTips":["Resolve and normalize relative paths against the intended root before building scheme URLs.","Reject or canonicalize any path segment equal to `..` in generated code.","Treat repeated traversal attempts from generated code as a signal to mount the correct root instead."],"tags":["path-traversal","security","sandbox","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}