feat: 升级至 v0.3.16 — 工单修复 51 项 + 审查修复 30 项(安全加固/适配器/工具系统/数据层/前端)
This commit is contained in:
@@ -16,6 +16,27 @@ import type Database from 'better-sqlite3';
|
||||
import { createHash } from 'crypto';
|
||||
import log from 'electron-log';
|
||||
|
||||
/**
|
||||
* #36 修复: 稳定序列化,递归按 key 字典序排序后序列化
|
||||
* JSON.stringify 对对象 key 顺序敏感({a:1,b:2} ≠ {b:2,a:1}),
|
||||
* 导致相同语义的对象产生不同 hash,审计去重失效。
|
||||
* stableStringify 保证相同内容始终产生相同字符串。
|
||||
*/
|
||||
function stableStringify(obj: unknown): string {
|
||||
if (obj === null || typeof obj !== 'object') return JSON.stringify(obj);
|
||||
if (Array.isArray(obj)) {
|
||||
return '[' + obj.map(stableStringify).join(',') + ']';
|
||||
}
|
||||
const keys = Object.keys(obj as Record<string, unknown>).sort();
|
||||
return (
|
||||
'{' +
|
||||
keys
|
||||
.map((k) => JSON.stringify(k) + ':' + stableStringify((obj as Record<string, unknown>)[k]))
|
||||
.join(',') +
|
||||
'}'
|
||||
);
|
||||
}
|
||||
|
||||
export type AuditEventType = 'tool_call' | 'permission_check' | 'error' | 'llm_request' | 'llm_response' | 'session_start' | 'session_end' | 'config_change';
|
||||
export type AuditActor = 'agent' | 'user' | 'system';
|
||||
export type AuditOutcome = 'success' | 'denied' | 'error';
|
||||
@@ -38,6 +59,7 @@ export class AuditService {
|
||||
* 计算链式哈希
|
||||
* current_hash = SHA256(prev_hash + 所有内容字段)
|
||||
* v0.2.0: 纳入 iteration/outcome/durationMs 字段,使用 JSON.stringify 避免分隔符碰撞
|
||||
* #36 修复: 改用 stableStringify 替代 JSON.stringify,避免对象 key 顺序不稳定导致 hash 不一致
|
||||
*/
|
||||
private computeHash(
|
||||
prevHash: string,
|
||||
@@ -47,8 +69,8 @@ export class AuditService {
|
||||
outcome: string | null; durationMs: number | null; createdAt: number;
|
||||
},
|
||||
): string {
|
||||
// 使用 JSON.stringify 避免分隔符碰撞
|
||||
const content = JSON.stringify({
|
||||
// #36 修复: 使用 stableStringify 避免分隔符碰撞且保证 key 顺序稳定
|
||||
const content = stableStringify({
|
||||
prevHash,
|
||||
sessionId: entry.sessionId,
|
||||
iteration: entry.iteration,
|
||||
@@ -328,6 +350,25 @@ export class AuditService {
|
||||
});
|
||||
|
||||
if (row.current_hash !== expectedHash) {
|
||||
// 审查修复: #36 将 JSON.stringify 改为 stableStringify 后,旧记录的 hash 用旧算法生成
|
||||
// 尝试用旧算法(JSON.stringify)重新计算,如果匹配则跳过(兼容旧数据)
|
||||
const legacyContent = JSON.stringify({
|
||||
prevHash,
|
||||
sessionId: row.session_id,
|
||||
iteration: row.iteration,
|
||||
eventType: row.event_type,
|
||||
actor: row.actor,
|
||||
target: row.target,
|
||||
details: row.details,
|
||||
outcome: row.outcome,
|
||||
durationMs: row.duration_ms,
|
||||
createdAt: row.created_at,
|
||||
});
|
||||
const legacyHash = createHash('sha256').update(legacyContent, 'utf-8').digest('hex');
|
||||
if (row.current_hash === legacyHash) {
|
||||
prevHash = row.current_hash;
|
||||
continue;
|
||||
}
|
||||
return { valid: false, totalRecords: rows.length, verifiedRecords: verified, tamperedId: row.id };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user