From 771e5e6c3e03123be23839c4c2e8640d57628426 Mon Sep 17 00:00:00 2001 From: suyao Date: Thu, 11 Dec 2025 11:51:44 +0800 Subject: [PATCH] fix(volcengine): address security and error handling issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add safeStorage availability check before encryption to prevent failures on unsupported platforms - Add file permission restriction (0o600) to credentials file for owner-only access - Improve error handling in listModels to differentiate credential/auth errors from network errors - Add error handler to useEffect for hasCredentials check with proper logging Addresses PR review comments from #11482 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/main/services/VolcengineService.ts | 5 +++++ .../clients/volcengine/VolcengineAPIClient.ts | 20 ++++++++++++++++--- .../ProviderSettings/VolcengineSettings.tsx | 9 ++++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/main/services/VolcengineService.ts b/src/main/services/VolcengineService.ts index 041e9cf650..67dc91738b 100644 --- a/src/main/services/VolcengineService.ts +++ b/src/main/services/VolcengineService.ts @@ -382,6 +382,10 @@ class VolcengineService { throw new VolcengineServiceError('Access Key ID and Secret Access Key are required') } + if (!safeStorage.isEncryptionAvailable()) { + throw new VolcengineServiceError('Secure storage is not available on this platform') + } + const credentials: VolcengineCredentials = { accessKeyId, secretAccessKey } const credentialsJson = JSON.stringify(credentials) const encryptedData = safeStorage.encryptString(credentialsJson) @@ -393,6 +397,7 @@ class VolcengineService { } await fs.promises.writeFile(this.credentialsFilePath, encryptedData) + await fs.promises.chmod(this.credentialsFilePath, 0o600) // Read/write for owner only logger.info('Volcengine credentials saved successfully') } catch (error) { logger.error('Failed to save Volcengine credentials:', error as Error) diff --git a/src/renderer/src/aiCore/legacy/clients/volcengine/VolcengineAPIClient.ts b/src/renderer/src/aiCore/legacy/clients/volcengine/VolcengineAPIClient.ts index 5b394c17ec..13aa5e3d8d 100644 --- a/src/renderer/src/aiCore/legacy/clients/volcengine/VolcengineAPIClient.ts +++ b/src/renderer/src/aiCore/legacy/clients/volcengine/VolcengineAPIClient.ts @@ -64,9 +64,23 @@ export class VolcengineAPIClient extends OpenAIAPIClient { return models } catch (error) { logger.error('Failed to list Volcengine models:', error as Error) - // Notify user before falling back - window.toast?.warning('Failed to fetch Volcengine models. Check credentials if this persists.') - // Fall back to standard OpenAI-compatible API on error + + const errorMessage = error instanceof Error ? error.message : String(error) + + // Credential errors should not fall back - user must fix + if (errorMessage.includes('could not be loaded') || errorMessage.includes('credentials')) { + window.toast?.error('Volcengine credentials error. Please re-enter your credentials in settings.') + throw error + } + + // Auth errors should not fall back + if (errorMessage.includes('401') || errorMessage.includes('403')) { + window.toast?.error('Volcengine authentication failed. Please verify your Access Key.') + throw error + } + + // Only fall back for transient network errors + window.toast?.warning('Temporarily unable to fetch Volcengine models. Using fallback.') logger.info('Falling back to OpenAI-compatible model list') return super.listModels() } diff --git a/src/renderer/src/pages/settings/ProviderSettings/VolcengineSettings.tsx b/src/renderer/src/pages/settings/ProviderSettings/VolcengineSettings.tsx index 61480f68a6..91f141254c 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/VolcengineSettings.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/VolcengineSettings.tsx @@ -1,3 +1,4 @@ +import { loggerService } from '@logger' import { HStack } from '@renderer/components/Layout' import { PROVIDER_URLS } from '@renderer/config/providers' import { useVolcengineSettings } from '@renderer/hooks/useVolcengine' @@ -24,7 +25,13 @@ const VolcengineSettings: FC = () => { // Check if credentials exist on mount useEffect(() => { - window.api.volcengine.hasCredentials().then(setHasCredentials) + window.api.volcengine + .hasCredentials() + .then(setHasCredentials) + .catch((error) => { + loggerService.withContext('VolcengineSettings').error('Failed to check credentials:', error as Error) + window.toast?.error('Failed to check Volcengine credentials') + }) }, []) // Sync local state with store (only for region and projectName)