{"record":{"id":"5c39868e917473e9","repo":"honojs/hono","slug":"path-traversal-detected-filepath-is-outside","errorCode":null,"errorMessage":"Path traversal detected: \"${filePath}\" is outside the output directory","messagePattern":"Path traversal detected: \"(.+?)\" is outside the output directory","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/helper/ssg/utils.ts","lineNumber":116,"sourceCode":"}\n\nexport const ensureWithinOutDir = (outDir: string, filePath: string): void => {\n  const outDirSegments = toSegments(joinPaths(outDir))\n  const filePathSegments = toSegments(joinPaths(filePath))\n\n  const hasMismatchedPathRoot = getPathRoot(outDir) !== getPathRoot(filePath)\n\n  // `joinPaths` collects every remaining `..` at the head, so a `..` right after\n  // the outDir segments means the file path climbs above outDir\n  const climbsAboveOutDir = filePathSegments[outDirSegments.length] === '..'\n\n  if (\n    hasMismatchedPathRoot ||\n    filePathSegments.length <= outDirSegments.length ||\n    !outDirSegments.every((segment, i) => segment === filePathSegments[i]) ||\n    climbsAboveOutDir\n  ) {\n    throw new Error(`Path traversal detected: \"${filePath}\" is outside the output directory`)\n  }\n}\n","sourceCodeStart":98,"sourceCodeEnd":119,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/helper/ssg/utils.ts#L98-L119","documentation":"A security check in the SSG utils: before writing a generated page to disk, it verifies the resolved output file path is still inside the configured output directory. If the route path resolves outside outDir — via ../ segments, absolute paths, or mismatched path roots (e.g. Windows drive vs UNC) — it throws rather than writing outside the directory.","triggerScenarios":"A route whose path (after joining with outDir and normalizing) escapes the output directory: paths containing encoded ../, routes generating absolute file names, or outDir/root mismatch where segment prefixes don't align (filePathSegments not starting with outDirSegments, or fewer segments than outDir).","commonSituations":"Dynamic routes using user-supplied or URL-decoded segments (e.g. /docs/:slug with slug='../..'); passing a wrong (relative vs absolute, trailing-slash) outDir to toSSG; Windows path-root mismatches between outDir and generated file path; attempts/scan payloads targeting the SSG writer.","solutions":["Sanitize route segments before generating pages: strip '..', leading slashes, and decode-then-validate slugs","Pass a clean absolute outDir to toSSG and ensure generated paths are joined relative to it","If intentional nesting is needed, configure outDir to a common parent containing all outputs","Add a check comparing path.resolve(outDir, filePath) prefix with path.resolve(outDir) before calling the SSG writer"],"exampleFix":"// before\napp.get('/docs/:slug', (c) => c.html(render(c.req.param('slug'))))\n// slug = '..%2F..%2Fetc' -> Path traversal detected\n\n// after\napp.get('/docs/:slug', (c) => {\n  const slug = c.req.param('slug').replace(/\\.\\./g, '').replace(/^[\\/\\\\]+/, '')\n  if (!slug) return c.notFound()\n  return c.html(render(slug))\n})","handlingStrategy":"validation","validationCode":"import path from 'node:path'\n\nfunction isWithinOutDir(outDir: string, filePath: string): boolean {\n  const rel = path.relative(path.resolve(outDir), path.resolve(outDir, filePath))\n  return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel)\n}","typeGuard":"const isSafeSegment = (s: string): boolean => /^[\\w.-]+$/.test(s) && !s.includes('..')","tryCatchPattern":"try { await toSSG(app, fs, { outDir }) } catch (e) { if (e instanceof Error && e.message.includes('Path traversal detected')) { /* sanitize slugs/routes and re-run */ } throw e }","preventionTips":["Sanitize dynamic route params: reject/strip '..' and absolute prefixes before using them as filenames","Use simple slugs (id/hash) as file names, never raw user input","Keep outDir absolute and consistent; verify generated path prefixes on Windows"],"tags":["ssg","path-traversal","security","filesystem","sanitization"],"backgroundTag":"path-traversal-detected","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}