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:
@@ -5,7 +5,7 @@
|
||||
备忘录存储在 `.data/memos.json` 文件中。
|
||||
|
||||
## Risk Level
|
||||
Low
|
||||
High
|
||||
|
||||
## Parameters
|
||||
```json
|
||||
|
||||
@@ -30,6 +30,8 @@ pub struct SummaryEntry {
|
||||
pub struct ChatSession {
|
||||
pub user_id: String,
|
||||
pub db_pool: Option<Arc<sqlx::PgPool>>,
|
||||
/// 当前正在处理消息的用户 ID(spawn 前设置,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
@@ -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
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user