mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 22:02:59 -07:00
fix(security) - Use emitFeedback instead of console error (#11948)
Co-authored-by: gemini-cli-robot <gemini-cli-robot@google.com>
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { coreEvents } from '@google/gemini-cli-core';
|
||||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import { promises as fs } from 'node:fs';
|
import { promises as fs } from 'node:fs';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
@@ -33,6 +34,12 @@ vi.mock('../config/storage.js', () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
vi.mock('@google/gemini-cli-core', () => ({
|
||||||
|
coreEvents: {
|
||||||
|
emitFeedback: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
const mockHybridTokenStorage = {
|
const mockHybridTokenStorage = {
|
||||||
listServers: vi.fn(),
|
listServers: vi.fn(),
|
||||||
setCredentials: vi.fn(),
|
setCredentials: vi.fn(),
|
||||||
@@ -72,7 +79,6 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
tokenStorage = new MCPOAuthTokenStorage();
|
tokenStorage = new MCPOAuthTokenStorage();
|
||||||
|
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
vi.spyOn(console, 'error');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -87,7 +93,7 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
const tokens = await tokenStorage.getAllCredentials();
|
const tokens = await tokenStorage.getAllCredentials();
|
||||||
|
|
||||||
expect(tokens.size).toBe(0);
|
expect(tokens.size).toBe(0);
|
||||||
expect(console.error).not.toHaveBeenCalled();
|
expect(coreEvents.emitFeedback).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load tokens from file successfully', async () => {
|
it('should load tokens from file successfully', async () => {
|
||||||
@@ -110,8 +116,10 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
const tokens = await tokenStorage.getAllCredentials();
|
const tokens = await tokenStorage.getAllCredentials();
|
||||||
|
|
||||||
expect(tokens.size).toBe(0);
|
expect(tokens.size).toBe(0);
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||||
|
'error',
|
||||||
expect.stringContaining('Failed to load MCP OAuth tokens'),
|
expect.stringContaining('Failed to load MCP OAuth tokens'),
|
||||||
|
expect.any(Error),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -122,8 +130,10 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
const tokens = await tokenStorage.getAllCredentials();
|
const tokens = await tokenStorage.getAllCredentials();
|
||||||
|
|
||||||
expect(tokens.size).toBe(0);
|
expect(tokens.size).toBe(0);
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('Failed to load MCP OAuth tokens'),
|
'error',
|
||||||
|
'Failed to load MCP OAuth tokens: Permission denied',
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -188,8 +198,10 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
tokenStorage.saveToken('test-server', mockToken),
|
tokenStorage.saveToken('test-server', mockToken),
|
||||||
).rejects.toThrow('Disk full');
|
).rejects.toThrow('Disk full');
|
||||||
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('Failed to save MCP OAuth token'),
|
'error',
|
||||||
|
'Failed to save MCP OAuth token: Disk full',
|
||||||
|
writeError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -277,12 +289,15 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
vi.mocked(fs.readFile).mockResolvedValue(
|
vi.mocked(fs.readFile).mockResolvedValue(
|
||||||
JSON.stringify([mockCredentials]),
|
JSON.stringify([mockCredentials]),
|
||||||
);
|
);
|
||||||
vi.mocked(fs.unlink).mockRejectedValue(new Error('Permission denied'));
|
const unlinkError = new Error('Permission denied');
|
||||||
|
vi.mocked(fs.unlink).mockRejectedValue(unlinkError);
|
||||||
|
|
||||||
await tokenStorage.deleteCredentials('test-server');
|
await tokenStorage.deleteCredentials('test-server');
|
||||||
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('Failed to remove MCP OAuth token'),
|
'error',
|
||||||
|
'Failed to remove MCP OAuth token: Permission denied',
|
||||||
|
unlinkError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -347,16 +362,19 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
|
|
||||||
await tokenStorage.clearAll();
|
await tokenStorage.clearAll();
|
||||||
|
|
||||||
expect(console.error).not.toHaveBeenCalled();
|
expect(coreEvents.emitFeedback).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle other file errors gracefully', async () => {
|
it('should handle other file errors gracefully', async () => {
|
||||||
vi.mocked(fs.unlink).mockRejectedValue(new Error('Permission denied'));
|
const unlinkError = new Error('Permission denied');
|
||||||
|
vi.mocked(fs.unlink).mockRejectedValue(unlinkError);
|
||||||
|
|
||||||
await tokenStorage.clearAll();
|
await tokenStorage.clearAll();
|
||||||
|
|
||||||
expect(console.error).toHaveBeenCalledWith(
|
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('Failed to clear MCP OAuth tokens'),
|
'error',
|
||||||
|
'Failed to clear MCP OAuth tokens: Permission denied',
|
||||||
|
unlinkError,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -368,7 +386,6 @@ describe('MCPOAuthTokenStorage', () => {
|
|||||||
tokenStorage = new MCPOAuthTokenStorage();
|
tokenStorage = new MCPOAuthTokenStorage();
|
||||||
|
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
vi.spyOn(console, 'error');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { coreEvents } from '@google/gemini-cli-core';
|
||||||
import { promises as fs } from 'node:fs';
|
import { promises as fs } from 'node:fs';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { Storage } from '../config/storage.js';
|
import { Storage } from '../config/storage.js';
|
||||||
@@ -68,8 +69,10 @@ export class MCPOAuthTokenStorage implements TokenStorage {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
// File doesn't exist or is invalid, return empty map
|
// File doesn't exist or is invalid, return empty map
|
||||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
console.error(
|
coreEvents.emitFeedback(
|
||||||
|
'error',
|
||||||
`Failed to load MCP OAuth tokens: ${getErrorMessage(error)}`,
|
`Failed to load MCP OAuth tokens: ${getErrorMessage(error)}`,
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,8 +105,10 @@ export class MCPOAuthTokenStorage implements TokenStorage {
|
|||||||
{ mode: 0o600 }, // Restrict file permissions
|
{ mode: 0o600 }, // Restrict file permissions
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
coreEvents.emitFeedback(
|
||||||
|
'error',
|
||||||
`Failed to save MCP OAuth token: ${getErrorMessage(error)}`,
|
`Failed to save MCP OAuth token: ${getErrorMessage(error)}`,
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@@ -181,8 +186,10 @@ export class MCPOAuthTokenStorage implements TokenStorage {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(
|
coreEvents.emitFeedback(
|
||||||
|
'error',
|
||||||
`Failed to remove MCP OAuth token: ${getErrorMessage(error)}`,
|
`Failed to remove MCP OAuth token: ${getErrorMessage(error)}`,
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,8 +223,10 @@ export class MCPOAuthTokenStorage implements TokenStorage {
|
|||||||
await fs.unlink(tokenFile);
|
await fs.unlink(tokenFile);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
console.error(
|
coreEvents.emitFeedback(
|
||||||
|
'error',
|
||||||
`Failed to clear MCP OAuth tokens: ${getErrorMessage(error)}`,
|
`Failed to clear MCP OAuth tokens: ${getErrorMessage(error)}`,
|
||||||
|
error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user