fix: 5 个安全/正确性问题(第二轮)
1. (High) user_id NOT NULL 导致 add 任务失败 → ALTER TABLE 加 DEFAULT '', DROP NOT NULL 2. (High) 调度器任务卡住阻塞后续 → timeout(120s) + kill_on_drop 3. (Medium) 多实例重复执行 → SELECT ... FOR UPDATE SKIP LOCKED 4. (Medium) SQL 注入(字符串拼接) → psql -v 参数化 (:name, :cmd, :interval) 5. (Medium) 非工具对话 usage 未记录 → consume() 改为 &mut self,消费后再读 usage
This commit is contained in:
@@ -222,7 +222,8 @@ impl ChatHandle {
|
||||
chunk
|
||||
}
|
||||
|
||||
pub async fn consume(mut self) -> Result<String, String> {
|
||||
/// 消费所有流式块,返回完整文本
|
||||
pub async fn consume(&mut self) -> Result<String, String> {
|
||||
while let Some(chunk) = self.next_chunk().await {
|
||||
if let StreamChunk::Error(e) = chunk { return Err(e); }
|
||||
}
|
||||
|
||||
+4
-2
@@ -598,7 +598,9 @@ async fn generate_reply(
|
||||
user_text: String,
|
||||
db: &Option<Arc<Database>>,
|
||||
) -> Result<String, String> {
|
||||
let handle = conv.chat(user_text).await?;
|
||||
let mut handle = conv.chat(user_text).await?;
|
||||
// 先消费流(usage 在 Done chunk 中才可用)
|
||||
let reply = handle.consume().await?;
|
||||
if let Some(u) = handle.get_usage() {
|
||||
info!(
|
||||
"📊 Token: {} 总 / {} 提示 / {} 生成 | 缓存命中 {} | 缓存未命中 {}",
|
||||
@@ -610,7 +612,7 @@ async fn generate_reply(
|
||||
);
|
||||
store_usage(db, "", conv.model(), conv.provider_name(), u).await;
|
||||
}
|
||||
handle.consume().await
|
||||
Ok(reply)
|
||||
}
|
||||
|
||||
async fn generate_reply_with_tools(
|
||||
|
||||
+16
-11
@@ -76,7 +76,8 @@ async fn fetch_due_tasks(pool: &PgPool) -> Result<Vec<DueTask>, String> {
|
||||
FROM scheduled_tasks
|
||||
WHERE enabled = true AND next_run_at <= NOW()
|
||||
ORDER BY next_run_at ASC
|
||||
LIMIT 10
|
||||
LIMIT 5
|
||||
FOR UPDATE SKIP LOCKED
|
||||
"#,
|
||||
)
|
||||
.fetch_all(pool)
|
||||
@@ -110,17 +111,20 @@ async fn execute_task(task: &DueTask) -> String {
|
||||
task.cwd.clone()
|
||||
};
|
||||
|
||||
let result = tokio::process::Command::new(shell)
|
||||
.arg("-c")
|
||||
.arg(&task.command)
|
||||
.current_dir(&cwd)
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.output()
|
||||
.await;
|
||||
let result = tokio::time::timeout(
|
||||
std::time::Duration::from_secs(120),
|
||||
tokio::process::Command::new(shell)
|
||||
.arg("-c")
|
||||
.arg(&task.command)
|
||||
.current_dir(&cwd)
|
||||
.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true)
|
||||
.output()
|
||||
).await;
|
||||
|
||||
match result {
|
||||
Ok(output) => {
|
||||
Ok(Ok(output)) => {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
|
||||
|
||||
@@ -138,7 +142,8 @@ async fn execute_task(task: &DueTask) -> String {
|
||||
)
|
||||
}
|
||||
}
|
||||
Err(e) => format!("执行失败: {}", e),
|
||||
Ok(Err(e)) => format!("进程错误: {}", e),
|
||||
Err(_timeout) => "执行超时 (120s)".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user