Fixed upstream 23 Oct 2026  Upstream bug fix in Orca

Orca: reading an SSH identity file could freeze the app

Our scanner flagged that Orca, a desktop app for running a fleet of coding agents, read SSH private keys with no check on the file type or size. We filed an issue and a pull request with a fix and tests. Maintainer Neil (nwparker) built a broader fix on top of it the next day, kept our test cases and credited us as co-author of the commit.

What was wrong

To connect to remote machines, Orca resolves the IdentityFile paths from the user’s SSH configuration and reads the keys in src/main/ssh/ssh-auth-resolution.ts. readPrivateKey() and findDefaultKeyFile() called readFileSync() on the path straight away:

The paths come from the user’s own configuration, so this is not a security boundary that an outside attacker crosses. It is a robustness problem: one odd or mistyped IdentityFile line could hang the whole app. The codebase already capped file sizes in other places, for example WORKTREE_INCLUDE_MAX_FILE_BYTES, but not here.

How it was fixed

Our pull request added a statSync() check before both reads: only a regular file of at most 256 KB was read. Tests covered a directory and an oversized key, for both default and explicit key paths.

The maintainer shipped a broader version in pull request #32274. Every place Orca reads an SSH key or identity file now goes through one reader, ssh-identity-file-read.ts. It accepts regular files only (also through symlinks), opens them non-blocking so a path swapped for a pipe after the check still can’t block, reuses the existing 1 MB identity-file limit, and covers the .pub read for IdentitiesOnly as well. A failed sign-in now says which key file was skipped and why:

// Why one reader: identity paths come from user config, so a FIFO or device must never block the
// main process and an oversized file must never be loaded whole.
export function readSshIdentityFileSync(filePath: string): SshIdentityFileRead {
  try {
    return {
      contents: readNodeFileSyncWithinLimit(filePath, MAX_SSH_IDENTITY_FILE_BYTES, READ_OPTIONS)
        .buffer
    }
  } catch (error) {
    return { contents: null, problem: describeIdentityFileProblem(error) }
  }
}

Neil (nwparker) replied on our pull request:

“Thanks a lot for finding this, @Huge, and for the clear write-up and fix.”

Timeline

How to read this

The scanner’s first description called the path “attacker-chosen”. Reading the code showed the path comes from the user’s own SSH configuration, so we reported it as a robustness fix and nothing more. The maintainer then went further than our patch, closing the check-then-read race and the .pub read we had missed. Findings are leads for people, not verdicts.

Sources

← Back to Success Stories