fix(kiro): eliminate TOCTOU in token fingerprinting and harden tests

Cache file content in RawTokenCandidate to avoid double-reading token
files (TOCTOU race between metadata extraction and fingerprinting).
Rename stale test description and add fingerprint fields to test
snapshots so they exercise the same comparison path as production.
This commit is contained in:
Tam Nhu Tran
2026-04-07 08:47:22 -04:00
parent 25bcee2fa1
commit d95cee1e84
2 changed files with 6 additions and 6 deletions
+3 -3
View File
@@ -62,7 +62,7 @@ type TokenCandidate = {
fingerprint: string;
};
type RawTokenCandidate = Omit<TokenCandidate, 'accountId' | 'fingerprint'>;
type RawTokenCandidate = Omit<TokenCandidate, 'accountId' | 'fingerprint'> & { content: string };
function buildTokenFingerprint(content: string): string {
return createHash('sha256').update(content).digest('hex');
@@ -92,6 +92,7 @@ function listTokenCandidates(provider: CLIProxyProvider, tokenDir: string): Toke
{
file,
filePath,
content,
email,
projectId,
mtimeMs: stats.mtimeMs,
@@ -122,7 +123,6 @@ function listTokenCandidates(provider: CLIProxyProvider, tokenDir: string): Toke
return rawCandidates
.map((rawCandidate) => {
const content = fs.readFileSync(rawCandidate.filePath, 'utf-8');
const duplicateEmailCount = rawCandidate.email
? (duplicateEmailCounts.get(rawCandidate.email.toLowerCase()) ?? 1)
: 1;
@@ -138,7 +138,7 @@ function listTokenCandidates(provider: CLIProxyProvider, tokenDir: string): Toke
return {
...rawCandidate,
accountId,
fingerprint: buildTokenFingerprint(content),
fingerprint: buildTokenFingerprint(rawCandidate.content),
};
})
.sort((left, right) => right.mtimeMs - left.mtimeMs);
@@ -10,7 +10,7 @@ import {
} from '../../../src/cliproxy/auth/oauth-process';
describe('oauth-process stderr parsing', () => {
it('ignores non-ghcp providers', () => {
it('does not match provider-specific patterns for other providers', () => {
const stderr =
'time="2026-03-03T10:00:00Z" level=error msg="GitHub Copilot authentication failed: example"';
@@ -55,8 +55,8 @@ describe('oauth-process successful exit analysis', () => {
it('treats unchanged existing kiro tokens as a failed add-account attempt', () => {
const result = analyzeSuccessfulAuthExit({
provider: 'kiro',
knownTokenFiles: [{ file: 'kiro-existing.json', mtimeMs: 100 }],
currentTokenFiles: [{ file: 'kiro-existing.json', mtimeMs: 100 }],
knownTokenFiles: [{ file: 'kiro-existing.json', mtimeMs: 100, fingerprint: 'same' }],
currentTokenFiles: [{ file: 'kiro-existing.json', mtimeMs: 100, fingerprint: 'same' }],
stdoutData:
'[error] Kiro IDC authentication failed: login failed: failed to register client: register client failed (status 400)',
stderrData: '',