{"record":{"id":"efa04202fb4f5445","repo":"stablyai/orca","slug":"access-denied-git-file-path-escapes-the-selected","errorCode":null,"errorMessage":"Access denied: git file path escapes the selected worktree","messagePattern":"Access denied: git file path escapes the selected worktree","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/main/ipc/filesystem-auth.ts","lineNumber":537,"sourceCode":"async function normalizeExistingPath(resolvedPath: string): Promise<string> {\n  try {\n    return resolve(await realpath(resolvedPath))\n  } catch (error) {\n    if (isENOENT(error)) {\n      return resolvedPath\n    }\n    throw error\n  }\n}\n\nexport function validateGitRelativeFilePath(worktreePath: string, filePath: string): string {\n  if (!filePath || filePath.includes('\\0') || resolve(filePath) === filePath) {\n    throw new Error('Access denied: invalid git file path')\n  }\n\n  const resolvedFilePath = resolve(worktreePath, filePath)\n  if (!isDescendantOrEqual(resolvedFilePath, worktreePath)) {\n    throw new Error('Access denied: git file path escapes the selected worktree')\n  }\n\n  const normalizedRelativePath = relative(worktreePath, resolvedFilePath)\n  if (!normalizedRelativePath) {\n    throw new Error('Access denied: invalid git file path')\n  }\n\n  return normalizedRelativePath\n}\n","sourceCodeStart":519,"sourceCodeEnd":547,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/filesystem-auth.ts#L519-L547","documentation":"Containment check in `validateGitRelativeFilePath`: after joining, `isDescendantOrEqual(resolvedFilePath, worktreePath)` is false, meaning the resolved path is not within the worktree. This catches relative traversal such as `../../etc/passwd` that survives the join but lands outside the worktree root.","triggerScenarios":"Calling `validateGitRelativeFilePath(worktreePath, filePath)` with a relative path containing `..` segments that resolve above the worktree root, e.g. `../../secrets.env`.","commonSituations":"User- or renderer-supplied path with parent-directory traversal; a diff tool returning paths outside the worktree; symlinked content whose relative path resolves above the root; mis-computed relative path from a wrong base.","solutions":["Sanitize the path to remove `..` segments, or reject any path that would resolve above the worktree root.","Compute the relative path using `path.relative(worktreePath, absPath)` and verify the result does not start with `..`.","Reject renderer-supplied paths that contain `..` at the trust boundary."],"exampleFix":"// before\nvalidateGitRelativeFilePath(worktreePath, '../../etc/passwd')\n\n// after\nconst rel = relative(worktreePath, resolve(worktreePath, userInput))\nif (rel.startsWith('..')) throw new Error('path escapes worktree')\nvalidateGitRelativeFilePath(worktreePath, rel)","handlingStrategy":"validation","validationCode":"// Strip/verify no parent traversal before validating.\nconst candidate = resolve(worktreePath, filePath)\nconst rel = relative(worktreePath, candidate)\nif (rel.startsWith('..')) {\n  throw new Error('path escapes the worktree')\n}\nreturn validateGitRelativeFilePath(worktreePath, filePath)","typeGuard":"function staysInsideWorktree(worktreePath: string, filePath: string): boolean {\n  const rel = relative(worktreePath, resolve(worktreePath, filePath))\n  return rel !== '' && !rel.startsWith('..')\n}","tryCatchPattern":"try {\n  return validateGitRelativeFilePath(worktreePath, filePath)\n} catch (e) {\n  if (e instanceof Error && e.message === 'Access denied: git file path escapes the selected worktree') {\n    // contained `..` traversal; reject the renderer-supplied path\n    throw new PathTraversalError(e.message)\n  }\n  throw e\n}","preventionTips":["Reject any renderer-supplied path containing `..` segments at the trust boundary.","Compute paths via path.relative(worktreePath, abs) and verify the result does not start with `..`.","Treat path-escape as a security event, not a recoverable input error."],"tags":["filesystem","security-boundary","path-traversal","git","authorization"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}