All releases
v0.11.1
security, performance & stability hardening
Released on .
Security, performance, and stability hardening from a full codebase audit. No changes to the 3 tools' input/output contracts (auto_optimize, smart_file_read, code_execute). Every change below is backward-compatible.
Install or upgrade:
npx distill-mcp@0.11.1 setup # or: bunx distill-mcp@latest setup
🔒 Security
- ReDoS guard on
ctx.search.grep. The grep pattern is LLM-authored and runs host-side (outside the QuickJS execution timeout) over up to 500 files. Catastrophic-backtracking or overlong patterns are now rejected viasafe-regex2before the regex is compiled. WORKING_DIRis no longer exposed to guest code. The host bridge resolves paths host-side, so the absolute host path was only something guest code could exfiltrate pastsanitizeError.smart_file_readcloses a validate-vs-read TOCTOU. The separatefs.accesspre-check is gone; a single guarded read follows the symlink-resolved path that validation approved, applies the blocked-pattern policy to that effective path, and never leaks the host path on error.- More keyword-reconstruction vectors blocked in the static analyzer:
atob,btoa, andString.fromCodePointjoinString.fromCharCodeon the layer-1 blocklist (QuickJS WASM stays the final containment). sanitizeErrorwidened to scrub/Users(macOS home),/tmp,/var,/opt,/private, and/srv, not just/home.- Language strings validated at the QuickJS guest→host boundary instead of being blind-cast to
SupportedLanguage.
🐛 Fixed
- Tree-sitter parsers recover from a failed init. Rust, Go, Python, PHP, and Swift parsers reset their init promise when WASM loading fails, so a transient failure (OOM, missing file) no longer poisons every later parse for the lifetime of the process. The warm-up failure is logged once per process.
- Cache memory counter can no longer go negative. Concurrent writes to the same key could double-subtract
memorySizeBytesinto the negative, silently disabling memory-based eviction (unbounded cache growth). It is now clamped at 0. - Hardened CLI argument handling.
main().catchhandles non-Errorthrows and preserves the stack on startup failures;setupwarns on unknown options instead of ignoring typos;analyzerejects a non-numeric or negative--threshold(which would otherwise silently match nothing).
⚡ Performance
- QuickJS WASM loader is memoized and shared across execution paths. The production
code_executepath was re-instantiating the entire WASM module on every call (~50-200ms + GC pressure); a single cached loader now serves both the production and isolation-test paths. - Log scorer memoizes
scoreAll(), which was recomputed 3+ times per summary. - Log clustering falls back to Jaccard above 500 entries, bounding the O(n²) Levenshtein cost on large corpora. Small-corpus behavior is unchanged.
- Cache size estimation uses a bounded structural probe instead of
JSON.stringify-ing whole cached values just to measure them.
♻️ Changed
- Renamed the sandbox SDK type
CtxOptSDKtoDistillSDKand removed residualCtxOptbranding.
🗑️ Removed
- Dead
shared/module (utils.ts,constants.ts,types.ts, and its barrel) with no consumers.shared/path-security.ts, the only used member, stays.
Published to npm via Trusted Publishing (OIDC) with build provenance. Full changelog: CHANGELOG.md. Compare: https://github.com/ArthurDEV44/distill/compare/v0.11.0...v0.11.1
Source: this release on GitHub.