fix: comprehensive audit fixes for dashboard and backend

Backend:
- Emit WebSocket events from REST delete/promote/demote handlers
- Emit DreamStarted/ConsolidationStarted from MCP tool dispatch
- Add path validation in backup_to() for defense-in-depth

Dashboard:
- Fix ConnectionDiscovered field names (source_id/target_id)
- Fix $effect → onMount in settings (prevents infinite loop)
- Fix $derived → $derived.by in RetentionCurve
- Fix field name mismatches in settings (nodesProcessed, etc.)
- Fix nested <button> → <span role="button"> in memories
- Fix unhandled Promise rejection in stats consolidation
- Add missing EVENT_TYPE_COLORS entries
- Add Three.js resource disposal and event listener cleanup
- Eliminate duplicate root page, redirect to /graph
- Update nav links and keyboard shortcuts to /graph

All 734+ tests passing, 22MB binary, zero build warnings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-02-22 15:50:47 -06:00
parent 22831af509
commit ec2af6e71b
220 changed files with 347 additions and 443 deletions

View file

@ -3293,8 +3293,13 @@ impl Storage {
let path_str = path.to_str().ok_or_else(|| {
StorageError::Init("Invalid backup path encoding".to_string())
})?;
// Validate path: reject control characters (except tab) for defense-in-depth
if path_str.bytes().any(|b| b < 0x20 && b != b'\t') {
return Err(StorageError::Init("Backup path contains invalid characters".to_string()));
}
let reader = self.reader.lock()
.map_err(|_| StorageError::Init("Reader lock poisoned".into()))?;
// VACUUM INTO doesn't support parameterized queries; escape single quotes
reader.execute_batch(&format!("VACUUM INTO '{}'", path_str.replace('\'', "''")))?;
Ok(())
}