stablyai/orca · critical · Error

Access denied: git file path escapes the selected worktree

Error message

Access denied: git file path escapes the selected worktree

What it means

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.

Source

Thrown at src/main/ipc/filesystem-auth.ts:537

async function normalizeExistingPath(resolvedPath: string): Promise<string> {
  try {
    return resolve(await realpath(resolvedPath))
  } catch (error) {
    if (isENOENT(error)) {
      return resolvedPath
    }
    throw error
  }
}

export function validateGitRelativeFilePath(worktreePath: string, filePath: string): string {
  if (!filePath || filePath.includes('\0') || resolve(filePath) === filePath) {
    throw new Error('Access denied: invalid git file path')
  }

  const resolvedFilePath = resolve(worktreePath, filePath)
  if (!isDescendantOrEqual(resolvedFilePath, worktreePath)) {
    throw new Error('Access denied: git file path escapes the selected worktree')
  }

  const normalizedRelativePath = relative(worktreePath, resolvedFilePath)
  if (!normalizedRelativePath) {
    throw new Error('Access denied: invalid git file path')
  }

  return normalizedRelativePath
}

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Sanitize the path to remove `..` segments, or reject any path that would resolve above the worktree root.
  2. Compute the relative path using `path.relative(worktreePath, absPath)` and verify the result does not start with `..`.
  3. Reject renderer-supplied paths that contain `..` at the trust boundary.

Example fix

// before
validateGitRelativeFilePath(worktreePath, '../../etc/passwd')

// after
const rel = relative(worktreePath, resolve(worktreePath, userInput))
if (rel.startsWith('..')) throw new Error('path escapes worktree')
validateGitRelativeFilePath(worktreePath, rel)
Defensive patterns

Strategy: validation

Validate before calling

// Strip/verify no parent traversal before validating.
const candidate = resolve(worktreePath, filePath)
const rel = relative(worktreePath, candidate)
if (rel.startsWith('..')) {
  throw new Error('path escapes the worktree')
}
return validateGitRelativeFilePath(worktreePath, filePath)

Type guard

function staysInsideWorktree(worktreePath: string, filePath: string): boolean {
  const rel = relative(worktreePath, resolve(worktreePath, filePath))
  return rel !== '' && !rel.startsWith('..')
}

Try / catch

try {
  return validateGitRelativeFilePath(worktreePath, filePath)
} catch (e) {
  if (e instanceof Error && e.message === 'Access denied: git file path escapes the selected worktree') {
    // contained `..` traversal; reject the renderer-supplied path
    throw new PathTraversalError(e.message)
  }
  throw e
}

Prevention

When it happens

Trigger: Calling `validateGitRelativeFilePath(worktreePath, filePath)` with a relative path containing `..` segments that resolve above the worktree root, e.g. `../../secrets.env`.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/efa04202fb4f5445. Report an issue: GitHub.