{"record":{"id":"97770f43d5500f2c","repo":"abhigyanpatwari/GitNexus","slug":"resolved-storage-path-must-remain-directly-inside-storage","errorCode":null,"errorMessage":"Resolved storage path must remain directly inside ${STORAGE_ROOT_ENV}.","messagePattern":"Resolved storage path must remain directly inside (.+?)\\.","errorType":"validation","errorClass":"InvalidStoragePathError","httpStatus":null,"severity":"error","filePath":"gitnexus/src/storage/storage-resolver.ts","lineNumber":278,"sourceCode":"  const rel = path.relative(parent, inspected);\n  if (rel.startsWith('..') || path.isAbsolute(rel)) {\n    throw new InvalidStoragePathError('Storage path escaped its parent directory.');\n  }\n  return inspected;\n};\n\n/** Resolve one repository's isolated slot under an external storage root. */\nexport const storagePathFromRoot = (rootPath: string, repoPath: string): string => {\n  const root = validateAbsolutePath(rootPath, STORAGE_ROOT_ENV);\n  const storagePath = path.resolve(root, storageSlotName(repoPath));\n  const rel = path.relative(root, storagePath);\n  if (\n    rel === '' ||\n    rel.startsWith('..') ||\n    path.isAbsolute(rel) ||\n    !samePath(path.dirname(storagePath), root)\n  ) {\n    throw new InvalidStoragePathError(\n      `Resolved storage path must remain directly inside ${STORAGE_ROOT_ENV}.`,\n    );\n  }\n  return storagePath;\n};\n\nconst configuredStoragePath = (): string | undefined => {\n  const value = process.env[STORAGE_PATH_ENV];\n  return value === undefined ? undefined : validateConfiguredStoragePath(value);\n};\n\nconst configuredStorageRoot = (repoPath: string): string | undefined => {\n  const value = process.env[STORAGE_ROOT_ENV];\n  return value === undefined ? undefined : storagePathFromRoot(value, repoPath);\n};\n\nconst registeredStoragePath = (repoPath: string): string | undefined => {\n  let entries: unknown[];","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/abhigyanpatwari/GitNexus/blob/ac9a4e9abd8fd3058c070b72c23402a4f887929a/gitnexus/src/storage/storage-resolver.ts#L260-L296","documentation":"storagePathFromRoot derives one repository's isolated slot under an external storage root and verifies the result is a single-level child: the relative path must be non-empty, not climb with '..', not absolute, and the slot's dirname must equal the root. Any violation throws InvalidStoragePathError 'Resolved storage path must remain directly inside GITNEXUS_STORAGE_ROOT'. This prevents a hostile repoPath from constructing slot paths deeper than or outside the configured root.","triggerScenarios":"Calling storagePathFromRoot(rootPath, repoPath) where the derived slot name (from the repo path) yields '' , '..' segments, or a nested path so that path.dirname(slot) !== root — e.g. a repoPath that encodes separators/slashes into the slot name.","commonSituations":"Unusual repository paths (URLs, remote refs, names containing slashes) hashed/encoded into slot names incorrectly; passing a root with trailing separator or a non-resolved root; upgrading between versions that changed slot-name encoding.","solutions":["Ensure rootPath is itself validated (run validateConfiguredStoragePath/root on it) before deriving slots.","Check the repoPath input: canonicalize it (path.resolve + realpath) so the slot name derivation is stable and single-level.","Log the computed storagePath and root on failure; if the encoding scheme produces separators, pre-hash the repo path (e.g. slug + hash) before deriving the slot."],"exampleFix":"// before\nconst slot = storagePathFromRoot(rawEnvRoot, remoteRepoUrl);\n// after\nconst slot = storagePathFromRoot(validateConfiguredStoragePath(rawEnvRoot), path.resolve(realpathSync(repoDir)));","handlingStrategy":"validation","validationCode":"const root = validateConfiguredStoragePath(rootPath);\nconst repoCanon = path.resolve(fs.realpathSync(repoPath));\nconst slot = storagePathFromRoot(root, repoCanon);\nif (path.dirname(slot) !== root) throw new Error('slot must be a direct child of root');","typeGuard":"const isDirectChild = (root: string, slot: string): boolean =>\n  path.dirname(path.resolve(slot)) === path.resolve(root);","tryCatchPattern":"try {\n  return storagePathFromRoot(root, repoPath);\n} catch (e) {\n  if (e instanceof InvalidStoragePathError && e.message.includes('directly inside')) {\n    throw new Error(`slot derivation left storage root — canonicalize repoPath and re-validate root`);\n  }\n  throw e;\n}","preventionTips":["Always pass an already-validated storage root into storagePathFromRoot.","Canonicalize (realpath) repo paths before slot derivation.","Test slot derivation with adversarial repo names (slashes, '..', unicode)."],"tags":["storage-root","path-traversal","validation"],"backgroundTag":"path-traversal-blocked","analyzedSha":"ac9a4e9abd8fd3058c070b72c23402a4f887929a","analyzedAt":"2026-09-15T23:29:44.066Z","contentChangedAt":"2026-09-15T23:29:44.066Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}