fix(backend): resolve 10 bugs from the final full-codebase audit (#141)

A second adversarial audit of shipped main (post the 29+3 earlier fixes) found
10 more real bugs in areas the first pass under-covered (FTS sanitizer,
protocol/HTTP, connectors, frontend). None regressions — all pre-existing, and
several are "twin sites" of a class fixed elsewhere but missed here.

Majors:
- FTS5: sanitize_fts5_terms leaked bare operators on doubled input ("foo AND AND
  AND"), aborting MATCH with a syntax error or silently flipping AND->OR. Now
  filters operator tokens (repetition-proof).
- Server crash (DoS): unbounded MCP `hours_back`/`hours_forward` panicked via
  Duration::minutes in CaptureWindow; unclamped `limit` in match_context
  panicked via negative Vec::with_capacity / limit*2 overflow. Both now
  bounded/checked.
- Connectors: the source idempotency key omitted source_project, so two repos
  (or two Redmine instances) with overlapping ids clobbered each other's memory.
  Key + unique index (migration V19) now include source_project.

Minors/nits:
- list_memories now honors node_type/tag on the search path (were dropped).
- HTTP Accept honors */* and type wildcards (was hard 406 for curl et al.).
- Bearer scheme matched case-insensitively (RFC 7235).
- Dream insight time-span no longer seeds from now/-365d sentinels (fabricated a
  TemporalTrend for old clusters).
- WebSocket disconnect() detaches onclose before close() so it can't resurrect
  the socket via scheduleReconnect.
- Redmine thread truncation keeps the NEWEST journals (was keeping oldest, so
  new activity never re-indexed).

Tests: FTS operator-leak regression added. Full workspace green (1559 tests,
clippy -D warnings clean); dashboard check + 937 tests green.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sam Valladares 2026-07-02 16:05:40 -05:00 committed by GitHub
parent b7a021417b
commit 0e5c71b4ca
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 152 additions and 44 deletions

View file

@ -74,8 +74,19 @@ function createWebSocketStore() {
}
function disconnect() {
if (reconnectTimer) clearTimeout(reconnectTimer);
ws?.close();
if (reconnectTimer) {
clearTimeout(reconnectTimer);
reconnectTimer = null;
}
if (ws) {
// Detach onclose BEFORE closing: otherwise close() fires the handler,
// which calls scheduleReconnect and resurrects the socket we just
// asked to tear down.
ws.onclose = null;
ws.onerror = null;
ws.onmessage = null;
ws.close();
}
ws = null;
set({ connected: false, reconnecting: false, events: [], lastHeartbeat: null, error: null });
}