fix(form): use JSON parsing for Gemini common config snippet

Address audit finding: ProviderForm.tsx:944 was using envStringToObj()
(KEY=VALUE format) to parse Gemini common config, but it's stored as JSON.

Changes:
- Add parseGeminiCommonConfig() helper function
- Support both flat {"KEY": "VALUE"} and wrapped {"env": {...}} formats
- Properly extract difference between custom and common env

This prevents common config values from being incorrectly saved as
custom config when the formats don't match.
This commit is contained in:
YoVinchen
2026-01-26 17:11:18 +08:00
parent e393dda68e
commit 1e1080c813
@@ -67,6 +67,36 @@ import {
} from "./hooks";
import { useProvidersQuery } from "@/lib/query/queries";
/**
* Parse Gemini common config snippet.
* Supports two formats:
* - Flat JSON: {"KEY": "VALUE", ...}
* - Wrapped JSON: {"env": {"KEY": "VALUE", ...}}
*
* Returns empty object if parsing fails.
*/
function parseGeminiCommonConfig(snippet: string): Record<string, string> {
try {
const parsed = JSON.parse(snippet);
if (!parsed || typeof parsed !== "object") {
return {};
}
// Check if it's wrapped format {"env": {...}}
const envObj =
parsed.env && typeof parsed.env === "object" ? parsed.env : parsed;
// Convert to string record
const result: Record<string, string> = {};
for (const [key, value] of Object.entries(envObj)) {
if (typeof value === "string") {
result[key] = value;
}
}
return result;
} catch {
return {};
}
}
const CLAUDE_DEFAULT_CONFIG = JSON.stringify({ env: {} }, null, 2);
const CODEX_DEFAULT_CONFIG = JSON.stringify({ auth: {}, config: "" }, null, 2);
const GEMINI_DEFAULT_CONFIG = JSON.stringify(
@@ -941,7 +971,11 @@ export function ProviderForm({
const configObj = geminiConfig.trim() ? JSON.parse(geminiConfig) : {};
// 如果启用了通用配置,只保存与通用配置不同的部分(自定义配置)
if (useGeminiCommonConfigFlag && geminiCommonConfigSnippet.trim()) {
const commonEnvObj = envStringToObj(geminiCommonConfigSnippet.trim());
// Parse common config as JSON (flat {"KEY": "VALUE"} format)
// Note: geminiCommonConfigSnippet is stored as JSON by useGeminiCommonConfig hook
const commonEnvObj = parseGeminiCommonConfig(
geminiCommonConfigSnippet.trim(),
);
if (isPlainObject(envObj) && isPlainObject(commonEnvObj)) {
const { customConfig } = extractDifference(envObj, commonEnvObj);
// Convert to string record with type guard to avoid type assertion issues