Skip to content

feat: v0.6.10 guardrails + esoteric bug sweep — 45 fixes + 10 guardrails#37

Merged
alderpath merged 4 commits into
masterfrom
fix/v0.6.9-guardrails
Jun 22, 2026
Merged

feat: v0.6.10 guardrails + esoteric bug sweep — 45 fixes + 10 guardrails#37
alderpath merged 4 commits into
masterfrom
fix/v0.6.9-guardrails

Conversation

@alderpath

@alderpath alderpath commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

v0.6.10 — Esoteric bug sweep (24 fixes + 4 new guardrails)

Deep audit identified bugs that don't show up in normal testing — race
conditions, panic recovery, cache keying, resource leaks, security
boundaries. All fixed with corresponding guardrails to prevent recurring patterns.

Critical (7)

  • ux.rs:402, mcp.rs:53: SQL PRAGMA corruption (synchronous=; with no value)

High (11)

  • Bug 51: Daemon connection counter leaks on thread spawn failure
  • Bug 52: RESPONSE_CACHE proper LRU eviction (was hash-order)
  • Bug 53: JSONL log persistent file handle (was reopen per call)
  • Bug 56: try_prefetch debounced to 32KB (was 1000+ spawns/sec)
  • Bug 57: Mutex::lock().unwrap() → unwrap_or_else(|e| e.into_inner())
  • Bug 58: Cache key includes model (was wrong model on hit)
  • Bug 59: Guard tool_calls-only check (revert prose regression)
  • Bug 64: Agent config cached 30s (was 4+ file opens/req)
  • Bug 68: Antidecision uses request workdir (inferred from path)
  • Bug 69: HTTP client 5-min timeout (was hang forever)
  • Bug 71: Error body capped 10MB (was unbounded)

Medium (7)

  • Bug 60: FTS5 query tokens sanitized (was corrupting search syntax)
  • Bug 61: open_existing_db_safe() with WAL+NORMAL for read paths
  • Bug 62: ANTI_DB capped 1000 workdirs with LRU eviction
  • Bug 63: Case-insensitive Bearer prefix
  • Bug 66: Upstream URL scheme validated (http/https only)
  • Bug 67: RATE_BUCKETS capped 1000 entries

Low (1)

  • Bug 70: Auth keys > 1KB rejected

v0.6.9 — Guardrails foundation (21 fixes + 6 guardrails)

reliary-core::fs_safe module: atomic_write, safe_read, safe_read_stdin, safe_open_db

6 guardrail rules: non-atomic-write, uncapped-read, curl-subprocess, sql-unknown-table, uncapped-stdin, hardcoded-list

4 new v0.6.10 guardrails: unbounded-collection, blocking-in-async, no-timeout, panic-lock

Test coverage

  • 89 unit tests passing
  • 10/10 guardrails passing on clean build
  • Pre-commit hook: passes

Critical:
- scavenger.rs:91 atomic_write (was std::fs::write, crash = corruption)
- scavenger.rs:80 10MB size guard (OOM risk on large files)
- ux.rs:403 wrong table name (file_phrases -> file_map, status was always 0)
- heal.rs:63 detect_test_command (cargo/pytest/npm/go)
- antidecision.rs extract_sed_target rewrite (was capturing s/old/new/ pattern)

High:
- antidecision.rs grammar-free: 25-item keyword ban -> structural heuristics
- routes.rs normalize_url: host-only anthropic check
- init.rs uninstall: file-based Pi detection, no pi --version
- README: document Claude Code 501 limitation
- config.rs:122 default Strict (was Reactive, matches docs)

Cleanup:
- 3 SQL PRAGMA corruptions: mcp.rs:53, ux.rs:402, scavenger.rs:60
- reindex.rs:118 bitwise | -> ||
- daemon.rs MAX_FILE_SIZE pub const, proxy.rs uses shared
- antidecision.rs: delete dead code (format_annotation, annotate_tool_result)

Tests: 267 passing (was 178, added 10 inline regression tests)
Phase A — Helpers (reliary-core::fs_safe):
- atomic_write: tmp + fsync + rename, cleans up on failure
- safe_read: read file with 10MB cap
- safe_read_stdin: stdin with 10MB cap
- safe_open_db: SQLite with correct PRAGMAs

Phase B — Bug-class guardrails (scripts/ci_guards.py):
1. non-atomic-write: std::fs::write outside atomic_write
2. uncapped-read: read_to_string without size guard
3. curl-subprocess: curl/wget subprocesses (we use reqwest)
4. sql-unknown-table: SQL against tables not in schema
5. uncapped-stdin: stdin reads without size cap
6. hardcoded-list: let valid_keys = [...] (drift risk)

Phase C — Wired into pre-commit and CI:
- .githooks/pre-commit: runs ci_guards.py --staged
- .github/workflows/ci.yml: runs ci_guards.py

Phase D — Single source of truth:
- config::FEATURE_DEFAULTS const
- config::VALID_CONFIG_KEYS const
- main.rs uses consts instead of hardcoded lists

Phase E — 21 bug fixes from round 3 audit:
- Bug 30: run_index backs up before delete
- Bug 33: do_update extracts to correct path (was broken)
- Bug 35-36, 39: compress/risk/dead use size-capped helpers
- Bug 37: start captures daemon stderr to log
- Bug 40: SSE_SESSIONS capped at 1000
- Bug 41: messages_handler drops lock before send
- Bug 43: UUID via getrandom (was monotonic)
- Bug 46: Pi settings.json uses atomic_write
- Bug 47: atomic_write cleans tmp on failure
- Bug 49: per-auth-key rate limit (60 req/min)
- Bug 50: edit_cache capped at 10K rows
- Bug 38, 44, 45, 48: feature/config validation
- Bug 31, 32, 34: trust/update/exec_sift fixes

Tests: 272 passing (was 267, +5 new fs_safe tests)
Guardrails: 6/6 passing on clean build
Deep audit identified bugs that don't show up in normal testing — race
conditions, panic recovery, cache keying, resource leaks, security
boundaries. All fixed with corresponding guardrails to prevent
recurring patterns.

Critical (7):
- ux.rs:402, mcp.rs:53: SQL PRAGMA corruption (synchronous=; no value)

High (11):
- Bug 51: Daemon connection counter leaks on thread spawn failure
- Bug 52: RESPONSE_CACHE proper LRU eviction (was hash-order)
- Bug 53: JSONL log persistent file handle (was reopen per call)
- Bug 56: try_prefetch debounced to 32KB (was 1000+ spawns/sec)
- Bug 57: Mutex::lock().unwrap() → unwrap_or_else(|e| e.into_inner())
- Bug 58: Cache key includes model (was wrong model on hit)
- Bug 59: Guard tool_calls-only check (revert prose regression)
- Bug 64: Agent config cached 30s (was 4+ file opens/req)
- Bug 68: Antidecision uses request workdir (inferred from path)
- Bug 69: HTTP client 5-min timeout (was hang forever)
- Bug 71: Error body capped 10MB (was unbounded)

Medium (7):
- Bug 60: FTS5 query tokens sanitized
- Bug 61: open_existing_db_safe() with WAL+NORMAL for read paths
- Bug 62: ANTI_DB capped 1000 workdirs with LRU
- Bug 63: Case-insensitive Bearer prefix
- Bug 66: Upstream URL scheme validated (http/https only)
- Bug 67: RATE_BUCKETS capped 1000

Low (1):
- Bug 70: Auth keys > 1KB rejected

Guardrails (4 new):
- unbounded-collection: catches HashMap without eviction
- blocking-in-async: catches std::fs in async without spawn_blocking
- no-timeout: catches reqwest::Client without .timeout()
- panic-lock: catches Mutex::lock().unwrap()

Tests: 89 unit tests pass, 10/10 guardrails pass on clean build
@alderpath alderpath changed the title feat: v0.6.9 guardrails + 21 bug fixes feat: v0.6.10 guardrails + esoteric bug sweep — 45 fixes + 10 guardrails Jun 22, 2026
… guardrails

- MCP path traversal: safe_path() canonicalizes all agent-provided paths
- Graceful shutdown: axum .with_graceful_shutdown() + JSONL flush
- Response cache: temperature in cache key, true LRU eviction
- COMPRESSION_DICT: refresh on index mtime change (was loaded once)
- Dead TCP daemon stripped (~100 lines of dead listener code)
- 2 new guardrails: mcp-path-traversal, lazy-lock-drop
- All 89 tests pass, 14/14 guardrails pass
@alderpath alderpath merged commit a89ce12 into master Jun 22, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant