修复: 全库走查 P0+P1(df-mcp 数据丢失/evaluate 拆 + df-execute probe_pwsh 死缓存/超时/单源 + ai_self_review 注入隔离)

- df-mcp P0: update_idea/project/task 缺省回退 existing(治部分更新清空 title 丢数据)+ P1: evaluate_idea 拆只读(Low)+score_idea(Medium 写),read-only 不再改库
- df-execute P0: probe_pwsh 死缓存(两 OnceLock 合并 PWSH_CACHE 单源,治 Windows 永走 PS5)+ P1: probe_pwsh 3s 超时防挂起 + detect_shell 复用 shell.rs 单源(探测与执行对齐)
- df-nodes P1: ai_self_review prompt 注入隔离(truncate + XML 标签 <task_output> 数据/指令隔离 + system 声明)
- generate_image: SSRF 集成测试 + b64 OOM 估算纯函数测试(走查测试增强)
This commit is contained in:
lxy
2026-08-02 03:11:44 +08:00
parent dffc4e4851
commit fc249adf17
7 changed files with 1000 additions and 82 deletions
+38 -19
View File
@@ -221,18 +221,27 @@ fn read_windows_version() -> Option<String> {
None
}
/// 探测默认 shell(复用 shell.rs 的 pwsh 探测语义)。
/// 探测默认 shell(Windows 走 shell.rs 单源,Unix 读 SHELL 环境变量)。
///
/// 单源语义(2026-08 修复):Windows 分支不再独立 probe pwsh/powershell,而是:
/// 1) 先调 `shell::probe_pwsh_blocking()` 填充模块级 `PWSH_CACHE`(与 `shell::probe_pwsh()` 异步路径
/// 共用同一 OnceLock + 同一阻塞探测实现,杜绝两套逻辑漂移);
/// 2) 再调 `shell::current_shell()`(同步读 `PWSH_CACHE`),映射 ShellType → prompt 字符串。
/// 后续 `execute()` → `probe_pwsh().await` 直接命中缓存,跳过重复探测。
/// Unix 分支保留原 SHELL 环境变量读取(与执行侧 ShellType::Sh 默认一致,无漂移风险)。
fn detect_shell() -> String {
#[cfg(windows)]
{
// 优先 pwsh(PS7,支持 &&),其次 powershell(PS5),兜底 cmd。
if probe_command_success("pwsh", &["-NoProfile", "-Command", "exit 0"]) {
return "pwsh".to_string();
// 单源填充 PWSH_CACHE(本函数运行在 EnvSnapshot::detect 的 spawn_blocking 内,
// 通常先于首次 execute(),故常是缓存的首次写入者)。
crate::shell::probe_pwsh_blocking();
// 单源读取并映射为 prompt 字符串。
match crate::shell::current_shell() {
crate::shell::ShellType::Pwsh => "pwsh".to_string(),
crate::shell::ShellType::PowerShell => "powershell".to_string(),
crate::shell::ShellType::Cmd => "cmd".to_string(),
crate::shell::ShellType::Sh => "sh".to_string(),
}
if probe_command_success("powershell", &["-NoProfile", "-Command", "exit 0"]) {
return "powershell".to_string();
}
return "cmd".to_string();
}
#[cfg(not(windows))]
{
@@ -301,17 +310,6 @@ fn extract_codepage(text: &str) -> Option<String> {
}
}
/// 执行 `tool args`,成功(true)即工具可用。Windows 加 CREATE_NO_WINDOW 防黑窗。
#[allow(dead_code)] // 仅 Windows 路径调用,非 Windows 静态裁掉
fn probe_command_success(tool: &str, args: &[&str]) -> bool {
let mut cmd = std::process::Command::new(tool);
cmd.args(args);
cmd.stdout(Stdio::null()).stderr(Stdio::null());
#[cfg(windows)]
cmd.creation_flags(0x0800_0000); // CREATE_NO_WINDOW
cmd.status().map(|s| s.success()).unwrap_or(false)
}
/// 执行 `tool --version`,解析首行返回版本串。失败/超时返回 None,不阻塞调用方。
///
/// 例:python --version 输出 "Python 3.11.5" → 返回 "3.11.5";git --version 输出
@@ -429,6 +427,27 @@ mod tests {
assert_eq!(a, b, "detect() 应返回同一静态引用");
}
/// 单源不变量回归:prompt 期 shell(env_snapshot.shell)与执行期 shell(current_shell)必须一致。
///
/// 历史 bug:detect_shell 持独立 probe_command_success 探测,与 shell.rs::probe_pwsh 各填各的
/// OnceLock → prompt 告诉 LLM 用 pwsh,执行却走 powershell(或反之)。根本修:二者共用
/// shell.rs 的 probe_pwsh_blocking + PWSH_CACHE 单源。本测试锁定「无漂移」不变量。
#[cfg(windows)]
#[tokio::test]
async fn detect_shell_matches_shell_rs_current_shell() {
// detect() 内 detect_shell → probe_pwsh_blocking 填 PWSH_CACHE,再读 current_shell 映射。
let snap = EnvSnapshot::detect().await;
let current = crate::shell::current_shell();
// 二者必须一致(同一 PWSH_CACHE 单源读出)。
match snap.shell.as_str() {
"pwsh" => assert_eq!(current, crate::shell::ShellType::Pwsh),
"powershell" => assert_eq!(current, crate::shell::ShellType::PowerShell),
"cmd" => assert_eq!(current, crate::shell::ShellType::Cmd),
"sh" => assert_eq!(current, crate::shell::ShellType::Sh),
other => panic!("未知 shell 字符串: {}", other),
}
}
#[test]
fn to_prompt_contains_os_and_shell() {
let snap = EnvSnapshot {