{"record":{"id":"9a1ef3079470ab34","repo":"abhigyanpatwari/GitNexus","slug":"upload-path-must-not-contain-traversal-segments","errorCode":null,"errorMessage":"Upload path must not contain traversal segments","messagePattern":"Upload path must not contain traversal segments","errorType":"http","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"gitnexus/src/server/upload-ingest.ts","lineNumber":91,"sourceCode":"  // webkitRelativePath is always relative; a leading slash is absolute/hostile.\n  if (rel.startsWith('/')) {\n    throw new BadRequestError('Invalid upload path');\n  }\n  // Browsers emit forward slashes only; a NUL byte or backslash is hostile.\n  if (rel.includes('\\u0000') || rel.includes('\\\\')) {\n    throw new BadRequestError('Invalid upload path');\n  }\n  const rawSegments = rel.split('/').filter((s) => s.length > 0);\n  if (rawSegments.length === 0 || rawSegments.length > MAX_PATH_DEPTH) {\n    throw new BadRequestError('Invalid upload path');\n  }\n  const segments: string[] = [];\n  for (const seg of rawSegments) {\n    // Normalize so NFC/NFD variants don't collide silently on case/unicode\n    // -folding filesystems (macOS/Windows).\n    const s = seg.normalize('NFC');\n    if (s === '.' || s === '..') {\n      throw new BadRequestError('Upload path must not contain traversal segments');\n    }\n    segments.push(s);\n  }\n  const dest = path.resolve(stageRoot, segments.join(path.sep));\n  // Suffix path.sep so a sibling prefix (/sandbox-evil vs /sandbox) can't pass.\n  const safePrefix = stageRoot.endsWith(path.sep) ? stageRoot : stageRoot + path.sep;\n  if (dest !== stageRoot && !dest.startsWith(safePrefix)) {\n    throw new BadRequestError('Upload path escapes the sandbox');\n  }\n  return dest;\n}\n\ninterface DirState {\n  dirCount: number;\n  limits: IngestLimits;\n}\n\n/**","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/upload-ingest.ts#L73-L109","documentation":"After splitting and NFC-normalizing each segment, resolveContainedDest rejects any segment equal to '.' or '..' with 'Upload path must not contain traversal segments'. This is the direct anti-traversal rule: even though a later resolve-then-contain check would also catch escapes, '.'/'..' segments are unambiguous traversal syntax and are refused explicitly, including post-normalization forms.","triggerScenarios":"POST multipart ingest with manifest paths like '../../etc/cron.d/x', 'src/../../../secrets', or 'a/./b' — any entry that yields a '.' or '..' segment after splitting on '/'. NFC normalization runs first so unicode tricks cannot smuggle a dot-dot equivalent.","commonSituations":"Classic path-traversal attack on the upload endpoint; a client that preserves '../' prefixes from paths outside the picked folder; test suites asserting OWASP File Upload coverage; accidentally built manifests that include parent references from relative-path math.","solutions":["Send only paths as the browser reports them — webkitRelativePath never contains '..' because the picker roots at the chosen folder","Strip traversal client-side if you compute paths: segments.filter(s => s && s !== '.' && s !== '..').join('/')","In security tests, keep '../..' and 'a/./b' cases asserting HTTP 400 'Upload path must not contain traversal segments'"],"exampleFix":"// before — client computes paths relative to CWD\nentry.path = path.relative(projectRoot, file.path); // may yield '../../downloads/x'\n// after\nentry.path = file.webkitRelativePath || file.name; // rooted at the picked folder","handlingStrategy":"type-guard","validationCode":"// Strip traversal segments before sending\nentry.path = entry.path\n  .split('/')\n  .filter((s) => s && s !== '.' && s !== '..')\n  .join('/');","typeGuard":"function isTraversalFreeUploadPath(rel: string): boolean {\n  return rel\n    .split('/')\n    .every((s) => s.normalize('NFC') !== '.' && s.normalize('NFC') !== '..');\n}","tryCatchPattern":null,"preventionTips":["Use webkitRelativePath verbatim — the folder picker roots paths at the chosen folder so '..' cannot appear","Never compute manifest paths with path.relative against locations outside the picked folder","Keep '../..' payloads in your security test corpus asserting HTTP 400"],"tags":["upload","path-traversal","dotdot-segments","validation","security-guard"],"backgroundTag":"upload-path-validation-failed","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T20:17:22.307Z"}