mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 01:51:20 -07:00
fix(security) - Use emitFeedback (#11961)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import type { KeychainTokenStorage } from './keychain-token-storage.js';
|
||||
import type { OAuthCredentials } from './types.js';
|
||||
import { coreEvents } from '../../utils/events.js';
|
||||
|
||||
// Hoist the mock to be available in the vi.mock factory
|
||||
const mockKeytar = vi.hoisted(() => ({
|
||||
@@ -30,6 +31,12 @@ vi.mock('node:crypto', () => ({
|
||||
})),
|
||||
}));
|
||||
|
||||
vi.mock('../../utils/events.js', () => ({
|
||||
coreEvents: {
|
||||
emitFeedback: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('KeychainTokenStorage', () => {
|
||||
let storage: KeychainTokenStorage;
|
||||
|
||||
@@ -82,7 +89,8 @@ describe('KeychainTokenStorage', () => {
|
||||
});
|
||||
|
||||
it('should return false if keytar fails to set password', async () => {
|
||||
mockKeytar.setPassword.mockRejectedValue(new Error('write error'));
|
||||
const error = new Error('write error');
|
||||
mockKeytar.setPassword.mockRejectedValue(error);
|
||||
const isAvailable = await storage.checkKeychainAvailability();
|
||||
expect(isAvailable).toBe(false);
|
||||
});
|
||||
@@ -265,14 +273,20 @@ describe('KeychainTokenStorage', () => {
|
||||
});
|
||||
|
||||
it('should return an empty array on error', async () => {
|
||||
mockKeytar.findCredentials.mockRejectedValue(new Error('find error'));
|
||||
const error = new Error('find error');
|
||||
mockKeytar.findCredentials.mockRejectedValue(error);
|
||||
const result = await storage.listServers();
|
||||
expect(result).toEqual([]);
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to list servers from keychain',
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllCredentials', () => {
|
||||
it('should return a map of all valid credentials', async () => {
|
||||
it('should return a map of all valid credentials and emit feedback for invalid ones', async () => {
|
||||
const creds2 = {
|
||||
...validCredentials,
|
||||
serverName: 'server2',
|
||||
@@ -310,6 +324,30 @@ describe('KeychainTokenStorage', () => {
|
||||
expect(result.has('expired-server')).toBe(false);
|
||||
expect(result.has('bad-server')).toBe(false);
|
||||
expect(result.has('invalid-server')).toBe(false);
|
||||
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to parse credentials for bad-server',
|
||||
expect.any(SyntaxError),
|
||||
);
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to parse credentials for invalid-server',
|
||||
expect.any(Error),
|
||||
);
|
||||
});
|
||||
|
||||
it('should emit feedback and return empty map if findCredentials fails', async () => {
|
||||
const error = new Error('find all error');
|
||||
mockKeytar.findCredentials.mockRejectedValue(error);
|
||||
|
||||
const result = await storage.getAllCredentials();
|
||||
expect(result.size).toBe(0);
|
||||
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||
'error',
|
||||
'Failed to get all credentials from keychain',
|
||||
error,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import * as crypto from 'node:crypto';
|
||||
import { BaseTokenStorage } from './base-token-storage.js';
|
||||
import type { OAuthCredentials } from './types.js';
|
||||
import { coreEvents } from '../../utils/events.js';
|
||||
|
||||
interface Keytar {
|
||||
getPassword(service: string, account: string): Promise<string | null>;
|
||||
@@ -42,7 +43,7 @@ export class KeychainTokenStorage extends BaseTokenStorage {
|
||||
const module = await import(moduleName);
|
||||
this.keytarModule = module.default || module;
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
coreEvents.emitFeedback('error', "Failed to load 'keytar' module", error);
|
||||
}
|
||||
return this.keytarModule;
|
||||
}
|
||||
@@ -139,7 +140,11 @@ export class KeychainTokenStorage extends BaseTokenStorage {
|
||||
.filter((cred) => !cred.account.startsWith(KEYCHAIN_TEST_PREFIX))
|
||||
.map((cred: { account: string }) => cred.account);
|
||||
} catch (error) {
|
||||
console.error('Failed to list servers from keychain:', error);
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to list servers from keychain',
|
||||
error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -167,14 +172,19 @@ export class KeychainTokenStorage extends BaseTokenStorage {
|
||||
result.set(cred.account, data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Failed to parse credentials for ${cred.account}:`,
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
`Failed to parse credentials for ${cred.account}`,
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to get all credentials from keychain:', error);
|
||||
coreEvents.emitFeedback(
|
||||
'error',
|
||||
'Failed to get all credentials from keychain',
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
Reference in New Issue
Block a user