Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export default tseslint.config(
ignores: ['**/*.test.ts', '**/*.test.tsx'],
rules: {
'@typescript-eslint/no-unsafe-type-assertion': 'error',
'@typescript-eslint/no-unsafe-assignment': 'error',
},
},
{
Expand Down
1 change: 1 addition & 0 deletions packages/a2a-server/src/commands/restore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export class RestoreCommand implements Command {
throw error;
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const toolCallData = JSON.parse(data);
const ToolCallDataSchema = getToolCallDataSchema();
const parseResult = ToolCallDataSchema.safeParse(toolCallData);
Expand Down
4 changes: 2 additions & 2 deletions packages/a2a-server/src/config/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import * as fs from 'node:fs';
import * as path from 'node:path';

import type { MCPServerConfig } from '@google/gemini-cli-core';
import {
import type { MCPServerConfig ,
debugLogger,
GEMINI_DIR,
getErrorMessage,
Expand Down Expand Up @@ -122,6 +121,7 @@ export function loadSettings(workspaceDir: string): Settings {
function resolveEnvVarsInString(value: string): string {
const envVarRegex = /\$(?:(\w+)|{([^}]+)})/g; // Find $VAR_NAME or ${VAR_NAME}
return value.replace(envVarRegex, (match, varName1, varName2) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const varName = varName1 || varName2;
if (process && process.env && typeof process.env[varName] === 'string') {
return process.env[varName];
Expand Down
8 changes: 4 additions & 4 deletions packages/a2a-server/src/http/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
import express from 'express';

import type { AgentCard, Message } from '@a2a-js/sdk';
import type { TaskStore } from '@a2a-js/sdk/server';
import {
import type { TaskStore ,
DefaultRequestHandler,
InMemoryTaskStore,
DefaultExecutionEventBus,
Expand All @@ -25,9 +24,8 @@ import { loadConfig, loadEnvironment, setTargetDir } from '../config/config.js';
import { loadSettings } from '../config/settings.js';
import { loadExtensions } from '../config/extension.js';
import { commandRegistry } from '../commands/command-registry.js';
import { debugLogger, SimpleExtensionLoader } from '@google/gemini-cli-core';
import { debugLogger, SimpleExtensionLoader , GitService } from '@google/gemini-cli-core';
import type { Command, CommandArgument } from '../commands/types.js';
import { GitService } from '@google/gemini-cli-core';

type CommandResponse = {
name: string;
Expand Down Expand Up @@ -88,6 +86,7 @@ async function handleExecuteCommand(
},
) {
logger.info('[CoreAgent] Received /executeCommand request: ', req.body);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const { command, args } = req.body;
try {
if (typeof command !== 'string') {
Expand Down Expand Up @@ -211,6 +210,7 @@ export async function createApp() {
const agentSettings = req.body.agentSettings as
| AgentSettings
| undefined;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const contextId = req.body.contextId || uuidv4();
const wrapper = await agentExecutor.createTask(
taskId,
Expand Down
3 changes: 3 additions & 0 deletions packages/a2a-server/src/persistence/gcs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ export class GCSTaskStore implements TaskStore {
}
const [compressedMetadata] = await metadataFile.download();
const jsonData = gunzipSync(compressedMetadata).toString();
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const loadedMetadata = JSON.parse(jsonData);
Comment on lines +249 to 250
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Assigning the result of JSON.parse to loadedMetadata without runtime validation is unsafe, as the data from GCS might not conform to the expected shape. This could lead to runtime errors when properties like _contextId are accessed later. Since Zod is already used in the project, I recommend defining a Zod schema for the metadata and using schema.parse() or schema.safeParse() to validate the data after parsing. This will ensure type safety and prevent potential crashes.

logger.info(`Task ${taskId} metadata loaded from GCS.`);

Expand Down Expand Up @@ -282,12 +283,14 @@ export class GCSTaskStore implements TaskStore {

return {
id: taskId,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
contextId: loadedMetadata._contextId || uuidv4(),
kind: 'task',
status: {
state: persistedState._taskState,
timestamp: new Date().toISOString(),
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
metadata: loadedMetadata,
history: [],
artifacts: [],
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/config/extension-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ Would you like to attempt to install via "git clone" instead?`,

try {
const hooksContent = await fs.promises.readFile(hooksFilePath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const rawHooks = JSON.parse(hooksContent);

if (
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/config/extensionRegistryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export class ExtensionRegistryClient {
`${ext.extensionName} ${ext.extensionDescription} ${ext.fullName}`,
fuzzy: true,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(query);
return results.map((r: { item: RegistryExtension }) => r.item);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/test-utils/AppRig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,14 @@ export class AppRig {
}

private stubRefreshAuth() {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
const gcConfig = this.config as any;
gcConfig.refreshAuth = async (authMethod: AuthType) => {
gcConfig.modelAvailabilityService.reset();

const newContentGeneratorConfig = {
authType: authMethod,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
proxy: gcConfig.getProxy(),
apiKey: process.env['GEMINI_API_KEY'] || 'test-api-key',
};
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/test-utils/customMatchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import type { TextBuffer } from '../ui/components/shared/text-buffer.js';
const invalidCharsRegex = /[\b\x1b]/;

function toHaveOnlyValidCharacters(this: Assertion, buffer: TextBuffer) {
// 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-assignment
const { isNot } = this as any;
let pass = true;
const invalidLines: Array<{ line: number; content: string }> = [];
Expand Down
9 changes: 7 additions & 2 deletions packages/cli/src/test-utils/mockCommandContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export const createMockCommandContext = (
forScope: vi.fn().mockReturnValue({ settings: {} }),
} as unknown as LoadedSettings,
git: undefined as GitService | undefined,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
logger: {
log: vi.fn(),
logMessage: vi.fn(),
Expand All @@ -54,7 +54,7 @@ export const createMockCommandContext = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any, // Cast because Logger is a class.
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
ui: {
addItem: vi.fn(),
clear: vi.fn(),
Expand Down Expand Up @@ -94,21 +94,26 @@ export const createMockCommandContext = (

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const merge = (target: any, source: any): any => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const output = { ...target };

for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const sourceValue = source[key];
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const targetValue = output[key];

if (
// We only want to recursively merge plain objects
Object.prototype.toString.call(sourceValue) === '[object Object]' &&
Object.prototype.toString.call(targetValue) === '[object Object]'
) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
output[key] = merge(targetValue, sourceValue);
} else {
// If not, we do a direct assignment. This preserves Date objects and others.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
output[key] = sourceValue;
}
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/src/test-utils/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const createMockSettings = (
workspace,
isTrusted,
errors,
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
merged: mergedOverride,
...settingsOverrides
} = overrides;
Expand Down Expand Up @@ -75,7 +76,7 @@ export const createMockSettings = (
// Assign any function overrides (e.g., vi.fn() for methods)
for (const key in overrides) {
if (typeof overrides[key] === 'function') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion, @typescript-eslint/no-unsafe-assignment
(loaded as any)[key] = overrides[key];
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/components/SettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ export function SettingsDialog({
}

const doSearch = async () => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzfInstance.find(searchQuery);

if (!active) return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ Return a JSON object with:
'--limit',
String(limit),
]);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const issues: Issue[] = JSON.parse(stdout);
Comment on lines +454 to 455
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Directly casting the result of JSON.parse(stdout) to Issue[] is unsafe. The output from the gh command is not guaranteed to match the Issue interface, which could lead to runtime errors. It would be safer to parse this into an unknown type and then validate its structure, for instance using a Zod schema, before casting it to Issue[].

if (issues.length === 0) {
setState((s) => ({
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/components/triage/TriageIssues.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export const TriageIssues = ({
'--limit',
String(limit),
]);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const issues: Issue[] = JSON.parse(stdout);
if (issues.length === 0) {
setState((s) => ({
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/ui/hooks/useAtCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ async function searchResourceCandidates(
const fzf = new AsyncFzf(candidates, {
selector: (candidate: ResourceSuggestionCandidate) => candidate.searchKey,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW * 3,
});
Expand All @@ -188,6 +189,7 @@ async function searchAgentCandidates(
const fzf = new AsyncFzf(candidates, {
selector: (s: Suggestion) => s.label,
});
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW,
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/hooks/useSessionBrowser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export const useSessionBrowser = (
const originalFilePath = path.join(chatsDir, fileName);

// Load up the conversation.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const conversation: ConversationRecord = JSON.parse(
await fs.readFile(originalFilePath, 'utf8'),
);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/hooks/useSlashCompletion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ function useCommandSuggestions(
const fzfInstance = getFzfForCommands(commandsToSearch);
if (fzfInstance) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const fzfResults = await fzfInstance.fzf.find(partial);
if (signal.aborted) return;
const uniqueCommands = new Set<SlashCommand>();
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/hooks/useStateAndRef.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export const useStateAndRef = <
(newStateOrCallback) => {
let newValue: T;
if (typeof newStateOrCallback === 'function') {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
newValue = newStateOrCallback(ref.current);
} else {
newValue = newStateOrCallback;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/utils/TableRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,7 @@ export const TableRenderer: React.FC<TableRendererProps> = ({
isHeader = false,
): React.ReactNode => {
const renderedCells = cells.map((cell, index) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const width = adjustedWidths[index] || 0;
return renderCell(cell, width, isHeader);
});
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/utils/terminalSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ async function configureVSCodeStyle(
await backupFile(keybindingsFile);
try {
const cleanContent = stripJsonComments(content);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsedContent = JSON.parse(cleanContent);
if (!Array.isArray(parsedContent)) {
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/ui/utils/textUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,9 @@ export function escapeAnsiCtrlCodes<T>(obj: T): T {
let newArr: unknown[] | null = null;

for (let i = 0; i < obj.length; i++) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const value = obj[i];
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const escapedValue = escapeAnsiCtrlCodes(value);
if (escapedValue !== value) {
if (newArr === null) {
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/envVarResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function resolveEnvVarsInString(
): string {
const envVarRegex = /\$(?:(\w+)|{([^}]+)})/g; // Find $VAR_NAME or ${VAR_NAME}
return value.replace(envVarRegex, (match, varName1, varName2) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const varName = varName1 || varName2;
if (customEnv && typeof customEnv[varName] === 'string') {
return customEnv[varName];
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/gitUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const getLatestGitHubRelease = async (
);
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const releaseTag = (await response.json()).tag_name;
if (!releaseTag) {
throw new Error(`Response did not include tag_name field`);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/jsonoutput.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export function tryParseJSON(input: string): object | null {
if (!checkInput(input)) return null;
const trimmed = input.trim();
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(trimmed);
if (parsed === null || typeof parsed !== 'object') {
return null;
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/persistentState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export class PersistentState {
const filePath = this.getPath();
if (fs.existsSync(filePath)) {
const content = fs.readFileSync(filePath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.cache = JSON.parse(content);
} else {
this.cache = {};
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/readStdin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export async function readStdin(): Promise<string> {

const onReadable = () => {
let chunk;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
while ((chunk = process.stdin.read()) !== null) {
if (pipedInputTimerId) {
clearTimeout(pipedInputTimerId);
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/utils/sessionUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ export const getAllSessionFiles = async (
async (file): Promise<SessionFileEntry> => {
const filePath = path.join(chatsDir, file);
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const content: ConversationRecord = JSON.parse(
await fs.readFile(filePath, 'utf8'),
);
Expand Down Expand Up @@ -498,6 +499,7 @@ export class SessionSelector {
const sessionPath = path.join(chatsDir, sessionInfo.fileName);

try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const sessionData: ConversationRecord = JSON.parse(
await fs.readFile(sessionPath, 'utf8'),
);
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/utils/settingsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ export function setPendingSettingValue(
pendingSettings: Settings,
): Settings {
const path = key.split('.');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const newSettings = JSON.parse(JSON.stringify(pendingSettings));
Comment on lines +374 to 375
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Instead of using JSON.parse(JSON.stringify(pendingSettings)) for deep cloning, consider using structuredClone(pendingSettings). It's a modern, more performant, and safer way to deep-clone objects, and it's supported since you're on Node.js >= 20.

Suggested change
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const newSettings = JSON.parse(JSON.stringify(pendingSettings));
const newSettings = structuredClone(pendingSettings);

setNestedValue(newSettings, path, value);
return newSettings;
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/zed-integration/acpErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ function extractRecursiveMessage(input: string): string {
(trimmed.startsWith('[') && trimmed.endsWith(']'))
) {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const parsed = JSON.parse(trimmed);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const next =
parsed?.error?.message ||
parsed?.[0]?.error?.message ||
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/zed-integration/fileSystemService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class AcpFileSystemService implements FileSystemService {
return this.fallback.readTextFile(filePath);
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const response = await this.connection.readTextFile({
path: filePath,
sessionId: this.sessionId,
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/zed-integration/zedIntegration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ export class Session {
},
};

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const output = await this.connection.requestPermission(params);
const outcome =
output.outcome.outcome === CoreToolCallStatus.Cancelled
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/agents/acknowledgedAgents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export class AcknowledgedAgentsService {
const filePath = Storage.getAcknowledgedAgentsPath();
try {
const content = await fs.readFile(filePath, 'utf-8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
this.acknowledgedAgents = JSON.parse(content);
} catch (error: unknown) {
if (!isNodeError(error) || error.code !== 'ENOENT') {
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/agents/agent-scheduler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export async function scheduleAgentTools(
} = options;

// Create a proxy/override of the config to provide the agent-specific tool registry.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const agentConfig: Config = Object.create(config);
agentConfig.getToolRegistry = () => toolRegistry;

Expand Down
Loading
Loading