{"record":{"id":"85150d15287df049","repo":"abhigyanpatwari/GitNexus","slug":"upload-path-too-long","errorCode":null,"errorMessage":"Upload path too long","messagePattern":"Upload path too long","errorType":"http","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"gitnexus/src/server/upload-ingest.ts","lineNumber":71,"sourceCode":"  stageRoot: string;\n  fileCount: number;\n  totalBytes: number;\n  /** First path segment shared by the uploaded tree (the picked folder). */\n  topLevelName: string;\n}\n\n/**\n * Resolve a client-provided relative path to an absolute destination PROVABLY\n * contained within `stageRoot`. Throws BadRequestError on any unsafe input.\n * This is the load-bearing path-traversal-on-write control; keep it pure and\n * unit-tested.\n */\nexport function resolveContainedDest(stageRoot: string, rel: unknown): string {\n  if (typeof rel !== 'string' || rel.length === 0) {\n    throw new BadRequestError('Invalid upload path');\n  }\n  if (rel.length > MAX_PATH_LENGTH) {\n    throw new BadRequestError('Upload path too long');\n  }\n  // 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');","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/upload-ingest.ts#L53-L89","documentation":"The length cap in resolveContainedDest: an upload manifest path longer than MAX_PATH_LENGTH (4096 chars) throws 'Upload path too long' before any write. The guard exists because some filesystems silently truncate or error on very long paths, and unbounded lengths are a cheap DoS/vector for the traversal checker; it runs after the type/emptiness check and before the leading-slash check.","triggerScenarios":"POST multipart ingest where a manifest webkitRelativePath exceeds 4096 characters — e.g. a fabricated manifest with a padded segment, a client bug concatenating paths cumulatively, or a hostile fuzzing payload probing the limits.","commonSituations":"Automated/fuzz clients generating pathological manifests; a client that joins folder names repeatedly (path += '/' + dir on each level); deeply nested picks on Windows where paths already approach limits; essentially never from a real browser folder picker, since OS path limits sit well below 4096.","solutions":["Fix the client to send the file's genuine webkitRelativePath (it is bounded by OS limits far below 4096)","Look for cumulative string concatenation bugs in your manifest builder (path growing per level instead of being taken from the File object)","Treat a 400 'Upload path too long' as evidence of a hostile or broken client — log the offending part's field name, not the path itself"],"exampleFix":"// before — cumulative join grows the path each level\nrel = rel + '/' + part; // may exceed 4096 across deep trees\n// after — take the browser-provided relative path once\nrel = file.webkitRelativePath || file.name;","handlingStrategy":"validation","validationCode":"const MAX_PATH_LENGTH = 4096; // mirror of the server cap\nif (entry.path.length > MAX_PATH_LENGTH) {\n  throw new Error(`manifest path too long (${entry.path.length} > ${MAX_PATH_LENGTH})`);\n}","typeGuard":"function isBoundedUploadPath(rel: string): boolean {\n  return rel.length > 0 && rel.length <= 4096;\n}","tryCatchPattern":null,"preventionTips":["Take paths from webkitRelativePath once instead of building them by cumulative concatenation","Enforce a client-side length cap matching the server's 4096 limit","Log field names (not full paths) when the server rejects an entry, to locate the broken uploader logic"],"tags":["upload","path-length","validation","limits","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"}