fix(volcengine): address security and error handling issues

- 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 <noreply@anthropic.com>
This commit is contained in:
suyao 2025-12-11 11:51:44 +08:00
parent cafc1648d9
commit 771e5e6c3e
No known key found for this signature in database
3 changed files with 30 additions and 4 deletions

View File

@ -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)

View File

@ -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()
}

View File

@ -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)