Disallow unsafe returns. (#19767)

This commit is contained in:
Christian Gunderman
2026-02-21 01:12:56 +00:00
committed by GitHub
parent 09218572d0
commit dfd7721e69
26 changed files with 42 additions and 7 deletions
+1
View File
@@ -201,6 +201,7 @@ export default tseslint.config(
rules: { rules: {
'@typescript-eslint/no-unsafe-type-assertion': 'error', '@typescript-eslint/no-unsafe-type-assertion': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error', '@typescript-eslint/no-unsafe-assignment': 'error',
'@typescript-eslint/no-unsafe-return': 'error',
}, },
}, },
{ {
+1 -1
View File
@@ -147,7 +147,7 @@ function resolveEnvVarsInObject<T>(obj: T): T {
} }
if (Array.isArray(obj)) { 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; return obj.map((item) => resolveEnvVarsInObject(item)) as unknown as T;
} }
@@ -47,6 +47,7 @@ const defaultRequestConfirmation: RequestConfirmationCallback = async (
message, message,
initial: false, initial: false,
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.confirm; return response.confirm;
}; };
@@ -83,6 +83,7 @@ export class ExtensionRegistryClient {
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(query); const results = await fzf.find(query);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map((r: { item: RegistryExtension }) => r.item); return results.map((r: { item: RegistryExtension }) => r.item);
} }
@@ -179,6 +179,7 @@ export class ExtensionEnablementManager {
readConfig(): AllExtensionsEnablementConfig { readConfig(): AllExtensionsEnablementConfig {
try { try {
const content = fs.readFileSync(this.configFilePath, 'utf-8'); const content = fs.readFileSync(this.configFilePath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(content); return JSON.parse(content);
} catch (error) { } catch (error) {
if ( if (
@@ -156,6 +156,7 @@ export async function promptForSetting(
name: 'value', name: 'value',
message: `${setting.name}\n${setting.description}`, message: `${setting.name}\n${setting.description}`,
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.value; return response.value;
} }
@@ -58,6 +58,7 @@ export function recursivelyHydrateStrings<T>(
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
return obj.map((item) => return obj.map((item) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
recursivelyHydrateStrings(item, values), recursivelyHydrateStrings(item, values),
) as unknown as T; ) as unknown as T;
} }
@@ -121,5 +121,6 @@ export const createMockCommandContext = (
return output; return output;
}; };
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return merge(defaultMocks, overrides); return merge(defaultMocks, overrides);
}; };
@@ -170,6 +170,7 @@ async function searchResourceCandidates(
const results = await fzf.find(normalizedPattern, { const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW * 3, limit: MAX_SUGGESTIONS_TO_SHOW * 3,
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map( return results.map(
(result: { item: ResourceSuggestionCandidate }) => result.item.suggestion, (result: { item: ResourceSuggestionCandidate }) => result.item.suggestion,
); );
@@ -193,6 +194,7 @@ async function searchAgentCandidates(
const results = await fzf.find(normalizedPattern, { const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW, limit: MAX_SUGGESTIONS_TO_SHOW,
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return results.map((r: { item: Suggestion }) => r.item); return results.map((r: { item: Suggestion }) => r.item);
} }
+5 -1
View File
@@ -193,7 +193,11 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
const wrappedRows = styledRows.map((row) => wrapAndProcessRow(row)); const wrappedRows = styledRows.map((row) => wrapAndProcessRow(row));
// Use the TIGHTEST widths that fit the wrapped content + padding // 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 }; return { wrappedHeaders, wrappedRows, adjustedWidths };
}, [styledHeaders, styledRows, terminalWidth]); }, [styledHeaders, styledRows, terminalWidth]);
+1 -1
View File
@@ -472,7 +472,7 @@ export class ActivityLogger extends EventEmitter {
body, body,
pending: true, 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]); return (oldEnd as any).apply(this, [chunk, ...etc]);
}; };
+1
View File
@@ -98,6 +98,7 @@ function resolveEnvVarsInObjectInternal<T>(
visited.add(obj); visited.add(obj);
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
const result = obj.map((item) => const result = obj.map((item) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
resolveEnvVarsInObjectInternal(item, visited, customEnv), resolveEnvVarsInObjectInternal(item, visited, customEnv),
) as unknown as T; ) as unknown as T;
visited.delete(obj); visited.delete(obj);
+1
View File
@@ -83,6 +83,7 @@ export const getLatestGitHubRelease = async (
if (!releaseTag) { if (!releaseTag) {
throw new Error(`Response did not include tag_name field`); throw new Error(`Response did not include tag_name field`);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return releaseTag; return releaseTag;
} catch (_error) { } catch (_error) {
debugLogger.debug( debugLogger.debug(
+1
View File
@@ -40,6 +40,7 @@ export function tryParseJSON(input: string): object | null {
if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null; if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return parsed; return parsed;
} catch (_err) { } catch (_err) {
return null; return null;
+1
View File
@@ -374,6 +374,7 @@ export function setPendingSettingValue(
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const newSettings = JSON.parse(JSON.stringify(pendingSettings)); const newSettings = JSON.parse(JSON.stringify(pendingSettings));
setNestedValue(newSettings, path, value); setNestedValue(newSettings, path, value);
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return newSettings; return newSettings;
} }
@@ -29,6 +29,7 @@ export class AcpFileSystemService implements FileSystemService {
sessionId: this.sessionId, sessionId: this.sessionId,
}); });
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return response.content; return response.content;
} }
+1
View File
@@ -636,6 +636,7 @@ async function fetchCachedCredentials(): Promise<
for (const keyFile of pathsToTry) { for (const keyFile of pathsToTry) {
try { try {
const keyFileString = await fs.readFile(keyFile, 'utf-8'); const keyFileString = await fs.readFile(keyFile, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(keyFileString); return JSON.parse(keyFileString);
} catch (error) { } catch (error) {
// Log specific error for debugging, but continue trying other paths // Log specific error for debugging, but continue trying other paths
@@ -59,6 +59,7 @@ export class ProjectRegistry {
try { try {
const content = await fs.promises.readFile(this.registryPath, 'utf8'); const content = await fs.promises.readFile(this.registryPath, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(content); return JSON.parse(content);
} catch (e) { } catch (e) {
debugLogger.debug('Failed to load registry: ', e); debugLogger.debug('Failed to load registry: ', e);
+3 -4
View File
@@ -13,21 +13,19 @@ import type {
GenerateContentConfig, GenerateContentConfig,
} from '@google/genai'; } from '@google/genai';
import type { Config } from '../config/config.js'; import type { Config } from '../config/config.js';
import type { ContentGenerator } from './contentGenerator.js'; import type { ContentGenerator, AuthType } from './contentGenerator.js';
import type { AuthType } from './contentGenerator.js';
import { handleFallback } from '../fallback/handler.js'; import { handleFallback } from '../fallback/handler.js';
import { getResponseText } from '../utils/partUtils.js'; import { getResponseText } from '../utils/partUtils.js';
import { reportError } from '../utils/errorReporting.js'; import { reportError } from '../utils/errorReporting.js';
import { getErrorMessage } from '../utils/errors.js'; import { getErrorMessage } from '../utils/errors.js';
import { logMalformedJsonResponse } from '../telemetry/loggers.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 { retryWithBackoff } from '../utils/retry.js';
import type { ModelConfigKey } from '../services/modelConfigService.js'; import type { ModelConfigKey } from '../services/modelConfigService.js';
import { import {
applyModelSelection, applyModelSelection,
createAvailabilityContextProvider, createAvailabilityContextProvider,
} from '../availability/policyHelpers.js'; } from '../availability/policyHelpers.js';
import { LlmRole } from '../telemetry/types.js';
const DEFAULT_MAX_ATTEMPTS = 5; const DEFAULT_MAX_ATTEMPTS = 5;
@@ -164,6 +162,7 @@ export class BaseLlmClient {
); );
// If we are here, the content is valid (not empty and parsable). // If we are here, the content is valid (not empty and parsable).
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse( return JSON.parse(
this.cleanJsonResponse(getResponseText(result)!.trim(), model), this.cleanJsonResponse(getResponseText(result)!.trim(), model),
); );
@@ -83,6 +83,7 @@ export class FakeContentGenerator implements ContentGenerator {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
role: LlmRole, role: LlmRole,
): Promise<GenerateContentResponse> { ): Promise<GenerateContentResponse> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.setPrototypeOf( return Object.setPrototypeOf(
this.getNextResponse('generateContent', request), this.getNextResponse('generateContent', request),
GenerateContentResponse.prototype, GenerateContentResponse.prototype,
@@ -116,6 +117,7 @@ export class FakeContentGenerator implements ContentGenerator {
async embedContent( async embedContent(
request: EmbedContentParameters, request: EmbedContentParameters,
): Promise<EmbedContentResponse> { ): Promise<EmbedContentResponse> {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return Object.setPrototypeOf( return Object.setPrototypeOf(
this.getNextResponse('embedContent', request), this.getNextResponse('embedContent', request),
EmbedContentResponse.prototype, EmbedContentResponse.prototype,
+1
View File
@@ -348,6 +348,7 @@ export class IdeClient {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsedJson = JSON.parse(textPart.text); const parsedJson = JSON.parse(textPart.text);
if (parsedJson && typeof parsedJson.content === 'string') { if (parsedJson && typeof parsedJson.content === 'string') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return parsedJson.content; return parsedJson.content;
} }
if (parsedJson && parsedJson.content === null) { if (parsedJson && parsedJson.content === null) {
@@ -123,6 +123,7 @@ export async function getConnectionConfigFromFile(
`gemini-ide-server-${pid}.json`, `gemini-ide-server-${pid}.json`,
); );
const portFileContents = await fs.promises.readFile(portFile, 'utf8'); const portFileContents = await fs.promises.readFile(portFile, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(portFileContents); return JSON.parse(portFileContents);
} catch (_) { } catch (_) {
// For newer extension versions, the file name matches the pattern // For newer extension versions, the file name matches the pattern
@@ -167,6 +168,7 @@ export async function getConnectionConfigFromFile(
} }
const parsedContents = fileContents.map((content) => { const parsedContents = fileContents.map((content) => {
try { try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(content); return JSON.parse(content);
} catch (e) { } catch (e) {
logger.debug('Failed to parse JSON from config file: ', e); logger.debug('Failed to parse JSON from config file: ', e);
@@ -196,6 +198,7 @@ export async function getConnectionConfigFromFile(
if (fileIndex !== -1) { if (fileIndex !== -1) {
logger.debug(`Selected IDE connection file: ${matchingFiles[fileIndex]}`); logger.debug(`Selected IDE connection file: ${matchingFiles[fileIndex]}`);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return selected; return selected;
} }
@@ -213,6 +216,7 @@ export async function getConnectionConfigFromFile(
`Selected IDE connection file (matched port from env): ${matchingFiles[fileIndex]}`, `Selected IDE connection file (matched port from env): ${matchingFiles[fileIndex]}`,
); );
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return selected; return selected;
} }
} }
@@ -225,6 +229,7 @@ export async function getConnectionConfigFromFile(
`Selected first valid IDE connection file: ${matchingFiles[fileIndex]}`, `Selected first valid IDE connection file: ${matchingFiles[fileIndex]}`,
); );
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return selected; return selected;
} }
@@ -419,6 +419,7 @@ export class ChatRecordingService {
private readConversation(): ConversationRecord { private readConversation(): ConversationRecord {
try { try {
this.cachedLastConvData = fs.readFileSync(this.conversationFile!, 'utf8'); this.cachedLastConvData = fs.readFileSync(this.conversationFile!, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(this.cachedLastConvData); return JSON.parse(this.cachedLastConvData);
} catch (error) { } catch (error) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
@@ -149,6 +149,7 @@ class RecursiveFileSearch implements FileSearch {
filteredCandidates = await this.fzf filteredCandidates = await this.fzf
.find(pattern) .find(pattern)
.then((results: Array<FzfResultItem<string>>) => .then((results: Array<FzfResultItem<string>>) =>
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
results.map((entry: FzfResultItem<string>) => entry.item), results.map((entry: FzfResultItem<string>) => entry.item),
) )
.catch(() => { .catch(() => {
@@ -27,6 +27,7 @@ export function safeJsonStringify(
} }
seen.add(value); seen.add(value);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value; return value;
}, },
space, space,
@@ -60,6 +61,7 @@ export function safeJsonStringifyBooleanValuesOnly(obj: any): string {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
if ((value as Config) !== null && !configSeen) { if ((value as Config) !== null && !configSeen) {
configSeen = true; configSeen = true;
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value; return value;
} }
if (typeof value === 'boolean') { if (typeof value === 'boolean') {
+4
View File
@@ -91,8 +91,10 @@ export function createWorkingStdio() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const value = Reflect.get(target, prop, receiver); const value = Reflect.get(target, prop, receiver);
if (typeof value === 'function') { if (typeof value === 'function') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value.bind(target); return value.bind(target);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value; return value;
}, },
}); });
@@ -105,8 +107,10 @@ export function createWorkingStdio() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const value = Reflect.get(target, prop, receiver); const value = Reflect.get(target, prop, receiver);
if (typeof value === 'function') { if (typeof value === 'function') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value.bind(target); return value.bind(target);
} }
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return value; return value;
}, },
}); });