mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-23 20:40:41 -07:00
Disallow unsafe type assertions (#18688)
This commit is contained in:
committed by
GitHub
parent
bce1caefd0
commit
fd65416a2f
@@ -80,6 +80,7 @@ export async function bfsFileSearch(
|
||||
return { currentDir, entries };
|
||||
} catch (error) {
|
||||
// Warn user that a directory could not be read, as this affects search results.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const message = (error as Error)?.message ?? 'Unknown error';
|
||||
debugLogger.warn(
|
||||
`[WARN] Skipping unreadable directory: ${currentDir} (${message})`,
|
||||
@@ -153,6 +154,7 @@ export function bfsFileSearchSync(
|
||||
foundFiles,
|
||||
);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const message = (error as Error)?.message ?? 'Unknown error';
|
||||
debugLogger.warn(
|
||||
`[WARN] Skipping unreadable directory: ${currentDir} (${message})`,
|
||||
|
||||
@@ -49,6 +49,7 @@ export function generateCheckpointFileName(
|
||||
toolCall: ToolCallRequestInfo,
|
||||
): string | null {
|
||||
const toolArgs = toolCall.args;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const toolFilePath = toolArgs['file_path'] as string;
|
||||
|
||||
if (!toolFilePath) {
|
||||
@@ -167,6 +168,7 @@ export function getCheckpointInfoList(
|
||||
|
||||
for (const [file, content] of checkpointFiles) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const toolCallData = JSON.parse(content) as ToolCallData;
|
||||
if (toolCallData.messageId) {
|
||||
checkpointInfoList.push({
|
||||
|
||||
@@ -208,9 +208,12 @@ export async function resolveEditorAsync(
|
||||
|
||||
coreEvents.emit(CoreEvent.RequestEditorSelection);
|
||||
|
||||
return once(coreEvents, CoreEvent.EditorSelected, { signal })
|
||||
.then(([payload]) => (payload as EditorSelectedPayload).editor)
|
||||
.catch(() => undefined);
|
||||
return (
|
||||
once(coreEvents, CoreEvent.EditorSelected, { signal })
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
.then(([payload]) => (payload as EditorSelectedPayload).editor)
|
||||
.catch(() => undefined)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -98,6 +98,7 @@ interface ResponseData {
|
||||
|
||||
export function toFriendlyError(error: unknown): unknown {
|
||||
if (error && typeof error === 'object' && 'response' in error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const gaxiosError = error as GaxiosError;
|
||||
const data = parseResponseData(gaxiosError);
|
||||
if (data && data.error && data.error.message && data.error.code) {
|
||||
@@ -122,11 +123,13 @@ function parseResponseData(error: GaxiosError): ResponseData | undefined {
|
||||
// Inexplicably, Gaxios sometimes doesn't JSONify the response data.
|
||||
if (typeof error.response?.data === 'string') {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return JSON.parse(error.response?.data) as ResponseData;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return error.response?.data as ResponseData | undefined;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,14 +199,14 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
|
||||
if (this._eventBacklog.length >= CoreEventEmitter.MAX_BACKLOG_SIZE) {
|
||||
this._eventBacklog.shift();
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
this._eventBacklog.push({ event, args } as EventBacklogItem);
|
||||
} else {
|
||||
(
|
||||
this.emit as <K extends keyof CoreEvents>(
|
||||
event: K,
|
||||
...args: CoreEvents[K]
|
||||
) => boolean
|
||||
)(event, ...args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(this.emit as (event: K, ...args: CoreEvents[K]) => boolean)(
|
||||
event,
|
||||
...args,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,12 +319,11 @@ export class CoreEventEmitter extends EventEmitter<CoreEvents> {
|
||||
const backlog = [...this._eventBacklog];
|
||||
this._eventBacklog.length = 0; // Clear in-place
|
||||
for (const item of backlog) {
|
||||
(
|
||||
this.emit as <K extends keyof CoreEvents>(
|
||||
event: K,
|
||||
...args: CoreEvents[K]
|
||||
) => boolean
|
||||
)(item.event, ...item.args);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(this.emit as (event: keyof CoreEvents, ...args: unknown[]) => boolean)(
|
||||
item.event,
|
||||
...item.args,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@ export function convertToFunctionResponse(
|
||||
if (inlineDataParts.length > 0) {
|
||||
if (isMultimodalFRSupported) {
|
||||
// Nest inlineData if supported by the model
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(part.functionResponse as unknown as { parts: Part[] }).parts =
|
||||
inlineDataParts;
|
||||
} else {
|
||||
@@ -151,6 +152,7 @@ export function getFunctionCalls(
|
||||
}
|
||||
const functionCallParts = parts
|
||||
.filter((part) => !!part.functionCall)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
.map((part) => part.functionCall as FunctionCall);
|
||||
return functionCallParts.length > 0 ? functionCallParts : undefined;
|
||||
}
|
||||
@@ -163,6 +165,7 @@ export function getFunctionCallsFromParts(
|
||||
}
|
||||
const functionCallParts = parts
|
||||
.filter((part) => !!part.functionCall)
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
.map((part) => part.functionCall as FunctionCall);
|
||||
return functionCallParts.length > 0 ? functionCallParts : undefined;
|
||||
}
|
||||
|
||||
@@ -195,6 +195,7 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
|
||||
if (Array.isArray(errorDetails)) {
|
||||
for (const detail of errorDetails) {
|
||||
if (detail && typeof detail === 'object') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const detailObj = detail as Record<string, unknown>;
|
||||
const typeKey = Object.keys(detailObj).find(
|
||||
(key) => key.trim() === '@type',
|
||||
@@ -205,6 +206,7 @@ export function parseGoogleApiError(error: unknown): GoogleApiError | null {
|
||||
delete detailObj[typeKey];
|
||||
}
|
||||
// We can just cast it; the consumer will have to switch on @type
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
details.push(detailObj as unknown as GoogleApiErrorDetail);
|
||||
}
|
||||
}
|
||||
@@ -253,6 +255,7 @@ function fromGaxiosError(errorObj: object): ErrorShape | undefined {
|
||||
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
if ('error' in data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
outerError = (data as { error: ErrorShape }).error;
|
||||
}
|
||||
}
|
||||
@@ -309,6 +312,7 @@ function fromApiError(errorObj: object): ErrorShape | undefined {
|
||||
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
if ('error' in data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
outerError = (data as { error: ErrorShape }).error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,9 +24,10 @@ export function getErrorStatus(error: unknown): number | undefined {
|
||||
typeof (error as { response?: unknown }).response === 'object' &&
|
||||
(error as { response?: unknown }).response !== null
|
||||
) {
|
||||
const response = (
|
||||
error as { response: { status?: unknown; headers?: unknown } }
|
||||
).response;
|
||||
const response =
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(error as { response: { status?: unknown; headers?: unknown } })
|
||||
.response;
|
||||
if ('status' in response && typeof response.status === 'number') {
|
||||
return response.status;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,7 @@ async function generateJsonWithTimeout<T>(
|
||||
timeoutSignal,
|
||||
]),
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return result as T;
|
||||
} catch (err) {
|
||||
debugLogger.debug(
|
||||
|
||||
@@ -54,6 +54,7 @@ async function findProjectRoot(startDir: string): Promise<string | null> {
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'code' in error &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(error as { code: string }).code === 'ENOENT';
|
||||
|
||||
// Only log unexpected errors in non-test environments
|
||||
@@ -63,6 +64,7 @@ async function findProjectRoot(startDir: string): Promise<string | null> {
|
||||
|
||||
if (!isENOENT && !isTestEnv) {
|
||||
if (typeof error === 'object' && error !== null && 'code' in error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const fsError = error as { code: string; message: string };
|
||||
logger.warn(
|
||||
`Error checking for .git directory at ${gitPath}: ${fsError.message}`,
|
||||
@@ -311,6 +313,7 @@ export function concatenateInstructions(
|
||||
return instructionContents
|
||||
.filter((item) => typeof item.content === 'string')
|
||||
.map((item) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const trimmedContent = (item.content as string).trim();
|
||||
if (trimmedContent.length === 0) {
|
||||
return null;
|
||||
@@ -359,6 +362,7 @@ export async function loadGlobalMemory(
|
||||
.filter((item) => item.content !== null)
|
||||
.map((item) => ({
|
||||
path: item.filePath,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
content: item.content as string,
|
||||
})),
|
||||
};
|
||||
@@ -456,6 +460,7 @@ export async function loadEnvironmentMemory(
|
||||
.filter((item) => item.content !== null)
|
||||
.map((item) => ({
|
||||
path: item.filePath,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
content: item.content as string,
|
||||
})),
|
||||
};
|
||||
@@ -640,6 +645,7 @@ export async function loadJitSubdirectoryMemory(
|
||||
.filter((item) => item.content !== null)
|
||||
.map((item) => ({
|
||||
path: item.filePath,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
content: item.content as string,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -109,6 +109,7 @@ export async function checkNextSpeaker(
|
||||
];
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const parsedResponse = (await baseLlmClient.generateJson({
|
||||
modelConfigKey: { model: 'next-speaker-checker' },
|
||||
contents,
|
||||
|
||||
@@ -30,6 +30,7 @@ export function partToString(
|
||||
}
|
||||
|
||||
// Cast to Part, assuming it might contain project-specific fields
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const part = value as Part & {
|
||||
videoMetadata?: unknown;
|
||||
thought?: string;
|
||||
|
||||
@@ -20,7 +20,9 @@ export function isApiError(error: unknown): error is ApiError {
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'error' in error &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
typeof (error as ApiError).error === 'object' &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
'message' in (error as ApiError).error
|
||||
);
|
||||
}
|
||||
@@ -30,6 +32,7 @@ export function isStructuredError(error: unknown): error is StructuredError {
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'message' in error &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
typeof (error as StructuredError).message === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ function getNetworkErrorCode(error: unknown): string | undefined {
|
||||
return undefined;
|
||||
}
|
||||
if ('code' in obj && typeof (obj as { code: unknown }).code === 'string') {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return (obj as { code: string }).code;
|
||||
}
|
||||
return undefined;
|
||||
@@ -196,6 +197,7 @@ export async function retryWithBackoff<T>(
|
||||
|
||||
if (
|
||||
shouldRetryOnContent &&
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
shouldRetryOnContent(result as GenerateContentResponse)
|
||||
) {
|
||||
const jitter = currentDelay * 0.3 * (Math.random() * 2 - 1);
|
||||
@@ -327,6 +329,7 @@ export async function retryWithBackoff<T>(
|
||||
// Generic retry logic for other errors
|
||||
if (
|
||||
attempt >= maxAttempts ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
!shouldRetryOnError(error as Error, retryFetchErrors)
|
||||
) {
|
||||
throw error;
|
||||
|
||||
@@ -56,6 +56,7 @@ function removeEmptyObjects(data: any): object {
|
||||
export function safeJsonStringifyBooleanValuesOnly(obj: any): string {
|
||||
let configSeen = false;
|
||||
return JSON.stringify(removeEmptyObjects(obj), (key, value) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
if ((value as Config) !== null && !configSeen) {
|
||||
configSeen = true;
|
||||
return value;
|
||||
|
||||
@@ -12,9 +12,9 @@ import * as addFormats from 'ajv-formats';
|
||||
import { debugLogger } from './debugLogger.js';
|
||||
|
||||
// Ajv's ESM/CJS interop: use 'any' for compatibility as recommended by Ajv docs
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
const AjvClass = (AjvPkg as any).default || AjvPkg;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
const Ajv2020Class = (Ajv2020Pkg as any).default || Ajv2020Pkg;
|
||||
|
||||
const ajvOptions = {
|
||||
@@ -34,7 +34,7 @@ const ajvDefault: Ajv = new AjvClass(ajvOptions);
|
||||
// Draft-2020-12 validator for MCP servers using rmcp
|
||||
const ajv2020: Ajv = new Ajv2020Class(ajvOptions);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
const addFormatsFunc = (addFormats as any).default || addFormats;
|
||||
addFormatsFunc(ajvDefault);
|
||||
addFormatsFunc(ajv2020);
|
||||
@@ -90,6 +90,7 @@ export class SchemaValidator {
|
||||
// This matches LenientJsonSchemaValidator behavior in mcp-client.ts.
|
||||
debugLogger.warn(
|
||||
`Failed to compile schema (${
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(schema as Record<string, unknown>)?.['$schema'] ?? '<no $schema>'
|
||||
}): ${error instanceof Error ? error.message : String(error)}. ` +
|
||||
'Skipping parameter validation.',
|
||||
@@ -121,6 +122,7 @@ export class SchemaValidator {
|
||||
// Skip validation rather than blocking tool usage.
|
||||
debugLogger.warn(
|
||||
`Failed to validate schema (${
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
(schema as Record<string, unknown>)?.['$schema'] ?? '<no $schema>'
|
||||
}): ${error instanceof Error ? error.message : String(error)}. ` +
|
||||
'Skipping schema validation.',
|
||||
|
||||
@@ -66,6 +66,7 @@ export async function isDirectorySecure(
|
||||
} catch (error) {
|
||||
return {
|
||||
secure: false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
reason: `A security check for the system policy directory '${dirPath}' failed and could not be completed. Please file a bug report. Original error: ${(error as Error).message}`,
|
||||
};
|
||||
}
|
||||
@@ -93,11 +94,13 @@ export async function isDirectorySecure(
|
||||
|
||||
return { secure: true };
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
return { secure: true };
|
||||
}
|
||||
return {
|
||||
secure: false,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
reason: `Failed to access directory: ${(error as Error).message}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ function parseCommandTree(
|
||||
progressCallback: () => {
|
||||
if (performance.now() > deadline) {
|
||||
timedOut = true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
return true as unknown as void; // Returning true cancels parsing, but type says void
|
||||
}
|
||||
},
|
||||
|
||||
@@ -52,6 +52,26 @@ export function disableSimulationAfterFallback(): void {
|
||||
fallbackOccurred = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a simulated 429 error response
|
||||
*/
|
||||
export function createSimulated429Error(): Error {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const error = new Error('Rate limit exceeded (simulated)') as Error & {
|
||||
status: number;
|
||||
};
|
||||
error.status = 429;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset simulation state when switching auth methods
|
||||
*/
|
||||
export function resetSimulationState(): void {
|
||||
fallbackOccurred = false;
|
||||
resetRequestCounter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable 429 simulation programmatically (for tests)
|
||||
*/
|
||||
|
||||
@@ -88,6 +88,7 @@ function estimateFunctionResponseTokens(part: Part, depth: number): number {
|
||||
}
|
||||
|
||||
// Gemini 3: Handle nested multimodal parts recursively.
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const nestedParts = (fr as unknown as { parts?: Part[] }).parts;
|
||||
if (nestedParts && nestedParts.length > 0) {
|
||||
totalTokens += estimateTokenCountSync(nestedParts, depth + 1);
|
||||
|
||||
@@ -104,6 +104,7 @@ export function doesToolInvocationMatch(
|
||||
// This invocation has no command - nothing to check.
|
||||
continue;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
command = String((invocation.params as { command: string }).command);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ export class UserAccountManager {
|
||||
debugLogger.log('Invalid accounts file schema, starting fresh.');
|
||||
return defaultState;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const { active, old } = parsed as Partial<UserAccounts>;
|
||||
const isValid =
|
||||
(active === undefined || active === null || typeof active === 'string') &&
|
||||
|
||||
Reference in New Issue
Block a user