{"record":{"id":"6567c2c85ddfa98b","repo":"can1357/oh-my-pi","slug":"path-traversal-is-not-allowed-in-omp-urls","errorCode":null,"errorMessage":"Path traversal (..) is not allowed in omp:// URLs","messagePattern":"Path traversal \\(\\.\\.\\) is not allowed in omp:// URLs","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/omp-protocol.ts","lineNumber":65,"sourceCode":"\t\tconst content = `# Documentation\\n\\n${filenames.length} files available:\\n\\n${listing}\\n`;\n\n\t\treturn {\n\t\t\turl: url.href,\n\t\t\tcontent,\n\t\t\tcontentType: \"text/markdown\",\n\t\t\tsize: Buffer.byteLength(content, \"utf-8\"),\n\t\t};\n\t}\n\n\tasync #readDoc(filename: string, url: InternalUrl): Promise<InternalResource> {\n\t\t// Validate: no traversal, no absolute paths\n\t\tif (path.isAbsolute(filename)) {\n\t\t\tthrow new Error(\"Absolute paths are not allowed in omp:// URLs\");\n\t\t}\n\n\t\tconst normalized = path.posix.normalize(filename.replaceAll(\"\\\\\", \"/\"));\n\t\tif (normalized === \"..\" || normalized.startsWith(\"../\") || normalized.includes(\"/../\")) {\n\t\t\tthrow new Error(\"Path traversal (..) is not allowed in omp:// URLs\");\n\t\t}\n\n\t\tconst docPath =\n\t\t\tnormalized === \"docs\" ? \"\" : normalized.startsWith(\"docs/\") ? normalized.slice(\"docs/\".length) : normalized;\n\t\tif (!docPath) {\n\t\t\treturn this.#listDocs(url);\n\t\t}\n\n\t\tconst content = await getEmbeddedDoc(docPath);\n\t\tif (content === undefined) {\n\t\t\tconst lookup = docPath.replace(/\\.md$/, \"\");\n\t\t\tconst suggestions = getDocFilenames()\n\t\t\t\t.filter(f => f.includes(lookup) || lookup.includes(f.replace(/\\.md$/, \"\")))\n\t\t\t\t.slice(0, 5);\n\t\t\tconst suffix =\n\t\t\t\tsuggestions.length > 0\n\t\t\t\t\t? `\\nDid you mean: ${suggestions.join(\", \")}`\n\t\t\t\t\t: \"\\nUse omp:// to list available files.\";","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/omp-protocol.ts#L47-L83","documentation":"As part of omp:// path sanitization, after normalizing the filename the handler rejects any result equal to '..' or containing '../' segments, blocking directory traversal out of the bundled docs namespace.","triggerScenarios":"Resolving omp://../secrets or omp://docs/../../etc/passwd — the normalized path escapes the docs root via '..' segments.","commonSituations":"Programmatically joining user input into an omp URL; untrusted config files embedding traversal paths; URL-encoding tricks that decode to '..'.","solutions":["Remove '..' segments and reference the doc file by its plain name within docs/.","Sanitize/normalize user-supplied names before embedding them into omp:// URLs.","Use omp:// with no path to list valid doc filenames."],"exampleFix":"// before\nrouter.resolve(`omp://${userInput}`);\n// after\nconst safe = path.posix.normalize(userInput.replaceAll('\\\\', '/'));\nif (safe === '..' || safe.startsWith('../') || safe.includes('/../')) throw new Error('invalid doc name');\nrouter.resolve(`omp://${safe}`);","handlingStrategy":"validation","validationCode":"const normalized = path.posix.normalize(input.replaceAll('\\\\', '/'));\nif (normalized === '..' || normalized.startsWith('../') || normalized.includes('/../')) throw new Error('traversal not allowed in omp:// URL');","typeGuard":"const isTraversalSafe = (s: string): boolean => {\n  const n = path.posix.normalize(s.replaceAll('\\\\', '/'));\n  return n !== '..' && !n.startsWith('../') && !n.includes('/../');\n};","tryCatchPattern":"try {\n  return await router.resolve(url);\n} catch (err) {\n  if (err instanceof Error && err.message.includes('Path traversal')) throw new Error('omp:// doc names cannot contain ..');\n  throw err;\n}","preventionTips":["Normalize and reject '..' segments before constructing omp:// URLs from any user input.","Whitelist doc names against omp:// listing instead of accepting free-form paths.","URL-decode input before validating, since encoded traversal still normalizes to '..'."],"tags":["security","path-traversal","internal-url"],"backgroundTag":"path-traversal","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}