fix: 5 个安全/正确性问题

1. (High) 无 DB 时审批绕过 — approval_manager 始终创建,
   无 DB 时用纯内存模式,不再绕过审批
2. (High) 多用户串上下文 — user_id 改存 ChatSession,
   spawn 前写入,executor 从 session 读(而非共享 Arc)
3. (Medium) 技能超时未杀子进程 — cmd.kill_on_drop(true)
4. (Medium) provider 配置不生效 — 模型选择跟随 LLM_PROVIDER
   环境变量(deepseek/lmstudio 各自取对应模型名)
5. (Low) manage_memos 改为 High 风险(add/delete 有副作用)
This commit is contained in:
2026-06-02 10:56:44 +08:00
parent 5daaba686b
commit 5602b08faf
5 changed files with 21 additions and 20 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
备忘录存储在 `.data/memos.json` 文件中。
## Risk Level
Low
High
## Parameters
```json
+3
View File
@@ -30,6 +30,8 @@ pub struct SummaryEntry {
pub struct ChatSession {
pub user_id: String,
pub db_pool: Option<Arc<sqlx::PgPool>>,
/// 当前正在处理消息的用户 IDspawn 前设置,executor 中读取)
pub current_user_id: String,
/// 摘要总结点——此位置之前的消息已被压缩
pub checkpoint: usize,
/// 全部消息(checkpoint 之后的保持完整)
@@ -49,6 +51,7 @@ impl Default for ChatSession {
Self {
user_id: "default".to_string(),
db_pool: None,
current_user_id: String::new(),
checkpoint: 0,
messages: Vec::new(),
summaries: Vec::new(),
+1 -2
View File
@@ -27,8 +27,7 @@ pub trait LlmProvider: Send + Sync {
pub type BoxedProvider = Arc<dyn LlmProvider>;
/// 从配置创建恰当的提供商
pub fn create_provider(_config: &ConversationConfig) -> Result<BoxedProvider, String> {
// 根据 model 前缀或环境变量选择
pub fn create_provider(config: &ConversationConfig) -> Result<BoxedProvider, String> {
let provider = std::env::var("LLM_PROVIDER")
.unwrap_or_else(|_| "deepseek".to_string())
.to_lowercase();
+15 -17
View File
@@ -220,10 +220,9 @@ async fn cmd_listen(
Some(Arc::new(skill_registry))
};
// 审批管理器
let approval_manager = database
.as_ref()
.map(|db| Arc::new(ApprovalManager::new(Some(Arc::new(db.pool().clone())))));
let approval_manager = Arc::new(ApprovalManager::new(
database.as_ref().map(|db| Arc::new(db.pool().clone()))
));
// 共享的当前用户 ID(用于审批消息)
let current_user_id = Arc::new(tokio::sync::Mutex::new(String::new()));
@@ -231,7 +230,12 @@ async fn cmd_listen(
let conversation = if enable_llm {
let sys_prompt = std::env::var("WEIXIN_LLM_SYSTEM_PROMPT")
.unwrap_or_else(|_| DEFAULT_SYSTEM_PROMPT.to_string());
let model = config.llm.deepseek.model.clone();
let provider = std::env::var("LLM_PROVIDER").unwrap_or_else(|_| "deepseek".to_string());
let model = if provider == "lmstudio" {
config.llm.lmstudio.model.clone()
} else {
config.llm.deepseek.model.clone()
};
let mut cfg = ConversationConfig {
system_prompt: sys_prompt,
@@ -360,7 +364,7 @@ async fn cmd_listen(
let args = args_json.to_string();
Box::pin(async move {
let user_id = current_user.lock().await.clone();
let user_id = session.lock().await.current_user_id.clone();
match n.as_str() {
"read_memories" => return Ok(memory_store.read().await),
"write_memory" => {
@@ -379,7 +383,7 @@ async fn cmd_listen(
if let Some(name) = cap_name {
if let Some(ref reg) = reg {
let mut ctx = ExecutionContext::new(&user_id);
if let Some(a) = approval { ctx = ctx.with_approval(a); }
ctx = ctx.with_approval(approval.clone());
ctx = ctx.with_wechat(send);
let result = reg.execute(&name, cap_params, &ctx).await;
return Ok(result.output);
@@ -394,9 +398,7 @@ async fn cmd_listen(
let params: serde_json::Value =
serde_json::from_str(&args).unwrap_or(serde_json::json!({}));
let mut ctx = ExecutionContext::new(&user_id);
if let Some(a) = approval {
ctx = ctx.with_approval(a);
}
ctx = ctx.with_approval(approval.clone());
ctx = ctx.with_wechat(send);
let result = reg.execute(&n, params, &ctx).await;
Ok(result.output)
@@ -451,7 +453,7 @@ async fn cmd_listen(
file_state.save_runtime(&client.get_updates_buf().await);
info!("已退出");
}
_ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, approval_manager, listen_user_id) => {}
_ = listen_loop(&client, enable_llm, echo, &account_id, database, file_state, conversation, &approval_manager, listen_user_id) => {}
}
}
@@ -463,7 +465,7 @@ async fn listen_loop(
database: &Option<Arc<Database>>,
file_state: &state::StateManager,
conversation: Option<Arc<Conversation>>,
approval_manager: Option<Arc<ApprovalManager>>,
approval_manager: &ApprovalManager,
current_user_id: Arc<tokio::sync::Mutex<String>>,
) {
loop {
@@ -526,11 +528,7 @@ async fn listen_loop(
}
// 审批回复检查
let is_approval_reply = if let Some(mgr) = &approval_manager {
mgr.handle_reply(from, text).await.is_some()
} else {
false
};
let is_approval_reply = approval_manager.handle_reply(from, text).await.is_some();
if is_approval_reply {
info!("消息匹配审批确认码,不传给 LLM");
+1
View File
@@ -226,6 +226,7 @@ async fn run_skill(skill: &Skill, params: serde_json::Value, _ctx: &ExecutionCon
cmd.stdin(std::process::Stdio::piped());
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
cmd.kill_on_drop(true);
let mut child = match cmd.spawn() {
Ok(c) => c,