{"record":{"id":"8c41b3e57a83dde5","repo":"stablyai/orca","slug":"access-denied-invalid-worktree-path-8c41b3","errorCode":null,"errorMessage":"Access denied: invalid worktree path","messagePattern":"Access denied: invalid worktree path","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/hosted-review.ts","lineNumber":73,"sourceCode":"      throw new Error('Access denied: worktree does not belong to repository')\n    }\n    return remoteWorktreePath\n  }\n  const resolvedWorktreePath = await resolveRegisteredWorktreePath(worktreePath, store)\n  const localGitOptions = getLocalProjectWorktreeGitOptions(store, repo)\n  const repoWorktrees =\n    Object.keys(localGitOptions).length > 0\n      ? await listRepoWorktrees(repo, localGitOptions)\n      : await listRepoWorktrees(repo)\n  if (!repoWorktrees.some((worktree) => resolve(worktree.path) === resolvedWorktreePath)) {\n    throw new Error('Access denied: worktree does not belong to repository')\n  }\n  return resolvedWorktreePath\n}\n\nfunction normalizeRemoteHostedReviewPath(remotePath: string): string {\n  if (!remotePath || remotePath.includes('\\0')) {\n    throw new Error('Access denied: invalid worktree path')\n  }\n  // Why: SSH worktree paths belong to the remote POSIX host. Local path.resolve\n  // rewrites them on Windows and cannot authorize remote-only paths.\n  const normalized = posix.normalize(remotePath)\n  return normalized.length > 1 ? normalized.replace(/\\/+$/, '') : normalized\n}\n\nexport function registerHostedReviewHandlers(store: Store, stats: StatsCollector): void {\n  ipcMain.handle('hostedReview:forBranch', async (_event, args: HostedReviewForBranchArgs) => {\n    const repo = assertRegisteredRepo(args.repoPath, store, args.repoId)\n    const localGitOptions = getLocalProjectWorktreeGitOptions(store, repo)\n    const review = await getHostedReviewForBranch({\n      repoPath: repo.path,\n      connectionId: repo.connectionId,\n      branch: args.branch,\n      linkedGitHubPR: args.linkedGitHubPR ?? null,\n      fallbackGitHubPR: args.linkedGitHubPR == null ? (args.fallbackGitHubPR ?? null) : null,\n      linkedGitLabMR: args.linkedGitLabMR ?? null,","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/hosted-review.ts#L55-L91","documentation":"Thrown inside normalizeRemoteHostedReviewPath when the supplied remote worktree path is falsy (empty/null) or contains a NUL byte (\\0). The NUL-byte check is a path-traversal / injection guard: NUL bytes can terminate or manipulate paths in lower-level systems. This runs before any authorization comparison, so it rejects malformed input early.","triggerScenarios":"Passing an empty string, null, or undefined as worktreePath to a remote hosted-review call. Embedding a \\0 byte in the path string (e.g. via crafted or corrupted input). Any code path where worktreePath arrives unvalidated from untrusted source over IPC.","commonSituations":"Renderer sends an empty worktreePath when no worktree is selected but the field is not optional. Deserialized/cached state containing a corrupted string. Adversarial IPC input attempting path injection.","solutions":["Ensure worktreePath is a non-empty string with no NUL bytes before calling the IPC method.","Treat an empty worktreePath explicitly: pass the repo default path or skip the call rather than forwarding an empty value.","Sanitize or reject input containing control characters at the renderer boundary."],"exampleFix":"// before\nconst path = maybeWorktree?.path ?? ''\nawait call(worktreePath: path)\n\n// after\nif (!worktreePath || worktreePath.includes('\\0')) {\n  throw new Error('A valid worktree path is required')\n}\nawait call(worktreePath)","handlingStrategy":"validation","validationCode":"if (!worktreePath || typeof worktreePath !== 'string' || worktreePath.includes('\\0')) {\n  throw new Error('A valid non-empty worktree path is required')\n}","typeGuard":"function isValidRemoteWorktreePath(value: unknown): value is string {\n  return typeof value === 'string' && value.length > 0 && !value.includes('\\0')\n}","tryCatchPattern":null,"preventionTips":["Never forward empty/null worktreePath to remote IPC calls.","Reject control characters (including NUL) at the renderer boundary.","Treat missing worktree selection as 'use repo default', not empty string."],"tags":["security","validation","path-injection","worktree","ipc"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}