From 8b07159c3594ff2d7222af14ebb243bc62fe246f Mon Sep 17 00:00:00 2001 From: Supra4E8C Date: Wed, 12 Nov 2025 12:04:58 +0800 Subject: [PATCH] refactor(app.js): improve API key handling and display logic - Enhanced the masking and display of API keys to ensure proper handling of null or undefined values. - Updated the HTML rendering logic to use normalized and escaped key values for better security and consistency. - Improved the handling of key arguments in button actions to prevent potential issues with special characters. - Refactored the file name handling in the UI to ensure proper escaping and display of file names. --- app.js | 74 ++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 46 insertions(+), 28 deletions(-) diff --git a/app.js b/app.js index 5c4a126..3939d6f 100644 --- a/app.js +++ b/app.js @@ -2432,14 +2432,18 @@ class CLIProxyManager { return; } - container.innerHTML = keys.map((key, index) => ` + container.innerHTML = keys.map((key, index) => { + const normalizedKey = typeof key === 'string' ? key : String(key ?? ''); + const maskedDisplay = this.escapeHtml(this.maskApiKey(normalizedKey)); + const keyArgument = JSON.stringify(normalizedKey).replace(/"/g, '"'); + return `
${i18n.t('api_keys.item_title')} #${index + 1}
-
${this.maskApiKey(key)}
+
${maskedDisplay}
-
- `).join(''); + `; + }).join(''); } // 遮蔽API密钥显示 maskApiKey(key) { - if (key.length > 8) { - return key.substring(0, 4) + '...' + key.substring(key.length - 4); - } else if (key.length > 4) { - return key.substring(0, 2) + '...' + key.substring(key.length - 2); - } else if (key.length > 2) { - return key.substring(0, 1) + '...' + key.substring(key.length - 1); + if (key === null || key === undefined) { + return ''; } - return key; + const normalizedKey = typeof key === 'string' ? key : String(key); + if (normalizedKey.length > 8) { + return normalizedKey.substring(0, 4) + '...' + normalizedKey.substring(normalizedKey.length - 4); + } else if (normalizedKey.length > 4) { + return normalizedKey.substring(0, 2) + '...' + normalizedKey.substring(normalizedKey.length - 2); + } else if (normalizedKey.length > 2) { + return normalizedKey.substring(0, 1) + '...' + normalizedKey.substring(normalizedKey.length - 1); + } + return normalizedKey; } // HTML 转义,防止 XSS @@ -2794,7 +2803,8 @@ class CLIProxyManager { container.innerHTML = normalizedList.map((config, index) => { const rawKey = config['api-key'] || ''; - const masked = rawKey ? this.maskApiKey(rawKey) : ''; + const masked = this.maskApiKey(rawKey || ''); + const maskedDisplay = this.escapeHtml(masked); const keyStats = (rawKey && (stats[rawKey] || stats[masked])) || { success: 0, failure: 0 }; const configJson = JSON.stringify(config).replace(/"/g, '"'); const apiKeyJson = JSON.stringify(rawKey || '').replace(/"/g, '"'); @@ -2803,7 +2813,7 @@ class CLIProxyManager {
${i18n.t('ai_providers.gemini_item_title')} #${index + 1}
-
${this.maskApiKey(rawKey || '')}
+
${maskedDisplay}
${baseUrl ? `
${i18n.t('common.base_url')}: ${this.escapeHtml(baseUrl)}
` : ''} ${this.renderHeaderBadges(config.headers)}
@@ -3070,14 +3080,16 @@ class CLIProxyManager { const stats = keyStats; container.innerHTML = list.map((config, index) => { - const rawKey = config['api-key']; - const masked = rawKey ? this.maskApiKey(rawKey) : ''; + const rawKey = config['api-key'] || ''; + const masked = this.maskApiKey(rawKey || ''); + const maskedDisplay = this.escapeHtml(masked); const keyStats = (rawKey && (stats[rawKey] || stats[masked])) || { success: 0, failure: 0 }; + const deleteArg = JSON.stringify(rawKey).replace(/"/g, '"'); return `
${i18n.t('ai_providers.codex_item_title')} #${index + 1}
-
${i18n.t('common.api_key')}: ${this.maskApiKey(config['api-key'])}
+
${i18n.t('common.api_key')}: ${maskedDisplay}
${config['base-url'] ? `
${i18n.t('common.base_url')}: ${this.escapeHtml(config['base-url'])}
` : ''} ${config['proxy-url'] ? `
${i18n.t('common.proxy_url')}: ${this.escapeHtml(config['proxy-url'])}
` : ''} ${this.renderHeaderBadges(config.headers)} @@ -3094,7 +3106,7 @@ class CLIProxyManager { -
@@ -3302,14 +3314,16 @@ class CLIProxyManager { const stats = keyStats; container.innerHTML = list.map((config, index) => { - const rawKey = config['api-key']; - const masked = rawKey ? this.maskApiKey(rawKey) : ''; + const rawKey = config['api-key'] || ''; + const masked = this.maskApiKey(rawKey || ''); + const maskedDisplay = this.escapeHtml(masked); const keyStats = (rawKey && (stats[rawKey] || stats[masked])) || { success: 0, failure: 0 }; + const deleteArg = JSON.stringify(rawKey).replace(/"/g, '"'); return `
${i18n.t('ai_providers.claude_item_title')} #${index + 1}
-
${i18n.t('common.api_key')}: ${this.maskApiKey(config['api-key'])}
+
${i18n.t('common.api_key')}: ${maskedDisplay}
${config['base-url'] ? `
${i18n.t('common.base_url')}: ${this.escapeHtml(config['base-url'])}
` : ''} ${config['proxy-url'] ? `
${i18n.t('common.proxy_url')}: ${this.escapeHtml(config['proxy-url'])}
` : ''} ${this.renderHeaderBadges(config.headers)} @@ -3326,7 +3340,7 @@ class CLIProxyManager { -
@@ -3572,6 +3586,7 @@ class CLIProxyManager { totalFailure += keyStats.failure; }); + const deleteArg = JSON.stringify(name).replace(/"/g, '"'); return `
@@ -3594,7 +3609,7 @@ class CLIProxyManager { -
@@ -3865,14 +3880,16 @@ class CLIProxyManager { this.updateFilterButtons(existingTypes); container.innerHTML = visibleFiles.map(file => { + const rawFileName = typeof file.name === 'string' ? file.name : ''; + const safeFileName = this.escapeHtml(rawFileName); // 认证文件的统计匹配逻辑: // 1. 首先尝试完整文件名匹配 // 2. 如果没有匹配,尝试脱敏文件名匹配(去掉扩展名后的脱敏版本) - let fileStats = stats[file.name] || { success: 0, failure: 0 }; + let fileStats = stats[rawFileName] || { success: 0, failure: 0 }; // 如果完整文件名没有统计,尝试基于文件名的脱敏版本匹配 if (fileStats.success === 0 && fileStats.failure === 0) { - const nameWithoutExt = file.name.replace(/\.[^/.]+$/, ""); // 去掉扩展名 + const nameWithoutExt = rawFileName.replace(/\.[^/.]+$/, ""); // 去掉扩展名 const possibleSources = []; @@ -3959,7 +3976,7 @@ class CLIProxyManager {
虚拟认证文件
` : ` -
+
@@ -3972,9 +3989,9 @@ class CLIProxyManager {
`; return ` -
+
-
${typeBadge}${file.name}
+
${typeBadge}${safeFileName}
${i18n.t('auth_files.file_modified')}: ${new Date(file.modtime).toLocaleString(i18n.currentLanguage === 'zh-CN' ? 'zh-CN' : 'en-US')} ${i18n.t('auth_files.file_size')}: ${this.formatFileSize(file.size)} @@ -4463,11 +4480,12 @@ class CLIProxyManager { // 显示JSON模态框 showJsonModal(filename, jsonContent) { // 创建模态框HTML + const safeFilename = this.escapeHtml(filename || ''); const modalHtml = `
-

${filename}

+

${safeFilename}