{"record":{"id":"8bfd149cab25c2c1","repo":"abhigyanpatwari/GitNexus","slug":"path-must-not-be-empty","errorCode":null,"errorMessage":"Path must not be empty","messagePattern":"Path must not be empty","errorType":"exception","errorClass":"BadRequestError","httpStatus":400,"severity":"error","filePath":"gitnexus/src/server/validation.ts","lineNumber":79,"sourceCode":"    }\n    throw new BadRequestError(`Parameter \"${fieldName}\" must be a string`);\n  }\n  return value;\n}\n\n/**\n * Resolve a user-supplied relative path against an allowed root and verify it\n * stays inside that root. Mirrors the existing guard at api.ts:1067-1077.\n *\n * Returns the absolute resolved path. Rejects empty paths, null bytes, and\n * paths that resolve outside the root (e.g., `../../../etc/passwd`).\n *\n * @throws BadRequestError when the path is empty or contains a null byte\n * @throws ForbiddenError when the resolved path escapes the root\n */\nexport function assertSafePath(rawPath: string, root: string): string {\n  if (rawPath.length === 0) {\n    throw new BadRequestError('Path must not be empty');\n  }\n  if (rawPath.includes('\\0')) {\n    throw new BadRequestError('Path must not contain null bytes');\n  }\n  const resolvedRoot = path.resolve(root);\n  const fullPath = path.resolve(resolvedRoot, rawPath);\n  const safePrefix = resolvedRoot.endsWith(path.sep) ? resolvedRoot : resolvedRoot + path.sep;\n  if (fullPath !== resolvedRoot && !fullPath.startsWith(safePrefix)) {\n    throw new ForbiddenError('Path traversal denied');\n  }\n  return fullPath;\n}\n\n/**\n * Escape regex metacharacters in a user-supplied string so it can be safely\n * embedded as a literal in `new RegExp(...)`. Used by /api/grep's literal mode\n * and any future endpoint that constructs a regex from caller input.\n */","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/aac7515d2a8c50a1f8f923c6fb77218b333560d6/gitnexus/src/server/validation.ts#L61-L97","documentation":"assertSafePath resolves a user-supplied relative path against an allowed root and verifies containment. It rejects the empty string up front with 400 because an empty path is almost always a missing or defaulted parameter rather than a meaningful target — and passing '' on to path.resolve/fs calls yields surprising behavior instead of a clean error.","triggerScenarios":"Calling an endpoint that takes a relative path parameter with path= (empty value), or a client that defaults the field to '' and sends it anyway instead of omitting it.","commonSituations":"Optional-path UI controls initialized to '' instead of null/undefined; template strings like `${base}${rest}` with rest empty; forms submitting untouched inputs.","solutions":["Omit the parameter when there is no path, or send '.' if the root itself is the intended target","Gate the request client-side on the field being non-empty after trim","Initialize optional path state to null/undefined, never ''"],"exampleFix":"// before\nconst url = `/api/file?path=${encodeURIComponent(filePath ?? '')}`;\n\n// after\nconst params = new URLSearchParams();\nif (filePath) params.set('path', filePath);\nconst url = `/api/file${params.size ? `?${params}` : ''}`;","handlingStrategy":"validation","validationCode":"const p = (pathParam ?? '').trim();\nif (!p) {\n  // omit the parameter instead of sending an empty string\n  delete params.path;\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Initialize optional path inputs to null/undefined, never ''","Trim and check emptiness before building the request URL","Send '.' when the root itself is the intended target"],"tags":["validation","path","http-400"],"backgroundTag":"path-validation-failed","analyzedSha":"aac7515d2a8c50a1f8f923c6fb77218b333560d6","analyzedAt":"2026-08-20T23:29:22.980Z","schemaVersion":2},"datasetVersion":"2026-08-22T14:17:55.899Z"}