fix(industry): 审计修复 — 4 CRITICAL + 5 HIGH 全部解决
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

C1: SaaS industry/service.rs SQL 注入风险 → 参数化查询 ($N 绑定)
C2: INDUSTRY_CONFIGS 死链 → Kernel 共享 Arc 接通 ButlerRouter
C3: IndustryListItem 缺 keywords_count → SQL 查询 + 类型补全
C4: set_account_industries 非事务性 → batch 验证 + 事务 DELETE+INSERT
H8: Accounts.tsx mutate 竞态 → mutateAsync 顺序等待
H9: XML 注入未转义 → xml_escape() 辅助函数
H10: update_industry 覆盖 source → 保留原始值
H11: 面包屑缺少 /industries → 添加行业配置映射
This commit is contained in:
iven
2026-04-12 19:06:19 +08:00
parent c3593d3438
commit fbc8c9fdde
7 changed files with 119 additions and 73 deletions

View File

@@ -693,9 +693,11 @@ pub async fn viking_store_with_summaries(
/// Load industry keywords into the ButlerRouter middleware.
///
/// Called from the frontend after fetching industry configs from SaaS.
/// Updates the ButlerRouter's dynamic keyword source for routing.
/// Updates the shared `industry_keywords` Arc on the Kernel, which the
/// ButlerRouterMiddleware reads automatically (same Arc instance).
#[tauri::command]
pub async fn viking_load_industry_keywords(
kernel_state: tauri::State<'_, crate::kernel_commands::KernelState>,
configs: String,
) -> Result<(), String> {
let raw: Vec<IndustryConfigPayload> = serde_json::from_str(&configs)
@@ -711,43 +713,25 @@ pub async fn viking_load_industry_keywords(
})
.collect();
// The ButlerRouter is in the kernel's middleware chain.
// For now, log and store for future retrieval by the kernel.
tracing::info!(
"[viking_commands] Loading {} industry keyword configs",
"[viking_commands] Loading {} industry keyword configs into Kernel",
industry_configs.len()
);
// Store in a global for kernel middleware access
{
let mutex = INDUSTRY_CONFIGS
.get_or_init(|| async { std::sync::Mutex::new(Vec::new()) })
.await;
let mut guard = mutex.lock().map_err(|e| format!("Lock poisoned: {}", e))?;
// 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();
let mut guard = shared.write().await;
*guard = industry_configs;
tracing::info!("[viking_commands] Industry keywords synced to ButlerRouter middleware");
} else {
tracing::warn!("[viking_commands] Kernel not initialized, industry keywords not loaded");
}
Ok(())
}
/// Global industry configs storage (accessed by kernel middleware)
static INDUSTRY_CONFIGS: tokio::sync::OnceCell<std::sync::Mutex<Vec<zclaw_runtime::IndustryKeywordConfig>>> =
tokio::sync::OnceCell::const_new();
/// Get the stored industry configs
pub async fn get_industry_configs() -> Vec<zclaw_runtime::IndustryKeywordConfig> {
let mutex = INDUSTRY_CONFIGS
.get_or_init(|| async { std::sync::Mutex::new(Vec::new()) })
.await;
match mutex.lock() {
Ok(guard) => guard.clone(),
Err(e) => {
tracing::warn!("[viking_commands] Industry configs lock poisoned: {}", e);
Vec::new()
}
}
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------