diff --git a/src/main/codex/codex-hook-trust-grant.test.ts b/src/main/codex/codex-hook-trust-grant.test.ts index 5f184355b58..cebe6ad20b6 100644 --- a/src/main/codex/codex-hook-trust-grant.test.ts +++ b/src/main/codex/codex-hook-trust-grant.test.ts @@ -31,6 +31,10 @@ let previousUserDataPath: string | undefined beforeEach(() => { userDataDir = mkdtempSync(join(tmpdir(), 'orca-trust-grant-userdata-')) runtimeHomeDir = join(userDataDir, 'codex-runtime-home', 'home') + // Why: production writes hooks.json before granting trust; keeping that + // ordering prevents test-only canonical-path drift during ledger setup. + mkdirSync(runtimeHomeDir, { recursive: true }) + writeFileSync(join(runtimeHomeDir, 'hooks.json'), '{"hooks":{}}\n', 'utf-8') previousUserDataPath = process.env.ORCA_USER_DATA_PATH process.env.ORCA_USER_DATA_PATH = userDataDir codexAppServerCapabilityCache.clear() diff --git a/src/main/codex/config-toml-trust.test.ts b/src/main/codex/config-toml-trust.test.ts index bc5f847b629..9e8ae78cdc0 100644 --- a/src/main/codex/config-toml-trust.test.ts +++ b/src/main/codex/config-toml-trust.test.ts @@ -311,6 +311,16 @@ describe('computeTrustKey', () => { ).toBe(`${join(realpathSync.native(dirname(hooksPath)), 'hooks.json')}:stop:0:0`) }) + it.skipIf(process.platform === 'win32')( + 'canonicalizes an existing POSIX path with two leading slashes', + () => { + const hooksPath = `/${join(tmpDir, 'hooks.json')}` + writeFileSync(hooksPath, '{"hooks":{}}\n', 'utf-8') + + expect(getCodexCanonicalTrustPath(hooksPath)).toBe(realpathSync.native(hooksPath)) + } + ) + it('uses native Windows backslashes in the trust key Codex looks up', () => { // Why: Codex 0.140 writes approved Windows hook trust keys as raw native // paths under [hooks.state]. diff --git a/src/main/codex/config-toml-trust.ts b/src/main/codex/config-toml-trust.ts index 87396638300..2e40f49895d 100644 --- a/src/main/codex/config-toml-trust.ts +++ b/src/main/codex/config-toml-trust.ts @@ -169,7 +169,9 @@ export function computeTrustKey(entry: CodexTrustEntry): string { } export function getCodexCanonicalTrustPath(sourcePath: string): string { - if (process.platform !== 'win32' && usesWindowsPathSeparators(sourcePath)) { + if (process.platform !== 'win32' && isWindowsDriveOrBackslashUncPath(sourcePath)) { + // Why: an existing `//` path is valid on POSIX, so let realpath distinguish + // it from forward-slash UNC before falling back to Windows normalization. return normalizeWindowsPathForCodexLookup(sourcePath) } try { @@ -206,11 +208,11 @@ function normalizeWindowsPathSeparators(sourcePath: string): string { } function usesWindowsPathSeparators(sourcePath: string): boolean { - return ( - /^[A-Za-z]:[\\/]/.test(sourcePath) || - sourcePath.startsWith('\\\\') || - sourcePath.startsWith('//') - ) + return isWindowsDriveOrBackslashUncPath(sourcePath) || sourcePath.startsWith('//') +} + +function isWindowsDriveOrBackslashUncPath(sourcePath: string): boolean { + return /^[A-Za-z]:[\\/]/.test(sourcePath) || sourcePath.startsWith('\\\\') } // Why: Codex and Orca can disagree on quote style, separators, and casing for