{"record":{"id":"55615a805e4dba59","repo":"can1357/oh-my-pi","slug":"absolute-paths-are-not-allowed-in-scheme-url","errorCode":null,"errorMessage":"Absolute paths are not allowed in ${scheme}:// URLs: ${rawPath}","messagePattern":"Absolute paths are not allowed in (.+?):// URLs: (.+?)","errorType":"validation","errorClass":"ToolError","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/eval/js/shared/helpers.ts","lineNumber":133,"sourceCode":"\tif (!root) {\n\t\tthrow new ToolError(`Protocol paths are not supported by ${op}(): ${rawPath}`);\n\t}\n\treturn resolveUnderRoot(scheme, root, match[2], rawPath);\n}\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);","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/eval/js/shared/helpers.ts#L115-L151","documentation":"Internal-URL paths are treated as relative to the scheme's registered root; absolute paths inside a `scheme://` URL are rejected by `resolveUnderRoot` because they would bypass root confinement. This is a sandbox security boundary: only paths relative to the mounted root are legal.","triggerScenarios":"Calling read()/write() with paths like `local:///etc/passwd` or `local://C:/data/file.txt` — the portion after `scheme://` begins with `/` (or is a Windows drive path) after decoding and backslash normalization.","commonSituations":"Prepending `/` out of habit when constructing scheme URLs; mixing ordinary absolute file paths with protocol syntax; generated code that joins scheme prefix with an absolute path variable.","solutions":["Drop the leading slash: use `local://report.md` instead of `local:///report.md`.","For genuine absolute filesystem locations, pass the absolute path directly (no scheme:// prefix) instead of an internal URL.","Express the target relative to the scheme's mounted root directory."],"exampleFix":"// before\nawait read(\"local:///etc/passwd\");\n// after\nawait read(\"/etc/passwd\"); // or a root-relative: await read(\"local://reports/q3.md\");","handlingStrategy":"validation","validationCode":"const rel = url.replace(/^\\w+:\\/\\//, \"\").replaceAll(\"\\\\\", \"/\");\nif (rel.startsWith(\"/\") || /^[A-Za-z]:/.test(rel)) throw new Error(\"absolute path not allowed in scheme URL\");","typeGuard":null,"tryCatchPattern":"try {\n\treturn await read(url);\n} catch (err) {\n\tif (String(err?.message).startsWith(\"Absolute paths are not allowed\")) {\n\t\tconst rel = url.replace(/^\\w+:\\/\\/+/, \"\");\n\t\treturn await read(`${url.slice(0, url.indexOf(\"://\"))}://${rel.replace(/^\\/+/, \"\")}`);\n\t}\n\tthrow err;\n}","preventionTips":["Never prefix scheme:// paths with `/`.","Keep absolute filesystem paths and internal URLs as separate, non-mixed forms.","When joining a scheme with a variable path, strip leading slashes first."],"tags":["path-traversal","security","sandbox","validation"],"backgroundTag":"absolute-path-in-protocol-url","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}