/** * 导出脱敏回归测试(S-1) * * 背景:data:export 全量导出曾直接透传 configService.getAll(), * 该方法对敏感 key 解密返回明文,导致导出文件泄露明文 API Key。 * sanitizeExportConfig 必须保证任何敏感 key 经其处理后不含明文。 */ import { describe, it, expect } from 'vitest'; import { sanitizeExportConfig, maskSensitive } from '../shared'; describe('sanitizeExportConfig — 导出配置脱敏', () => { it('llm.apiKey / fallbackApiKey / searxng.auth_key 导出为掩码', () => { const config = { 'llm.provider': 'deepseek', 'llm.model': 'deepseek-v4-pro', 'llm.apiKey': 'sk-very-secret-key-1234', 'llm.fallbackApiKey': 'sk-fallback-secret-9876', 'searxng.auth_key': 'bearer-token-abcdef', 'searxng.enabled': true, }; const out = sanitizeExportConfig(config); expect(out['llm.apiKey']).not.toContain('sk-very-secret'); expect(out['llm.apiKey']).toBe('***1234'); expect(out['llm.fallbackApiKey']).toBe('***9876'); expect(out['searxng.auth_key']).toBe('***cdef'); // 非敏感 key 原样保留 expect(out['llm.provider']).toBe('deepseek'); expect(out['searxng.enabled']).toBe(true); }); it('短敏感值(<=4 字符)完全掩码', () => { expect(sanitizeExportConfig({ 'llm.apiKey': 'abc' })['llm.apiKey']).toBe('***'); expect(sanitizeExportConfig({ 'llm.apiKey': '' })['llm.apiKey']).toBe(''); }); it('不修改入参对象(纯函数)', () => { const config = { 'llm.apiKey': 'sk-original-plaintext' }; const snapshot = { ...config }; sanitizeExportConfig(config); expect(config).toEqual(snapshot); }); it('空对象与混合类型安全', () => { expect(sanitizeExportConfig({})).toEqual({}); const out = sanitizeExportConfig({ 'agent.maxIterations': 20, 'agent.enableThinking': true, 'ollama.numCtx': null, 'tools.run_command.enabled': false, }); expect(out).toEqual({ 'agent.maxIterations': 20, 'agent.enableThinking': true, 'ollama.numCtx': null, 'tools.run_command.enabled': false, }); }); it('maskSensitive:非字符串敏感值原样返回', () => { expect(maskSensitive('llm.apiKey', 123)).toBe(123); expect(maskSensitive('llm.apiKey', null)).toBe(null); }); });