修复: DeepSeek 400 全量扫描 + 队列 per-conv 隔离

- openai_compat: 扫描所有 assistant 消息剥离 orphan tool_calls(原仅查末条)
- queue 加 conversationId 字段,按会话精准 drain
- regenerate/editMessage 只清本会话排队消息
- newConversation 保留旧会话排队消息
- AiError 只清出错会话的队列项
This commit is contained in:
lxy
2026-07-20 00:19:50 +08:00
parent 42efb31bbf
commit e9e3578d26
59 changed files with 2875 additions and 1330 deletions
+48 -3
View File
@@ -165,8 +165,22 @@ pub fn check_path_authorization(
raw_path: &str,
allowed: &AllowedDirs,
) -> PathAuthDecision {
// Phase C: 黑名单优先独立判定(is_authorized 内也判,此处先判便于 NeedsAuthorization
// 不误把黑名单路径推到弹窗 — 黑名单路径直接硬拒不让用户"授权")。
//
// BUG-2026-07-07: 黑名单检查必须在路径规范化(is_absolute / root.join)之前对**原始路径**
// 也判一次。原因:Windows 上 "/home/user/.ssh/config" 这类 Unix 风格路径 is_absolute() 返 false
// (Windows 绝对路径需盘符或 UNC),会被当相对路径走 first_persistent_dir 分支;若 persistent 为空,
// 旧逻辑直接 return NeedsAuthorization,**跳过黑名单检查** → .ssh/.aws 等敏感路径在 Windows 上绕过黑名单。
// 修复:先用原始路径判黑名单(segment 匹配,任意路径段命中 .ssh 即拒,与路径形态无关),命中直接 Denied。
let raw_resolved = Path::new(raw_path);
if is_in_system_blacklist(raw_resolved) {
return PathAuthDecision::Denied {
reason: format!("路径命中系统敏感目录黑名单: {}", raw_path),
};
}
// 规范化:绝对路径原样,相对路径锚定首个持久授权目录(与 resolve_workspace_path_impl 一致)
let resolved = if Path::new(raw_path).is_absolute() {
let resolved = if raw_resolved.is_absolute() {
PathBuf::from(raw_path)
} else if let Some(root) = allowed.first_persistent_dir() {
root.join(raw_path)
@@ -176,8 +190,7 @@ pub fn check_path_authorization(
dir: PathBuf::from("."),
};
};
// Phase C: 黑名单优先独立判定(is_authorized 内也判,此处先判便于 NeedsAuthorization
// 不误把黑名单路径推到弹窗 — 黑名单路径直接硬拒不让用户"授权")。
// 规范化后再判一次黑名单(锚定后路径形态可能变化,双保险)
if is_in_system_blacklist(&resolved) {
return PathAuthDecision::Denied {
reason: format!("路径命中系统敏感目录黑名单: {}", raw_path),
@@ -430,6 +443,38 @@ mod tests {
}
}
/// BUG-2026-07-07: Windows 上 Unix 风格 .ssh 路径必须被黑名单拒(不能绕过)。
///
/// 背景:Windows 上 `/home/user/.ssh/config` 的 is_absolute() 返 false(无盘符),
/// 旧逻辑在 persistent 为空时直接返 NeedsAuthorization,**跳过黑名单检查**,
/// 致 .ssh/.aws 等敏感路径在 Windows 上绕过黑名单。修复后原始路径也判黑名单。
/// 此测试锁死该修复:空 persistent + Unix 风格 .ssh 路径 → 必 Denied。
#[test]
fn test_check_path_blacklist_unix_style_ssh_on_windows() {
let allowed = AllowedDirs::default_with_root();
match check_path_authorization("/home/user/.ssh/config", &allowed) {
PathAuthDecision::Denied { .. } => {}
other => panic!("Unix 风格 .ssh 路径应 Denied(黑名单), got {:?}", other),
}
// 同理 .aws / .gnupg
match check_path_authorization("/home/user/.aws/credentials", &allowed) {
PathAuthDecision::Denied { .. } => {}
other => panic!("Unix 风格 .aws 路径应 Denied(黑名单), got {:?}", other),
}
}
/// 回归:合法相对路径(非黑名单)在空 persistent 下仍应返 NeedsAuthorization(引导授权)。
/// 修复黑名单提前后,需确认非黑名单相对路径行为不变。
#[test]
fn test_check_path_non_blacklist_relative_still_needs_auth() {
let allowed = AllowedDirs::default_with_root();
match check_path_authorization("some/regular/path", &allowed) {
PathAuthDecision::NeedsAuthorization { .. } => {}
other => panic!("非黑名单相对路径 + 空 persistent 应 NeedsAuthorization, got {:?}", other),
}
}
// ============================================================
// F-260620: strip_verbatim 比对侧收口 + 黑名单增强测试
// 三方审查(安全/UX/跨端)交叉印证:strip_verbatim 仅写入侧调用,比对侧遗漏致误弹窗。