feat: 升级至 v0.3.1 — 全量代码审计修复 + 安全增强

本次升级基于完整代码审查,修复 Critical/High/Medium/Low 四级共 96 项问题,
并通过返工审计修复 10 项遗留问题,tsc 双端类型检查零错误。

Critical (10/10 完成):
- C-4: command.ts 接入 shell-quote 进行 token-level 注入检测,替代原有正则匹配
  可防御 r"m" -rf /、$'rm'、$(echo rm) 等字符串拼接绕过

High (11/11 完成):
- 竞态保护、Promise.allSettled、AbortController 资源泄漏、IPC 参数校验等

Medium (55/55 完成):
- 事务保护、敏感数据脱敏、枚举校验、MUI v9 Stack prop 迁移、
  React 组件 cancelled 标志、类型收窄等

Low (20/20 完成):
- 辅助方法提取(flushToolCallBuffer/scoreAndPushMemory/tryAddColumn 等)
- nanoid 统一替代 Date.now()+Math.random()
- confirm() 替换为 MUI Dialog、useMemo 缓存、魔法数字命名化等

返工审计修复 (10/10 完成):
- L-11: LogsSettings 残留的原生 confirm()/alert() 全部替换为 MUI Dialog/Alert
- M-53: MemoryViewer handleSearch 独立 ref,修复 searching 状态卡死
- M-42: 脱敏短值(length <= 4)泄露修复
- M-47: tasks:update 补全 title/description 类型校验
- L-9: ollama.adapter 非流式路径 nanoid 统一
- M-45: audit:query limit 策略与 memory:listAll 一致化
- SettingsModal handleConfirmRemove 补全 try/catch + loadServers cleanup
- L-15: CommandPalette useMemo 补全 sessions 响应式依赖
- useAgentStream 事件类型补全 seq/timestamp 字段

新增依赖: shell-quote + @types/shell-quote
版本号: 0.3.0 -> 0.3.1
This commit is contained in:
thzxx
2026-07-13 22:36:58 +08:00
parent 4f5f570ac8
commit e4d81d8247
47 changed files with 2247 additions and 475 deletions
+177 -2
View File
@@ -1,7 +1,8 @@
/**
* Health Checker — 健康检查器
* Health Checker + SLO Monitor — 健康检查器与 SLO 监控
*
* 对数据库连通性、磁盘空间、内存使用执行真实检查。
* HealthChecker: 对数据库连通性、磁盘空间、内存使用执行真实检查。
* SLOMonitor: C-9 修复 — 补全 SLO 监控核心功能(错误率、延迟分布、吞吐量、燃烧速率)。
*
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第十一章
*/
@@ -107,3 +108,177 @@ export class HealthChecker {
};
}
}
// ===== C-9 修复: SLO Monitor — SLO 监控核心功能 =====
/**
* SLO 监控配置
*/
export interface SLOConfig {
/** SLO 目标(如 0.999 表示 99.9% 可用性) */
target: number;
/** 滑动窗口大小(毫秒,默认 5 分钟) */
windowMs: number;
/** 延迟分位数(如 0.95 表示 P95) */
latencyPercentiles: number[];
/** 延迟告警阈值(毫秒) */
latencyThresholdMs: number;
}
/**
* SLO 请求记录
*/
interface SLORequestRecord {
timestamp: number;
latencyMs: number;
success: boolean;
}
/**
* SLO 状态报告
*/
export interface SLOStatus {
/** 当前错误率(0-1 */
errorRate: number;
/** 当前吞吐量(请求/秒) */
throughput: number;
/** 平均延迟(毫秒) */
avgLatencyMs: number;
/** 延迟分位数 */
percentiles: Record<string, number>;
/** 燃烧速率(实际错误率 / 错误预算) */
burnRate: number;
/** SLO 目标 */
target: number;
/** 错误预算(1 - target */
errorBudget: number;
/** 是否违反 SLO */
violated: boolean;
/** 窗口内总请求数 */
totalRequests: number;
/** 窗口内错误请求数 */
errorRequests: number;
/** 时间戳 */
timestamp: Date;
}
/**
* SLO Monitor — SLO 监控器
*
* C-9 修复: 补全 SLO 监控核心功能
*
* 功能:
* 1. 错误率追踪 — 记录请求成功/失败,计算滑动窗口内错误率
* 2. 延迟分布追踪 — 记录请求延迟,计算 P50/P95/P99 分位数
* 3. 吞吐量追踪 — 计算每秒请求数
* 4. 燃烧速率计算 — 实际错误率 / 错误预算,>1 表示 SLO 即将违反
*
* @see docs/生产级通用 AI Agent 智能体桌面应用:完整设计与构建指南.html — 第十一章
*/
export class SLOMonitor {
private records: SLORequestRecord[] = [];
private readonly config: SLOConfig;
constructor(config: Partial<SLOConfig> = {}) {
this.config = {
target: 0.999,
windowMs: 5 * 60 * 1000, // 5 分钟
latencyPercentiles: [0.5, 0.95, 0.99],
latencyThresholdMs: 5000,
...config,
};
}
/**
* 记录一次请求
* @param latencyMs 延迟(毫秒)
* @param success 是否成功
*/
recordRequest(latencyMs: number, success: boolean): void {
const record: SLORequestRecord = {
timestamp: Date.now(),
latencyMs,
success,
};
this.records.push(record);
// 清理过期记录
this.cleanup();
}
/**
* 清理窗口外的过期记录
*/
private cleanup(): void {
const cutoff = Date.now() - this.config.windowMs;
// 保留最近 10 分钟的数据(2 倍窗口),避免边界效应
this.records = this.records.filter((r) => r.timestamp >= cutoff);
}
/**
* 获取当前 SLO 状态
*/
getStatus(): SLOStatus {
this.cleanup();
const now = Date.now();
const windowStart = now - this.config.windowMs;
const windowRecords = this.records.filter((r) => r.timestamp >= windowStart);
const totalRequests = windowRecords.length;
const errorRequests = windowRecords.filter((r) => !r.success).length;
const errorRate = totalRequests > 0 ? errorRequests / totalRequests : 0;
const throughput = totalRequests / (this.config.windowMs / 1000);
const latencies = windowRecords.map((r) => r.latencyMs).sort((a, b) => a - b);
const avgLatencyMs = latencies.length > 0
? latencies.reduce((sum, l) => sum + l, 0) / latencies.length
: 0;
const percentiles: Record<string, number> = {};
for (const p of this.config.latencyPercentiles) {
const key = `P${(p * 100).toFixed(0)}`;
percentiles[key] = this.calculatePercentile(latencies, p);
}
const errorBudget = 1 - this.config.target;
const burnRate = errorBudget > 0 ? errorRate / errorBudget : 0;
return {
errorRate,
throughput,
avgLatencyMs,
percentiles,
burnRate,
target: this.config.target,
errorBudget,
violated: burnRate > 1,
totalRequests,
errorRequests,
timestamp: new Date(),
};
}
/**
* 计算分位数
*/
private calculatePercentile(sortedValues: number[], percentile: number): number {
if (sortedValues.length === 0) return 0;
const index = Math.ceil(percentile * sortedValues.length) - 1;
return sortedValues[Math.max(0, index)];
}
/**
* 重置所有记录
*/
reset(): void {
this.records = [];
}
/**
* 获取配置
*/
getConfig(): SLOConfig {
return { ...this.config };
}
}