fix: resolve medium/low audit findings across backend and frontend

- ctx CLI: validate int() limit arg, wrap FTS5 MATCH in try/except
- ctx.rs: FTS5 error message clarity, Mutex::lock() returns Err not panic
- sdk-messages.ts: runtime type guards (str/num) replace bare `as` casts
- agent-runner.ts: strip ANTHROPIC_* env vars alongside CLAUDE*
- agent-dispatcher.ts: timestamps use seconds (match session.rs convention)
- remote.rs: disconnect handler uses lock().await not try_lock()
- session.rs: propagate pane_ids serialization error
- watcher.rs: reject root-level paths instead of silent no-op
- lib.rs: log warnings on profile.toml read failure and resource_dir error
- agent-bridge.ts: validate event payload is object before cast
This commit is contained in:
Hibryda 2026-03-08 20:10:54 +01:00
parent 044f891c3a
commit 3f1638c98b
10 changed files with 97 additions and 57 deletions

View file

@ -127,7 +127,7 @@ impl CtxDb {
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY | rusqlite::OpenFlags::SQLITE_OPEN_NO_MUTEX,
).map_err(|e| format!("Failed to reopen database: {e}"))?;
let mut lock = self.conn.lock().unwrap();
let mut lock = self.conn.lock().map_err(|_| "ctx database lock poisoned".to_string())?;
*lock = Some(ro_conn);
Ok(())
@ -149,7 +149,7 @@ impl CtxDb {
}
pub fn get_context(&self, project: &str) -> Result<Vec<CtxEntry>, String> {
let lock = self.conn.lock().unwrap();
let lock = self.conn.lock().map_err(|_| "ctx database lock poisoned".to_string())?;
let conn = lock.as_ref().ok_or("ctx database not found")?;
let mut stmt = conn
@ -173,7 +173,7 @@ impl CtxDb {
}
pub fn get_shared(&self) -> Result<Vec<CtxEntry>, String> {
let lock = self.conn.lock().unwrap();
let lock = self.conn.lock().map_err(|_| "ctx database lock poisoned".to_string())?;
let conn = lock.as_ref().ok_or("ctx database not found")?;
let mut stmt = conn
@ -197,7 +197,7 @@ impl CtxDb {
}
pub fn get_summaries(&self, project: &str, limit: i64) -> Result<Vec<CtxSummary>, String> {
let lock = self.conn.lock().unwrap();
let lock = self.conn.lock().map_err(|_| "ctx database lock poisoned".to_string())?;
let conn = lock.as_ref().ok_or("ctx database not found")?;
let mut stmt = conn
@ -220,7 +220,7 @@ impl CtxDb {
}
pub fn search(&self, query: &str) -> Result<Vec<CtxEntry>, String> {
let lock = self.conn.lock().unwrap();
let lock = self.conn.lock().map_err(|_| "ctx database lock poisoned".to_string())?;
let conn = lock.as_ref().ok_or("ctx database not found")?;
let mut stmt = conn
@ -236,7 +236,14 @@ impl CtxDb {
updated_at: String::new(), // FTS5 virtual table doesn't store updated_at
})
})
.map_err(|e| format!("ctx search failed: {e}"))?
.map_err(|e| {
let msg = e.to_string();
if msg.contains("fts5") || msg.contains("syntax") {
format!("Invalid search query syntax: {e}")
} else {
format!("ctx search failed: {e}")
}
})?
.collect::<Result<Vec<_>, _>>()
.map_err(|e| format!("ctx row read failed: {e}"))?;

View file

@ -240,7 +240,10 @@ fn claude_list_profiles() -> Vec<ClaudeProfile> {
// Read profile.toml for metadata
let toml_path = entry.path().join("profile.toml");
let (email, subscription_type, display_name) = if toml_path.exists() {
let content = std::fs::read_to_string(&toml_path).unwrap_or_default();
let content = std::fs::read_to_string(&toml_path).unwrap_or_else(|e| {
log::warn!("Failed to read {}: {e}", toml_path.display());
String::new()
});
(
extract_toml_value(&content, "email"),
extract_toml_value(&content, "subscription_type"),
@ -606,7 +609,10 @@ pub fn run() {
.handle()
.path()
.resource_dir()
.unwrap_or_default();
.unwrap_or_else(|e| {
log::warn!("Failed to resolve resource_dir: {e}");
std::path::PathBuf::new()
});
let dev_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()

View file

@ -226,11 +226,10 @@ impl RemoteManager {
// Mark disconnected and clear connection
{
if let Ok(mut machines) = machines_ref.try_lock() {
if let Some(machine) = machines.get_mut(&mid) {
machine.status = "disconnected".to_string();
machine.connection = None;
}
let mut machines = machines_ref.lock().await;
if let Some(machine) = machines.get_mut(&mid) {
machine.status = "disconnected".to_string();
machine.connection = None;
}
}
let _ = app_handle.emit("remote-machine-disconnected", &serde_json::json!({

View file

@ -284,7 +284,8 @@ impl SessionDb {
pub fn save_layout(&self, layout: &LayoutState) -> Result<(), String> {
let conn = self.conn.lock().unwrap();
let pane_ids_json = serde_json::to_string(&layout.pane_ids).unwrap_or_default();
let pane_ids_json = serde_json::to_string(&layout.pane_ids)
.map_err(|e| format!("Serialize pane_ids failed: {e}"))?;
conn.execute(
"UPDATE layout_state SET preset = ?1, pane_ids = ?2 WHERE id = 1",
params![layout.preset, pane_ids_json],

View file

@ -72,8 +72,10 @@ impl FileWatcherManager {
)
.map_err(|e| format!("Failed to create watcher: {e}"))?;
let watch_dir = file_path.parent()
.ok_or_else(|| format!("Cannot watch root-level path: {path}"))?;
watcher
.watch(file_path.parent().unwrap_or(&file_path), RecursiveMode::NonRecursive)
.watch(watch_dir, RecursiveMode::NonRecursive)
.map_err(|e| format!("Failed to watch path: {e}"))?;
let mut watchers = self.watchers.lock().unwrap();