fix(industry): 二次审计修复 — 2 CRITICAL + 4 HIGH + 2 MEDIUM
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
C-1: Industries.tsx 创建弹窗缺少 id 字段 → 添加 id 输入框 + 自动生成 C-2: Accounts.tsx handleSave 无 try/catch → 包装 + handleClose 统一关闭 V1: viking_commands Mutex 跨 await → 先 clone Arc 再释放 Mutex I1: intelligence_hooks 误导性"相关度" → 移除 access_count 伪分数 I2: pain point 摘要未 XML 转义 → xml_escape() 处理 S1: industry status 无枚举验证 → active/inactive 白名单 S2: create_industry id 无格式验证 → 正则 + 长度检查 H-3: Industries.tsx 编辑模态数据竞争 → data.id === industryId 守卫 H-4: Accounts.tsx useEffect 覆盖用户编辑 → editingId 守卫
This commit is contained in:
@@ -344,14 +344,14 @@ async fn build_continuity_context(agent_id: &str, user_message: &str) -> String
|
||||
.collect();
|
||||
if !high_pains.is_empty() {
|
||||
let pain_lines: Vec<String> = high_pains.iter()
|
||||
.filter_map(|p| {
|
||||
.map(|p| {
|
||||
let summary = &p.summary;
|
||||
let count = p.occurrence_count;
|
||||
let conf = (p.confidence * 100.0) as u8;
|
||||
Some(format!(
|
||||
format!(
|
||||
"- {} (出现{}次, 置信度 {}%)",
|
||||
summary, count, conf
|
||||
))
|
||||
xml_escape(summary), count, conf
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
if !pain_lines.is_empty() {
|
||||
@@ -378,8 +378,7 @@ async fn build_continuity_context(agent_id: &str, user_message: &str) -> String
|
||||
.map(|e| {
|
||||
let overview = e.overview.as_deref().unwrap_or(&e.content);
|
||||
let truncated: String = overview.chars().take(60).collect();
|
||||
let score_pct = (e.access_count as f64).min(10.0) / 10.0 * 100.0;
|
||||
format!("- {} (相关度: {:.0}%)", truncated, score_pct)
|
||||
format!("- {}", xml_escape(&truncated))
|
||||
})
|
||||
.collect();
|
||||
parts.push(format!("<experience>\n{}\n</experience>", exp_lines.join("\n")));
|
||||
@@ -398,6 +397,13 @@ async fn build_continuity_context(agent_id: &str, user_message: &str) -> String
|
||||
)
|
||||
}
|
||||
|
||||
/// Escape XML special characters in content injected into `<butler-context>`.
|
||||
fn xml_escape(s: &str) -> String {
|
||||
s.replace('&', "&")
|
||||
.replace('<', "<")
|
||||
.replace('>', ">")
|
||||
}
|
||||
|
||||
/// Store a lightweight experience entry from a trigger signal.
|
||||
///
|
||||
/// Uses VikingStorage directly — template-based, no LLM cost.
|
||||
|
||||
@@ -719,9 +719,13 @@ pub async fn viking_load_industry_keywords(
|
||||
);
|
||||
|
||||
// Update through the Kernel's shared Arc (connected to ButlerRouterMiddleware)
|
||||
let kernel_guard = kernel_state.lock().await;
|
||||
if let Some(kernel) = kernel_guard.as_ref() {
|
||||
let shared = kernel.industry_keywords();
|
||||
// Clone the Arc first, then drop the KernelState Mutex before awaiting RwLock.
|
||||
// This prevents blocking all other kernel commands during the write.
|
||||
let shared = {
|
||||
let kernel_guard = kernel_state.lock().await;
|
||||
kernel_guard.as_ref().map(|k| k.industry_keywords())
|
||||
};
|
||||
if let Some(shared) = shared {
|
||||
let mut guard = shared.write().await;
|
||||
*guard = industry_configs;
|
||||
tracing::info!("[viking_commands] Industry keywords synced to ButlerRouter middleware");
|
||||
|
||||
Reference in New Issue
Block a user