Commit Graph

2 Commits

Author SHA1 Message Date
rUv 41bee64593 fix(recorder): bound history query (memory-DoS) + add missing transactional purge (disk-DoS); SQL-injection & NaN dims clean (#1084)
* fix(homecore-recorder): bound history query + add transactional purge (memory-DoS + disk-DoS)

Security review of the HA-compat state recorder (ADR-132) found two real
bounding bugs; SQL-injection and NaN-index dimensions confirmed clean.

(1) Memory-DoS: get_state_history carried no LIMIT — a wide [since,until]
    window over a high-frequency entity loaded an unbounded row set into a
    single in-memory Vec. Added LIMIT MAX_HISTORY_ROWS (1,000,000); the
    sibling search paths were already k-bounded.

(2) Disk-DoS / documented-but-missing purge: README advertised
    Recorder::purge(older_than) but no retention path existed -> unbounded
    disk growth. Added a transactional purge with an EXCLUSIVE cutoff
    (idempotent, no off-by-one) that deletes old states+events and
    garbage-collects orphaned state_attributes blobs (dedup-shared blobs
    are kept until their last referencing state is gone). All three deletes
    run in one transaction so a mid-purge failure rolls back cleanly.

Pinning tests (homecore-recorder 19->25 no-default / 25->31 ruvector, 0 failed):
- malicious_entity_id_is_stored_literally_not_executed (SQL injection)
- like_metacharacters_in_query_are_literal_not_wildcards (LIKE escape)
- history_query_carries_a_limit_clause (memory-DoS bound)
- purge_keeps_boundary_row_and_drops_older (exclusive-cutoff, true pin)
- purge_gcs_orphaned_attributes_but_keeps_shared (dedup-safe GC)
- purge_also_removes_old_events

No behaviour change beyond the two fixes. Python deterministic proof
unchanged (recorder is off the signal proof path).

Co-Authored-By: claude-flow <ruv@ruv.net>

* docs(homecore-recorder): record ADR-132 security review findings

Add a "3a. Security review" section to ADR-132 and a CHANGELOG [Unreleased]
Security entry covering the homecore-recorder review: SQL-injection and
NaN-index dimensions confirmed clean with evidence (every query bound; LIKE
pattern bound+escaped; SHA-256->i32->f32 embeddings always finite, empty
index/k=0 probed no-panic), plus the two fixes (unbounded history LIMIT,
transactional exclusive-cutoff purge with orphan-attribute GC).

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-06-14 21:00:52 -04:00
ruv 9457d441b2 docs(adr): write missing ADR-132 (HOMECORE-RECORDER) — resolves ADR-164 G3
homecore-recorder cites "ADR-132" in Cargo.toml/README/lib.rs/schema.rs/
semantic.rs, but no ADR-132 file existed — the durable-state backbone was
ungoverned (ADR-164 Gap G3 / Coverage-Gaps Lens A).

Reverse-documented from the shipped, tested crate (not invented): SQLite
HA-compatible recorder schema v48 (P1, 14 tests), ruvector HNSW semantic
index (P2, feature-gated, 20 tests), hash-embedding honesty note, P3 real
embeddings planned. Status: Accepted (shipped). Filename matches the link
the crate README already pointed at. Documented retroactively; honest about
hash-embedding limits and unbenchmarked latency targets.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-06-12 23:00:15 -04:00