From dfd7721e69f196ba574a4e9e9aa95c2e17b7e5c9 Mon Sep 17 00:00:00 2001 From: Christian Gunderman Date: Sat, 21 Feb 2026 01:12:56 +0000 Subject: [PATCH] Disallow unsafe returns. (#19767) --- eslint.config.js | 1 + packages/a2a-server/src/config/settings.ts | 2 +- packages/cli/src/commands/extensions/utils.ts | 1 + packages/cli/src/config/extensionRegistryClient.ts | 1 + packages/cli/src/config/extensions/extensionEnablement.ts | 1 + packages/cli/src/config/extensions/extensionSettings.ts | 1 + packages/cli/src/config/extensions/variables.ts | 1 + packages/cli/src/test-utils/mockCommandContext.ts | 1 + packages/cli/src/ui/hooks/useAtCompletion.ts | 2 ++ packages/cli/src/ui/utils/TableRenderer.tsx | 6 +++++- packages/cli/src/utils/activityLogger.ts | 2 +- packages/cli/src/utils/envVarResolver.ts | 1 + packages/cli/src/utils/gitUtils.ts | 1 + packages/cli/src/utils/jsonoutput.ts | 1 + packages/cli/src/utils/settingsUtils.ts | 1 + packages/cli/src/zed-integration/fileSystemService.ts | 1 + packages/core/src/code_assist/oauth2.ts | 1 + packages/core/src/config/projectRegistry.ts | 1 + packages/core/src/core/baseLlmClient.ts | 7 +++---- packages/core/src/core/fakeContentGenerator.ts | 2 ++ packages/core/src/ide/ide-client.ts | 1 + packages/core/src/ide/ide-connection-utils.ts | 5 +++++ packages/core/src/services/chatRecordingService.ts | 1 + packages/core/src/utils/filesearch/fileSearch.ts | 1 + packages/core/src/utils/safeJsonStringify.ts | 2 ++ packages/core/src/utils/stdio.ts | 4 ++++ 26 files changed, 42 insertions(+), 7 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index e2a7fa3e8a..12dc29a238 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -201,6 +201,7 @@ export default tseslint.config( rules: { '@typescript-eslint/no-unsafe-type-assertion': 'error', '@typescript-eslint/no-unsafe-assignment': 'error', + '@typescript-eslint/no-unsafe-return': 'error', }, }, { diff --git a/packages/a2a-server/src/config/settings.ts b/packages/a2a-server/src/config/settings.ts index 3f4528c3b2..a2b11d0886 100644 --- a/packages/a2a-server/src/config/settings.ts +++ b/packages/a2a-server/src/config/settings.ts @@ -147,7 +147,7 @@ function resolveEnvVarsInObject(obj: T): T { } if (Array.isArray(obj)) { - // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion + // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-return return obj.map((item) => resolveEnvVarsInObject(item)) as unknown as T; } diff --git a/packages/cli/src/commands/extensions/utils.ts b/packages/cli/src/commands/extensions/utils.ts index 26e47b912b..78bad54502 100644 --- a/packages/cli/src/commands/extensions/utils.ts +++ b/packages/cli/src/commands/extensions/utils.ts @@ -47,6 +47,7 @@ const defaultRequestConfirmation: RequestConfirmationCallback = async ( message, initial: false, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return response.confirm; }; diff --git a/packages/cli/src/config/extensionRegistryClient.ts b/packages/cli/src/config/extensionRegistryClient.ts index 073b78ad79..bf09aabe77 100644 --- a/packages/cli/src/config/extensionRegistryClient.ts +++ b/packages/cli/src/config/extensionRegistryClient.ts @@ -83,6 +83,7 @@ export class ExtensionRegistryClient { }); // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const results = await fzf.find(query); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return results.map((r: { item: RegistryExtension }) => r.item); } diff --git a/packages/cli/src/config/extensions/extensionEnablement.ts b/packages/cli/src/config/extensions/extensionEnablement.ts index a619587342..7ae2431ee9 100644 --- a/packages/cli/src/config/extensions/extensionEnablement.ts +++ b/packages/cli/src/config/extensions/extensionEnablement.ts @@ -179,6 +179,7 @@ export class ExtensionEnablementManager { readConfig(): AllExtensionsEnablementConfig { try { const content = fs.readFileSync(this.configFilePath, 'utf-8'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(content); } catch (error) { if ( diff --git a/packages/cli/src/config/extensions/extensionSettings.ts b/packages/cli/src/config/extensions/extensionSettings.ts index 23df066db1..06e4f49db4 100644 --- a/packages/cli/src/config/extensions/extensionSettings.ts +++ b/packages/cli/src/config/extensions/extensionSettings.ts @@ -156,6 +156,7 @@ export async function promptForSetting( name: 'value', message: `${setting.name}\n${setting.description}`, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return response.value; } diff --git a/packages/cli/src/config/extensions/variables.ts b/packages/cli/src/config/extensions/variables.ts index 5a2e0ca457..3a79fc705f 100644 --- a/packages/cli/src/config/extensions/variables.ts +++ b/packages/cli/src/config/extensions/variables.ts @@ -58,6 +58,7 @@ export function recursivelyHydrateStrings( if (Array.isArray(obj)) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion return obj.map((item) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return recursivelyHydrateStrings(item, values), ) as unknown as T; } diff --git a/packages/cli/src/test-utils/mockCommandContext.ts b/packages/cli/src/test-utils/mockCommandContext.ts index 6bf7aafdbc..8dc5b9930a 100644 --- a/packages/cli/src/test-utils/mockCommandContext.ts +++ b/packages/cli/src/test-utils/mockCommandContext.ts @@ -121,5 +121,6 @@ export const createMockCommandContext = ( return output; }; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return merge(defaultMocks, overrides); }; diff --git a/packages/cli/src/ui/hooks/useAtCompletion.ts b/packages/cli/src/ui/hooks/useAtCompletion.ts index af827f1b12..8d860bb6ce 100644 --- a/packages/cli/src/ui/hooks/useAtCompletion.ts +++ b/packages/cli/src/ui/hooks/useAtCompletion.ts @@ -170,6 +170,7 @@ async function searchResourceCandidates( const results = await fzf.find(normalizedPattern, { limit: MAX_SUGGESTIONS_TO_SHOW * 3, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return results.map( (result: { item: ResourceSuggestionCandidate }) => result.item.suggestion, ); @@ -193,6 +194,7 @@ async function searchAgentCandidates( const results = await fzf.find(normalizedPattern, { limit: MAX_SUGGESTIONS_TO_SHOW, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return results.map((r: { item: Suggestion }) => r.item); } diff --git a/packages/cli/src/ui/utils/TableRenderer.tsx b/packages/cli/src/ui/utils/TableRenderer.tsx index e57e2f7faa..ab1981762c 100644 --- a/packages/cli/src/ui/utils/TableRenderer.tsx +++ b/packages/cli/src/ui/utils/TableRenderer.tsx @@ -193,7 +193,11 @@ export const TableRenderer: React.FC = ({ const wrappedRows = styledRows.map((row) => wrapAndProcessRow(row)); // Use the TIGHTEST widths that fit the wrapped content + padding - const adjustedWidths = actualColumnWidths.map((w) => w + COLUMN_PADDING); + const adjustedWidths = actualColumnWidths.map( + (w) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + w + COLUMN_PADDING, + ); return { wrappedHeaders, wrappedRows, adjustedWidths }; }, [styledHeaders, styledRows, terminalWidth]); diff --git a/packages/cli/src/utils/activityLogger.ts b/packages/cli/src/utils/activityLogger.ts index cf852257a9..9f1d268a91 100644 --- a/packages/cli/src/utils/activityLogger.ts +++ b/packages/cli/src/utils/activityLogger.ts @@ -472,7 +472,7 @@ export class ActivityLogger extends EventEmitter { body, pending: true, }); - // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-return return (oldEnd as any).apply(this, [chunk, ...etc]); }; diff --git a/packages/cli/src/utils/envVarResolver.ts b/packages/cli/src/utils/envVarResolver.ts index 4bd1d6f82f..6e01f67ac7 100644 --- a/packages/cli/src/utils/envVarResolver.ts +++ b/packages/cli/src/utils/envVarResolver.ts @@ -98,6 +98,7 @@ function resolveEnvVarsInObjectInternal( visited.add(obj); // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion const result = obj.map((item) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return resolveEnvVarsInObjectInternal(item, visited, customEnv), ) as unknown as T; visited.delete(obj); diff --git a/packages/cli/src/utils/gitUtils.ts b/packages/cli/src/utils/gitUtils.ts index c25366f72a..e27673f0fe 100644 --- a/packages/cli/src/utils/gitUtils.ts +++ b/packages/cli/src/utils/gitUtils.ts @@ -83,6 +83,7 @@ export const getLatestGitHubRelease = async ( if (!releaseTag) { throw new Error(`Response did not include tag_name field`); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return releaseTag; } catch (_error) { debugLogger.debug( diff --git a/packages/cli/src/utils/jsonoutput.ts b/packages/cli/src/utils/jsonoutput.ts index f600b7e165..7f60c34104 100644 --- a/packages/cli/src/utils/jsonoutput.ts +++ b/packages/cli/src/utils/jsonoutput.ts @@ -40,6 +40,7 @@ export function tryParseJSON(input: string): object | null { if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return parsed; } catch (_err) { return null; diff --git a/packages/cli/src/utils/settingsUtils.ts b/packages/cli/src/utils/settingsUtils.ts index 8d313cf688..3fa1d8bd5d 100644 --- a/packages/cli/src/utils/settingsUtils.ts +++ b/packages/cli/src/utils/settingsUtils.ts @@ -374,6 +374,7 @@ export function setPendingSettingValue( // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const newSettings = JSON.parse(JSON.stringify(pendingSettings)); setNestedValue(newSettings, path, value); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return newSettings; } diff --git a/packages/cli/src/zed-integration/fileSystemService.ts b/packages/cli/src/zed-integration/fileSystemService.ts index fc4672b4b5..1d3c8ad0b8 100644 --- a/packages/cli/src/zed-integration/fileSystemService.ts +++ b/packages/cli/src/zed-integration/fileSystemService.ts @@ -29,6 +29,7 @@ export class AcpFileSystemService implements FileSystemService { sessionId: this.sessionId, }); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return response.content; } diff --git a/packages/core/src/code_assist/oauth2.ts b/packages/core/src/code_assist/oauth2.ts index fd2f5ea178..bfa50599c7 100644 --- a/packages/core/src/code_assist/oauth2.ts +++ b/packages/core/src/code_assist/oauth2.ts @@ -636,6 +636,7 @@ async function fetchCachedCredentials(): Promise< for (const keyFile of pathsToTry) { try { const keyFileString = await fs.readFile(keyFile, 'utf-8'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(keyFileString); } catch (error) { // Log specific error for debugging, but continue trying other paths diff --git a/packages/core/src/config/projectRegistry.ts b/packages/core/src/config/projectRegistry.ts index 225faedf9b..725ea081f9 100644 --- a/packages/core/src/config/projectRegistry.ts +++ b/packages/core/src/config/projectRegistry.ts @@ -59,6 +59,7 @@ export class ProjectRegistry { try { const content = await fs.promises.readFile(this.registryPath, 'utf8'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(content); } catch (e) { debugLogger.debug('Failed to load registry: ', e); diff --git a/packages/core/src/core/baseLlmClient.ts b/packages/core/src/core/baseLlmClient.ts index 64730ff74c..64442ac86e 100644 --- a/packages/core/src/core/baseLlmClient.ts +++ b/packages/core/src/core/baseLlmClient.ts @@ -13,21 +13,19 @@ import type { GenerateContentConfig, } from '@google/genai'; import type { Config } from '../config/config.js'; -import type { ContentGenerator } from './contentGenerator.js'; -import type { AuthType } from './contentGenerator.js'; +import type { ContentGenerator, AuthType } from './contentGenerator.js'; import { handleFallback } from '../fallback/handler.js'; import { getResponseText } from '../utils/partUtils.js'; import { reportError } from '../utils/errorReporting.js'; import { getErrorMessage } from '../utils/errors.js'; import { logMalformedJsonResponse } from '../telemetry/loggers.js'; -import { MalformedJsonResponseEvent } from '../telemetry/types.js'; +import { MalformedJsonResponseEvent, LlmRole } from '../telemetry/types.js'; import { retryWithBackoff } from '../utils/retry.js'; import type { ModelConfigKey } from '../services/modelConfigService.js'; import { applyModelSelection, createAvailabilityContextProvider, } from '../availability/policyHelpers.js'; -import { LlmRole } from '../telemetry/types.js'; const DEFAULT_MAX_ATTEMPTS = 5; @@ -164,6 +162,7 @@ export class BaseLlmClient { ); // If we are here, the content is valid (not empty and parsable). + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse( this.cleanJsonResponse(getResponseText(result)!.trim(), model), ); diff --git a/packages/core/src/core/fakeContentGenerator.ts b/packages/core/src/core/fakeContentGenerator.ts index 5bedc2d187..c765bde087 100644 --- a/packages/core/src/core/fakeContentGenerator.ts +++ b/packages/core/src/core/fakeContentGenerator.ts @@ -83,6 +83,7 @@ export class FakeContentGenerator implements ContentGenerator { // eslint-disable-next-line @typescript-eslint/no-unused-vars role: LlmRole, ): Promise { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.setPrototypeOf( this.getNextResponse('generateContent', request), GenerateContentResponse.prototype, @@ -116,6 +117,7 @@ export class FakeContentGenerator implements ContentGenerator { async embedContent( request: EmbedContentParameters, ): Promise { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return Object.setPrototypeOf( this.getNextResponse('embedContent', request), EmbedContentResponse.prototype, diff --git a/packages/core/src/ide/ide-client.ts b/packages/core/src/ide/ide-client.ts index 2b3b29ac3c..373df31f5f 100644 --- a/packages/core/src/ide/ide-client.ts +++ b/packages/core/src/ide/ide-client.ts @@ -348,6 +348,7 @@ export class IdeClient { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const parsedJson = JSON.parse(textPart.text); if (parsedJson && typeof parsedJson.content === 'string') { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return parsedJson.content; } if (parsedJson && parsedJson.content === null) { diff --git a/packages/core/src/ide/ide-connection-utils.ts b/packages/core/src/ide/ide-connection-utils.ts index 81a9740327..c9776e1509 100644 --- a/packages/core/src/ide/ide-connection-utils.ts +++ b/packages/core/src/ide/ide-connection-utils.ts @@ -123,6 +123,7 @@ export async function getConnectionConfigFromFile( `gemini-ide-server-${pid}.json`, ); const portFileContents = await fs.promises.readFile(portFile, 'utf8'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(portFileContents); } catch (_) { // For newer extension versions, the file name matches the pattern @@ -167,6 +168,7 @@ export async function getConnectionConfigFromFile( } const parsedContents = fileContents.map((content) => { try { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(content); } catch (e) { logger.debug('Failed to parse JSON from config file: ', e); @@ -196,6 +198,7 @@ export async function getConnectionConfigFromFile( if (fileIndex !== -1) { logger.debug(`Selected IDE connection file: ${matchingFiles[fileIndex]}`); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return selected; } @@ -213,6 +216,7 @@ export async function getConnectionConfigFromFile( `Selected IDE connection file (matched port from env): ${matchingFiles[fileIndex]}`, ); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return selected; } } @@ -225,6 +229,7 @@ export async function getConnectionConfigFromFile( `Selected first valid IDE connection file: ${matchingFiles[fileIndex]}`, ); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return selected; } diff --git a/packages/core/src/services/chatRecordingService.ts b/packages/core/src/services/chatRecordingService.ts index 0b94825353..6d94b9a3bf 100644 --- a/packages/core/src/services/chatRecordingService.ts +++ b/packages/core/src/services/chatRecordingService.ts @@ -419,6 +419,7 @@ export class ChatRecordingService { private readConversation(): ConversationRecord { try { this.cachedLastConvData = fs.readFileSync(this.conversationFile!, 'utf8'); + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return JSON.parse(this.cachedLastConvData); } catch (error) { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion diff --git a/packages/core/src/utils/filesearch/fileSearch.ts b/packages/core/src/utils/filesearch/fileSearch.ts index 21d1e19168..97560f7070 100644 --- a/packages/core/src/utils/filesearch/fileSearch.ts +++ b/packages/core/src/utils/filesearch/fileSearch.ts @@ -149,6 +149,7 @@ class RecursiveFileSearch implements FileSearch { filteredCandidates = await this.fzf .find(pattern) .then((results: Array>) => + // eslint-disable-next-line @typescript-eslint/no-unsafe-return results.map((entry: FzfResultItem) => entry.item), ) .catch(() => { diff --git a/packages/core/src/utils/safeJsonStringify.ts b/packages/core/src/utils/safeJsonStringify.ts index 611519d09b..b32a09df27 100644 --- a/packages/core/src/utils/safeJsonStringify.ts +++ b/packages/core/src/utils/safeJsonStringify.ts @@ -27,6 +27,7 @@ export function safeJsonStringify( } seen.add(value); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; }, space, @@ -60,6 +61,7 @@ export function safeJsonStringifyBooleanValuesOnly(obj: any): string { // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion if ((value as Config) !== null && !configSeen) { configSeen = true; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; } if (typeof value === 'boolean') { diff --git a/packages/core/src/utils/stdio.ts b/packages/core/src/utils/stdio.ts index 22fd2e8447..66abbe6ade 100644 --- a/packages/core/src/utils/stdio.ts +++ b/packages/core/src/utils/stdio.ts @@ -91,8 +91,10 @@ export function createWorkingStdio() { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const value = Reflect.get(target, prop, receiver); if (typeof value === 'function') { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value.bind(target); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; }, }); @@ -105,8 +107,10 @@ export function createWorkingStdio() { // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment const value = Reflect.get(target, prop, receiver); if (typeof value === 'function') { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value.bind(target); } + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return value; }, });