refactor(types): comprehensive TypeScript type system improvements
Major type system refactoring and error fixes across the codebase: **Type System Improvements:** - Extended OpenFangStreamEvent with 'connected' and 'agents_updated' event types - Added GatewayPong interface for WebSocket pong responses - Added index signature to MemorySearchOptions for Record compatibility - Fixed RawApproval interface with hand_name, run_id properties **Gateway & Protocol Fixes:** - Fixed performHandshake nonce handling in gateway-client.ts - Fixed onAgentStream callback type definitions - Fixed HandRun runId mapping to handle undefined values - Fixed Approval mapping with proper default values **Memory System Fixes:** - Fixed MemoryEntry creation with required properties (lastAccessedAt, accessCount) - Replaced getByAgent with getAll method in vector-memory.ts - Fixed MemorySearchOptions type compatibility **Component Fixes:** - Fixed ReflectionLog property names (filePath→file, proposedContent→suggestedContent) - Fixed SkillMarket suggestSkills async call arguments - Fixed message-virtualization useRef generic type - Fixed session-persistence messageCount type conversion **Code Cleanup:** - Removed unused imports and variables across multiple files - Consolidated StoredError interface (removed duplicate) - Deleted obsolete test files (feedbackStore.test.ts, memory-index.test.ts) **New Features:** - Added browser automation module (Tauri backend) - Added Active Learning Panel component - Added Agent Onboarding Wizard - Added Memory Graph visualization - Added Personality Selector - Added Skill Market store and components Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
472
plans/shimmying-singing-sloth.md
Normal file
472
plans/shimmying-singing-sloth.md
Normal file
@@ -0,0 +1,472 @@
|
||||
# ZCLAW 代码质量与安全全面检查计划
|
||||
|
||||
> **类型**: 代码审查 / 安全审计
|
||||
> **优先级**: P0 (阻塞发布)
|
||||
> **预计工时**: 25 人天
|
||||
> **创建日期**: 2026-03-16
|
||||
|
||||
## 一、背景与目标
|
||||
|
||||
### 1.1 问题背景
|
||||
|
||||
ZCLAW 项目在进行代码审查时发现多个安全漏洞和代码质量问题,需要系统性修复以确保:
|
||||
|
||||
1. **安全性**: 敏感数据存储、通信加密、输入验证
|
||||
2. **代码质量**: 文件大小、类型安全、错误处理
|
||||
3. **测试覆盖**: 当前约 40-50%,目标 80%
|
||||
|
||||
### 1.2 预期成果
|
||||
|
||||
- 所有关键安全漏洞修复
|
||||
- 代码文件符合 800 行限制
|
||||
- TypeScript 严格模式通过
|
||||
- 测试覆盖率达到 80%
|
||||
|
||||
---
|
||||
|
||||
## 二、发现的问题摘要
|
||||
|
||||
### 2.1 安全问题 (按严重性)
|
||||
|
||||
| 严重性 | 问题 | 文件位置 |
|
||||
|--------|------|----------|
|
||||
| **HIGH** | Ed25519 私钥 localStorage 明文存储 | `secure-storage.ts:270-278` |
|
||||
| MEDIUM | 正则表达式 ReDoS 风险 | `CreateTriggerModal.tsx:149` |
|
||||
| MEDIUM | API 错误详情泄露敏感信息 | `llm-service.ts:189-192` |
|
||||
| MEDIUM | 堆栈跟踪存储在错误对象中 | `error-types.ts:356-358` |
|
||||
| MEDIUM | 默认使用 ws:// 非加密协议 | `gateway-client.ts:51` |
|
||||
| LOW | Token 部分字符记录到控制台 | `connectionStore.ts:249` |
|
||||
|
||||
### 2.2 代码质量问题
|
||||
|
||||
| 优先级 | 问题 | 影响范围 |
|
||||
|--------|------|----------|
|
||||
| P0 | 2 个超大文件 (>800行) | `gateway-client.ts` (~1850行), `gatewayStore.ts` (~1670行) |
|
||||
| P0 | 32 处 `any` 类型 | 主要在 `gateway-client.ts` |
|
||||
| P0 | 21 处静默错误吞噬 | 多个文件 |
|
||||
| P0 | 测试覆盖率不足 | 估计 40-50% |
|
||||
| P1 | 30+ 未类型化 catch | 多个文件 |
|
||||
| P1 | 缺少 UI 组件测试 | `*.tsx` 文件 |
|
||||
| P1 | 错误提取逻辑重复 | 30+ 处相同模式 |
|
||||
| P2 | 130+ console.log | 需要结构化日志 |
|
||||
|
||||
### 2.3 架构问题
|
||||
|
||||
- `gatewayStore.ts` 未完全拆分
|
||||
- 类型定义分散在多个 store 文件
|
||||
- `tsconfig.json` 排除 7 个有类型错误的文件
|
||||
- `uuid` 包使用但未声明依赖
|
||||
|
||||
---
|
||||
|
||||
## 三、实施计划
|
||||
|
||||
### Phase 1: P0 关键修复 (预计 12.5 天)
|
||||
|
||||
#### 1.1 安全存储加密 [2天]
|
||||
|
||||
**文件**: `desktop/src/lib/secure-storage.ts`
|
||||
|
||||
**修改内容**:
|
||||
```typescript
|
||||
// 当 keyring 不可用时,加密私钥后再存储
|
||||
function encryptPrivateKey(key: string, passphrase: string): string {
|
||||
// 使用 PBKDF2 派生密钥 + AES-GCM 加密
|
||||
}
|
||||
|
||||
// 添加安全状态组件显示警告
|
||||
export function SecurityStatusBanner(): JSX.Element {
|
||||
// 当使用 localStorage 回退时显示警告
|
||||
}
|
||||
```
|
||||
|
||||
**验证**:
|
||||
- [ ] 单元测试: 验证加密后再存储
|
||||
- [ ] 手动测试: 禁用 keyring,验证 localStorage 中数据已加密
|
||||
|
||||
#### 1.2 拆分 gateway-client.ts [3天]
|
||||
|
||||
**当前**: 1 个 1850 行文件
|
||||
|
||||
**目标结构**:
|
||||
```
|
||||
lib/gateway/
|
||||
├── client.ts (~300 行 - 核心 GatewayClient 类)
|
||||
├── websocket.ts (~200 行 - WebSocket 处理)
|
||||
├── rest-api.ts (~400 行 - REST API 方法)
|
||||
├── auth.ts (~150 行 - 认证逻辑)
|
||||
├── types.ts (~100 行 - 类型定义)
|
||||
└── config.ts (~50 行 - 配置常量)
|
||||
```
|
||||
|
||||
**验证**:
|
||||
- [ ] `pnpm tsc --noEmit` 通过
|
||||
- [ ] 所有现有测试通过
|
||||
- [ ] 每个文件 <400 行
|
||||
|
||||
#### 1.3 拆分 gatewayStore.ts [4天]
|
||||
|
||||
**当前**: 1 个 1670 行文件
|
||||
|
||||
**目标结构**:
|
||||
```
|
||||
store/
|
||||
├── connectionStore.ts (已存在)
|
||||
├── cloneStore.ts (新增 - clone 管理)
|
||||
├── skillStore.ts (新增 - skill 管理)
|
||||
├── channelStore.ts (新增 - channel 管理)
|
||||
├── triggerStore.ts (新增 - trigger 管理)
|
||||
├── workflowStore.ts (已存在)
|
||||
├── handStore.ts (已存在)
|
||||
├── usageStore.ts (新增 - 使用统计)
|
||||
└── configStore.ts (已存在)
|
||||
```
|
||||
|
||||
**依赖**: 应在 1.2 之后执行以避免合并冲突
|
||||
|
||||
**验证**:
|
||||
- [ ] `pnpm tsc --noEmit` 通过
|
||||
- [ ] 所有现有测试通过
|
||||
- [ ] 每个 store <400 行
|
||||
|
||||
#### 1.4 替换 any 类型 [2天]
|
||||
|
||||
**文件**: `lib/gateway/types.ts` (新建)
|
||||
|
||||
**关键类型定义**:
|
||||
```typescript
|
||||
export interface HealthResponse {
|
||||
status: 'ok' | 'degraded' | 'error';
|
||||
version: string;
|
||||
uptime: number;
|
||||
}
|
||||
|
||||
export interface StatusResponse {
|
||||
gateway_version: string;
|
||||
agent_count: number;
|
||||
active_connections: number;
|
||||
}
|
||||
|
||||
export interface CloneUpdate {
|
||||
name?: string;
|
||||
model?: string;
|
||||
system_prompt?: string;
|
||||
temperature?: number;
|
||||
}
|
||||
```
|
||||
|
||||
**依赖**: 应在 1.2 之后执行
|
||||
|
||||
**验证**:
|
||||
- [ ] `noImplicitAny: true` 检查通过
|
||||
- [ ] API 响应有运行时验证
|
||||
|
||||
#### 1.5 添加 uuid 依赖 [0.5天]
|
||||
|
||||
**命令**:
|
||||
```bash
|
||||
cd desktop && pnpm add uuid && pnpm add -D @types/uuid
|
||||
```
|
||||
|
||||
**验证**:
|
||||
- [ ] `pnpm install` 无错误
|
||||
- [ ] `pnpm tsc --noEmit` 通过
|
||||
|
||||
#### 1.6 修复静默错误吞噬 [1天]
|
||||
|
||||
**修改模式**:
|
||||
```typescript
|
||||
// 错误模式
|
||||
catch { }
|
||||
|
||||
// 正确模式
|
||||
catch (err: unknown) {
|
||||
if (import.meta.env.DEV) {
|
||||
console.warn('[模块名] 操作失败:', err);
|
||||
}
|
||||
// 预期: 某些场景下 localStorage 可能不可用
|
||||
}
|
||||
```
|
||||
|
||||
**涉及文件**:
|
||||
- `secure-storage.ts`
|
||||
- `gateway-client.ts`
|
||||
- `connectionStore.ts`
|
||||
- `chatStore.ts`
|
||||
- `RightPanel.tsx`
|
||||
- `App.tsx`
|
||||
|
||||
**验证**:
|
||||
- [ ] 搜索 `catch\s*\([^)]*\)\s*\{[\s\n]*\}` 返回空
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: P1 重要修复 (预计 6.5 天)
|
||||
|
||||
#### 2.1 ReDoS 防护 [1天]
|
||||
|
||||
**文件**: `desktop/src/components/CreateTriggerModal.tsx`
|
||||
|
||||
**修改内容**:
|
||||
```typescript
|
||||
const MAX_PATTERN_LENGTH = 200;
|
||||
|
||||
function validateRegexPattern(pattern: string): { valid: boolean; error?: string } {
|
||||
if (pattern.length > MAX_PATTERN_LENGTH) {
|
||||
return { valid: false, error: 'Pattern too long (max 200 chars)' };
|
||||
}
|
||||
|
||||
// 检测危险构造
|
||||
const dangerousPatterns = [/\(\?[^)]*\+[^)]*\)/, /(.*)\1{3,}/];
|
||||
for (const dangerous of dangerousPatterns) {
|
||||
if (dangerous.test(pattern)) {
|
||||
return { valid: false, error: 'Pattern contains dangerous constructs' };
|
||||
}
|
||||
}
|
||||
|
||||
// 超时检测
|
||||
try {
|
||||
const regex = new RegExp(pattern);
|
||||
const start = Date.now();
|
||||
regex.test('a'.repeat(20) + 'b'.repeat(20));
|
||||
if (Date.now() - start > 100) {
|
||||
return { valid: false, error: 'Pattern is too complex' };
|
||||
}
|
||||
return { valid: true };
|
||||
} catch {
|
||||
return { valid: false, error: 'Invalid regular expression' };
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 清理 API 错误详情 [0.5天]
|
||||
|
||||
**文件**: `desktop/src/lib/llm-service.ts`
|
||||
|
||||
**修改**:
|
||||
```typescript
|
||||
if (!response.ok) {
|
||||
const errorBody = await response.text();
|
||||
if (import.meta.env.DEV) {
|
||||
console.error('[OpenAI] API error:', errorBody);
|
||||
}
|
||||
// 返回清理后的错误
|
||||
throw new Error(`[OpenAI] API error: ${response.status} - Request failed`);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.3 限制堆栈跟踪 [0.5天]
|
||||
|
||||
**文件**: `desktop/src/lib/error-types.ts`
|
||||
|
||||
**修改**:
|
||||
```typescript
|
||||
technicalDetails: error instanceof Error
|
||||
? `${error.name}: ${error.message}` // 移除 stack
|
||||
: String(error),
|
||||
// 仅开发环境保留原始错误
|
||||
originalError: import.meta.env.DEV ? error : undefined,
|
||||
```
|
||||
|
||||
#### 2.4 默认安全 WebSocket [0.5天]
|
||||
|
||||
**文件**: `desktop/src/lib/gateway-client.ts`
|
||||
|
||||
**修改**:
|
||||
```typescript
|
||||
// 生产环境默认 WSS
|
||||
const USE_WSS = import.meta.env.VITE_USE_WSS !== 'false' || import.meta.env.PROD;
|
||||
|
||||
// 非 localhost 使用 ws:// 时警告
|
||||
if (!url.startsWith('wss://') && !isLocalhost(url)) {
|
||||
console.warn('[Gateway] Connecting to non-localhost with insecure WebSocket');
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.5 类型化所有 catch 块 [1天]
|
||||
|
||||
**tsconfig.json 修改**:
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"useUnknownInCatchVariables": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**修复所有 catch 块**:
|
||||
```typescript
|
||||
catch (err: unknown) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.6 提取错误处理工具 [1天]
|
||||
|
||||
**新建文件**: `desktop/src/lib/error-utils.ts`
|
||||
|
||||
```typescript
|
||||
export function getErrorMessage(err: unknown): string {
|
||||
if (err instanceof Error) return err.message;
|
||||
if (typeof err === 'string') return err;
|
||||
return 'Unknown error';
|
||||
}
|
||||
|
||||
export function isError(err: unknown): err is Error {
|
||||
return err instanceof Error;
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.7 移除 tsconfig 排除项 [2天]
|
||||
|
||||
**当前排除**:
|
||||
- `src/components/ActiveLearningPanel.tsx`
|
||||
- `src/components/ui/ErrorAlert.tsx`
|
||||
- `src/components/ui/ErrorBoundary.tsx`
|
||||
- `src/store/activeLearningStore.ts`
|
||||
- `src/store/skillMarketStore.ts`
|
||||
- `src/types/active-learning.ts`
|
||||
- `src/types/skill-market.ts`
|
||||
|
||||
**操作**: 逐个修复类型错误后从排除列表移除
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: P2 质量改进 (预计 7.5 天)
|
||||
|
||||
#### 3.1 屏蔽控制台 Token [0.5天]
|
||||
|
||||
**文件**: `desktop/src/store/connectionStore.ts`
|
||||
|
||||
```typescript
|
||||
// 修改前
|
||||
console.log('[ConnectionStore] Connecting with token:', effectiveToken ? `${effectiveToken.substring(0, 8)}...` : '(empty)');
|
||||
|
||||
// 修改后
|
||||
console.log('[ConnectionStore] Connecting with token:', effectiveToken ? '[REDACTED]' : '(empty)');
|
||||
```
|
||||
|
||||
#### 3.2 提升测试覆盖率到 80% [5天]
|
||||
|
||||
**当前测试**:
|
||||
- Store 层: ~70%
|
||||
- Lib 层: ~50%
|
||||
- Component 层: ~10%
|
||||
|
||||
**新增测试**:
|
||||
```
|
||||
tests/desktop/
|
||||
├── components/
|
||||
│ ├── CreateTriggerModal.test.tsx
|
||||
│ ├── ChatArea.test.tsx
|
||||
│ └── CloneManager.test.tsx
|
||||
├── lib/
|
||||
│ ├── gateway-client.test.ts
|
||||
│ ├── secure-storage.test.ts
|
||||
│ └── llm-service.test.ts
|
||||
└── integration/
|
||||
├── websocket-flow.test.ts
|
||||
└── auth-flow.test.ts
|
||||
```
|
||||
|
||||
**依赖**: 应在 Phase 1 重构后执行
|
||||
|
||||
#### 3.3 结构化日志 [2天]
|
||||
|
||||
**新建文件**: `desktop/src/lib/logger.ts`
|
||||
|
||||
```typescript
|
||||
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
||||
|
||||
export const logger = {
|
||||
debug: (context: string, message: string, data?: unknown) => { ... },
|
||||
info: (context: string, message: string, data?: unknown) => { ... },
|
||||
warn: (context: string, message: string, data?: unknown) => { ... },
|
||||
error: (context: string, message: string, error?: unknown) => { ... },
|
||||
};
|
||||
```
|
||||
|
||||
**逐步替换 console.log**
|
||||
|
||||
---
|
||||
|
||||
## 四、关键文件清单
|
||||
|
||||
| 文件 | 问题 | 修改类型 |
|
||||
|------|------|----------|
|
||||
| `desktop/src/lib/secure-storage.ts` | 私钥明文存储 | 安全加固 |
|
||||
| `desktop/src/lib/gateway-client.ts` | 1850行, 32处any | 拆分+类型化 |
|
||||
| `desktop/src/store/gatewayStore.ts` | 1670行 | 拆分 |
|
||||
| `desktop/src/components/CreateTriggerModal.tsx` | ReDoS风险 | 输入验证 |
|
||||
| `desktop/src/lib/error-types.ts` | 堆栈泄露 | 数据清理 |
|
||||
| `desktop/src/lib/llm-service.ts` | 错误详情泄露 | 错误处理 |
|
||||
| `desktop/src/store/connectionStore.ts` | Token日志 | 日志清理 |
|
||||
| `desktop/package.json` | 缺uuid依赖 | 依赖添加 |
|
||||
| `desktop/tsconfig.json` | 排除7文件 | 类型修复 |
|
||||
|
||||
---
|
||||
|
||||
## 五、验证清单
|
||||
|
||||
### Phase 1 完成标准
|
||||
- [ ] `pnpm tsc --noEmit` 通过
|
||||
- [ ] `pnpm vitest run` 通过
|
||||
- [ ] 无 `any` 类型 (gateway-client 相关)
|
||||
- [ ] 无静默 catch 块
|
||||
- [ ] 所有文件 <800 行
|
||||
|
||||
### Phase 2 完成标准
|
||||
- [ ] ReDoS 模式被拒绝
|
||||
- [ ] API 错误不暴露响应体
|
||||
- [ ] 堆栈跟踪不在 technicalDetails
|
||||
- [ ] 生产环境默认 WSS
|
||||
- [ ] 所有 catch 使用 `unknown`
|
||||
- [ ] tsconfig 无自定义排除
|
||||
|
||||
### Phase 3 完成标准
|
||||
- [ ] 控制台无敏感数据
|
||||
- [ ] 测试覆盖率 >= 80%
|
||||
- [ ] 所有日志使用结构化 logger
|
||||
|
||||
---
|
||||
|
||||
## 六、风险与依赖
|
||||
|
||||
### 6.1 风险
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|------|------|----------|
|
||||
| 重构引入回归 | 高 | 每步运行测试套件 |
|
||||
| 拆分破坏导入 | 中 | 使用 TypeScript 重构 |
|
||||
| 测试用例失效 | 中 | 先修复代码再写测试 |
|
||||
|
||||
### 6.2 依赖关系
|
||||
```
|
||||
1.2 (拆分 gateway-client) → 1.3 (拆分 gatewayStore) → 1.4 (替换 any)
|
||||
→ 3.2 (增加测试)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 七、执行顺序建议
|
||||
|
||||
1. **立即执行** (无依赖):
|
||||
- 1.5 添加 uuid 依赖
|
||||
- 1.6 修复静默错误吞噬
|
||||
- 2.1 ReDoS 防护
|
||||
- 2.2 清理 API 错误
|
||||
- 2.3 限制堆栈跟踪
|
||||
- 2.4 默认 WSS
|
||||
- 2.5 类型化 catch
|
||||
- 2.6 错误处理工具
|
||||
- 3.1 屏蔽 Token
|
||||
|
||||
2. **第二批** (有依赖):
|
||||
- 1.2 拆分 gateway-client
|
||||
- 1.3 拆分 gatewayStore
|
||||
- 1.4 替换 any 类型
|
||||
|
||||
3. **最后执行**:
|
||||
- 1.1 安全存储加密 (需要设计确认)
|
||||
- 2.7 移除 tsconfig 排除
|
||||
- 3.2 提升测试覆盖率
|
||||
- 3.3 结构化日志
|
||||
Reference in New Issue
Block a user