fix(github): scope GHES host-auth cache to the executing connection (#14948)

* fix(github): scope GHES host-auth cache to the executing connection

The gh auth answer cached for a connection-backed repo is probed without
the repository cwd, so it describes that connection's runtime — not the
local host. Keying only on repoPath+wslDistro let a local repo and an
SSH-hosted repo at the same path share one entry, and whichever resolved
first decided "is this GHES host authenticated" for both.

Include the connection identity in the runtime cache key. Local and WSL
keys are unchanged.

* fix(github): fence GHES auth cache across SSH reconnects

* fix(github): fence origin cache across SSH reconnects

* fix(github): fence repository identity cache on reconnect
This commit is contained in:
Brennan Benson
2026-08-18 13:18:56 -07:00
committed by GitHub
parent bef76953d7
commit 4b0e01f613
3 changed files with 208 additions and 12 deletions
+78 -1
View File
@@ -1,7 +1,7 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type * as SshGitDispatch from '../providers/ssh-git-dispatch'
import type * as GitHubEnterpriseRepository from './github-enterprise-repository'
import type * as GhUtils from './gh-utils'
import type * as SshGitDispatch from '../providers/ssh-git-dispatch'
const {
getEnterpriseGitHubRepoSlugMock,
@@ -268,4 +268,81 @@ describe('origin repository cache', () => {
await expect(getOriginGitHubApiRepository('/repo')).resolves.toBeNull()
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(1)
})
it('does not reuse a cached answer after the SSH provider reconnects', async () => {
const oldRepository = {
owner: 'old-owner',
repo: 'widgets',
host: 'github.acme-corp.com'
}
const newRepository = {
owner: 'new-owner',
repo: 'widgets',
host: 'github.acme-corp.com'
}
getSshGitProviderGenerationMock.mockReturnValue(1)
getEnterpriseGitHubRepoSlugMock
.mockResolvedValueOnce(oldRepository)
.mockResolvedValueOnce(newRepository)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toEqual(oldRepository)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toEqual(oldRepository)
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(1)
getSshGitProviderGenerationMock.mockReturnValue(2)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toEqual(newRepository)
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(2)
})
it('does not reuse a cached negative after the SSH provider reconnects', async () => {
const repository = {
owner: 'acme',
repo: 'widgets',
host: 'github.acme-corp.com'
}
getSshGitProviderGenerationMock.mockReturnValue(1)
getEnterpriseGitHubRepoSlugMock.mockResolvedValueOnce(null).mockResolvedValueOnce(repository)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toBeNull()
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toBeNull()
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(1)
getSshGitProviderGenerationMock.mockReturnValue(2)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toEqual(repository)
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(2)
})
it('isolates an in-flight probe from an older SSH provider generation', async () => {
const oldRepository = {
owner: 'old-owner',
repo: 'widgets',
host: 'github.acme-corp.com'
}
const newRepository = {
owner: 'new-owner',
repo: 'widgets',
host: 'github.acme-corp.com'
}
let resolveOldProbe: (repository: typeof oldRepository) => void = () => {}
const oldProbeResult = new Promise<typeof oldRepository>((resolve) => {
resolveOldProbe = resolve
})
getEnterpriseGitHubRepoSlugMock
.mockImplementationOnce(() => oldProbeResult)
.mockResolvedValueOnce(newRepository)
getSshGitProviderGenerationMock.mockReturnValue(1)
const oldProbe = getOriginGitHubApiRepository('/repo', 'ssh-1')
await vi.waitFor(() => expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(1))
getSshGitProviderGenerationMock.mockReturnValue(2)
const newProbe = getOriginGitHubApiRepository('/repo', 'ssh-1')
await vi.waitFor(() => expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(2))
await expect(newProbe).resolves.toEqual(newRepository)
resolveOldProbe(oldRepository)
await expect(oldProbe).resolves.toEqual(oldRepository)
await expect(getOriginGitHubApiRepository('/repo', 'ssh-1')).resolves.toEqual(newRepository)
expect(getEnterpriseGitHubRepoSlugMock).toHaveBeenCalledTimes(2)
})
})