From d0dc83aa64f37a6f465383ca562a80d59ef10bba Mon Sep 17 00:00:00 2001 From: galz10 Date: Tue, 24 Feb 2026 11:50:29 -0800 Subject: [PATCH] chore: fix lint errors --- .../mcp/token-storage/file-secret-storage.ts | 53 ++++++++++++++----- packages/core/src/mcp/token-storage/index.ts | 3 ++ 2 files changed, 43 insertions(+), 13 deletions(-) diff --git a/packages/core/src/mcp/token-storage/file-secret-storage.ts b/packages/core/src/mcp/token-storage/file-secret-storage.ts index 9ce2d51063..2c0b31ca92 100644 --- a/packages/core/src/mcp/token-storage/file-secret-storage.ts +++ b/packages/core/src/mcp/token-storage/file-secret-storage.ts @@ -11,6 +11,19 @@ import * as crypto from 'node:crypto'; import type { SecretStorage } from './types.js'; import { GEMINI_DIR, homedir } from '../../utils/paths.js'; +/** + * Type guard for NodeJS.ErrnoException + */ +function isErrnoException(error: unknown): error is NodeJS.ErrnoException { + return ( + error instanceof Error && + ('code' in error || + 'errno' in error || + 'path' in error || + 'syscall' in error) + ); +} + /** * Encrypted file-based storage for secrets, used as a fallback * when a system keychain (like keytar) is unavailable. @@ -23,7 +36,10 @@ export class FileSecretStorage implements SecretStorage { const configDir = path.join(homedir(), GEMINI_DIR); // Sanitize service name for filename const sanitizedService = serviceName.replace(/[^a-zA-Z0-9-_.]/g, '_'); - this.secretFilePath = path.join(configDir, `secrets-${sanitizedService}.json`); + this.secretFilePath = path.join( + configDir, + `secrets-${sanitizedService}.json`, + ); this.encryptionKey = this.deriveEncryptionKey(); } @@ -76,18 +92,30 @@ export class FileSecretStorage implements SecretStorage { try { const data = await fs.readFile(this.secretFilePath, 'utf-8'); const decrypted = this.decrypt(data); - return JSON.parse(decrypted) as Record; + const parsed = JSON.parse(decrypted) as unknown; + + if (parsed === null || typeof parsed !== 'object') { + throw new Error('Secret file content is not a valid object'); + } + + const result: Record = {}; + for (const [key, value] of Object.entries(parsed)) { + if (typeof value === 'string') { + result[key] = value; + } + } + + return result; } catch (error: unknown) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const err = error as NodeJS.ErrnoException & { message?: string }; - if (err.code === 'ENOENT') { + if (isErrnoException(error) && error.code === 'ENOENT') { return {}; } + + const message = error instanceof Error ? error.message : ''; + if ( - err.message?.includes('Invalid encrypted data format') || - err.message?.includes( - 'Unsupported state or unable to authenticate data', - ) + message.includes('Invalid encrypted data format') || + message.includes('Unsupported state or unable to authenticate data') ) { throw new Error('Secret file corrupted'); } @@ -133,11 +161,10 @@ export class FileSecretStorage implements SecretStorage { try { await fs.unlink(this.secretFilePath); } catch (error: unknown) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion - const err = error as NodeJS.ErrnoException; - if (err.code !== 'ENOENT') { - throw error; + if (isErrnoException(error) && error.code === 'ENOENT') { + return; } + throw error; } } } diff --git a/packages/core/src/mcp/token-storage/index.ts b/packages/core/src/mcp/token-storage/index.ts index 598f6d60de..bd3be4f25a 100644 --- a/packages/core/src/mcp/token-storage/index.ts +++ b/packages/core/src/mcp/token-storage/index.ts @@ -11,3 +11,6 @@ export * from './keychain-token-storage.js'; export * from './hybrid-token-storage.js'; export * from './file-secret-storage.js'; export * from './hybrid-secret-storage.js'; + +export const DEFAULT_SERVICE_NAME = 'gemini-cli-mcp'; +export const FORCE_ENCRYPTED_FILE_ENV_VAR = 'GEMINI_FORCE_ENCRYPTED_FILE';