fix(cli): a sandbox that denies chmod no longer breaks reading Orca's state files (#20937) (#33662)

Orca CLI commands inside a sandbox that blocks chmod (e.g. Claude Code's Bash sandbox) no longer fail with a misleading "file is invalid": the pre-read hardening of existing secure files ignores EPERM/EACCES/EROFS; writers still create files 0600.

Fixes #20937
This commit is contained in:
Neil
2026-10-25 16:05:58 -07:00
committed by GitHub
parent 23798eff7c
commit f94b15ec85
2 changed files with 52 additions and 7 deletions
+34 -1
View File
@@ -1,7 +1,8 @@
import { chmodSync, mkdtempSync, rmSync, statSync, writeFileSync } from 'node:fs'
import type * as NodeFs from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import {
__getSecureFileHardeningCacheStateForTests,
__resetSecureFileHardenedPathsForTests,
@@ -15,6 +16,21 @@ import {
} from './windows-security-addon'
import { createFakeWindowsSecurity } from './windows-security-addon.test-fixture'
const chmodFailure = vi.hoisted((): { code: string | null } => ({ code: null }))
vi.mock('node:fs', async (importOriginal) => {
const actual = await importOriginal<typeof NodeFs>()
return {
...actual,
chmodSync: (...args: Parameters<typeof actual.chmodSync>) => {
if (chmodFailure.code) {
throw Object.assign(new Error(chmodFailure.code), { code: chmodFailure.code })
}
actual.chmodSync(...args)
}
}
})
const posixModeIt = process.platform === 'win32' ? it.skip : it
describe('POSIX secure files', () => {
@@ -27,6 +43,7 @@ describe('POSIX secure files', () => {
})
afterEach(() => {
chmodFailure.code = null
resetWindowsSecurityLoadForTests()
__resetSecureFileHardenedPathsForTests()
setWindowsSecurityForTests(null)
@@ -82,6 +99,22 @@ describe('POSIX secure files', () => {
expect(statMode(userDataPath)).toBe(0o700)
})
/** Claude Code's Bash sandbox denies chmod on userData; `orca worktree list` failed reading it (#20937). */
posixModeIt(
'lets readers proceed when a sandbox denies hardening, but not on other errors',
() => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'linux' })
const targetPath = join(tempDir(), 'secret.json')
writeFileSync(targetPath, '{}')
chmodFailure.code = 'EPERM'
expect(() => hardenExistingSecureFile(targetPath)).not.toThrow()
chmodFailure.code = 'EIO'
expect(() => hardenExistingSecureFile(targetPath)).toThrow('EIO')
}
)
posixModeIt('LRU-bounds POSIX hardening entries while keeping recent paths cached', () => {
Object.defineProperty(process, 'platform', { configurable: true, value: 'linux' })
__resetSecureFileHardenedPathsForTests({
+18 -6
View File
@@ -207,16 +207,28 @@ export function isUnreadableError(error: unknown): boolean {
)
}
/** Readers tighten modes opportunistically; a sandbox that denies chmod must not fail the read (#20937). */
export function hardenExistingSecureFile(targetPath: string): void {
const dir = dirname(targetPath)
if (existsSync(dir)) {
hardenSecurePathOnce(dir, true)
}
if (existsSync(targetPath)) {
hardenSecurePathOnce(targetPath, false)
try {
const dir = dirname(targetPath)
if (existsSync(dir)) {
hardenSecurePathOnce(dir, true)
}
if (existsSync(targetPath)) {
hardenSecurePathOnce(targetPath, false)
}
} catch (error) {
if (!isPermissionDeniedError(error)) {
throw error
}
}
}
function isPermissionDeniedError(error: unknown): boolean {
const code = error instanceof Error && 'code' in error ? error.code : undefined
return code === 'EPERM' || code === 'EACCES' || code === 'EROFS'
}
/** Caches the current metadata snapshot for a just-hardened path, or clears it if the path is gone. */
function rememberHardenedPath(targetPath: string, isDirectory: boolean): void {
const entry = getHardenedPathCacheEntry(targetPath, isDirectory)