Skip to content

v0.2.1 — 2026-04-25

Choose a tag to compare

@github-actions github-actions released this 25 Apr 19:06

Release Notes

Hotfix release closing 4 GitHub issues filed against v0.2.0, with scope expanded mid-flight to incorporate 2 audit-driven fixes (M6: 7 additional unguarded daemon handlers under #5; M7: request-side camelCase mismatch under #19). All 6 fixes were confirmed reproducible at their respective starting commits and fixed at root cause with new in-process integration tests pinning the bug shape. No regressions: full 964/964 matrix (730 exhaustive + 234 language-command) green across every fix commit; cargo clippy --workspace --all-features --tests -- -D warnings clean.

Fixed

  • #5 (security, Unix-side path traversal): tldr-daemon IPC handlers (secrets, vuln) now route every caller-supplied absolute path through tldr_core::validate_file_path before any filesystem read, refusing requests for paths outside the active project root with BAD_REQUEST. Pre-fix, the handlers accepted any is_absolute() path verbatim, which on a daemon already running could be exploited to extract /Users/<other>/.aws/credentials-shaped secrets. The Windows TCP unauthenticated listener portion of #5 remains an open design question (multi-user daemon sharing semantics) and is deferred to v0.3.0. (commit 00ee2dc)
  • #11: tldr vuln --format sarif and --format json now correctly label Deserialization findings as deserialization (CWE-502) — pre-fix, the wildcard match arm _ => VulnType::SqlInjection at crates/tldr-cli/src/commands/remaining/vuln.rs:645-651 silently mislabeled them as SQL injection (CWE-89). Ssrf was affected by the same wildcard and is now correctly mapped to CWE-918. The match is exhaustive — future tldr_core::security::vuln::VulnType variants will fail to compile until they are mapped, preventing the same bug pattern from recurring. (commit 181f929)
  • #12: tldr-mcp now speaks JSON-RPC 2.0 + MCP 2024-11-05 lifecycle correctly. Three sub-bugs fixed in one commit: (a) JsonRpcRequest.id is now Option<Value> with #[serde(default)] so notification frames (no id) deserialize cleanly; (b) the dispatcher now suppresses all response emission when id is None, per JSON-RPC 2.0 §4.1 ("a server MUST NOT reply to a notification"); (c) the canonical method notifications/initialized is routed (the legacy bare initialized typo was a v0.1.x scaffold mistake — never spec-correct in any MCP draft — and was removed rather than kept as an alias to avoid masking client bugs in the wider ecosystem). (commit 1620b6d)
  • #19 (filed by @etal37): tldr-mcp's initialize response now emits protocolVersion and serverInfo in camelCase per the MCP 2024-11-05 wire spec. Pre-fix, InitializeResult serialized snake_case (protocol_version, server_info) which Claude Code and other spec-compliant clients reject during the lifecycle handshake — the user-facing failure was "Claude Code cannot connect to tldr-mcp". A recursive scan of the day-one handshake responses (initialize + tools/list) now returns zero snake_case keys outside JSON Schema property declarations under inputSchema.properties (which are user-defined argument names extracted by tool handlers, not MCP-defined wire fields). (commit 2726358)
  • #5 (security, broader handler audit): Audit of crates/tldr-daemon/src/handlers/{ast,flow,quality}.rs found 7 additional unguarded path arguments using the same is_absolute → accept pattern as the original #5 fix. Each was wired through tldr_core::validate_file_path with BAD_REQUEST mapping. Affected handlers: imports, cfg, dfg, slice, complexity, smells, maintainability. Reproduction tests in crates/tldr-daemon/tests/handler_path_traversal_audit_test.rs confirm canary file content no longer leaks (canary substring canary_xyz_42 previously appeared in ImportInfo.module, CfgInfo.function, DfgInfo.function, ComplexityMetrics.function response fields). (commit b988c42)
  • #19 (broader request-side audit): Beyond the response-side InitializeResult rename, audit of crates/tldr-mcp/src/protocol.rs found InitializeParams was silently dropping protocolVersion and clientInfo from spec-compliant client requests because the request-side struct lacked #[serde(rename_all = "camelCase")]. With #[serde(default)] on every field, missing-key errors degraded to None — Claude Code's day-1 handshake completed but the server's announced-protocol-version and client-info diagnostics became dead code. Now applied via struct-level rename_all attribute (future-proof against new fields silently regressing). (commit 4204616)

Notes

  • cargo install tldr-cli and cargo install tldr-cli --features semantic continue to work as in v0.2.0 — no new install-time requirements.
  • The 4 binary targets (aarch64-apple-darwin, x86_64-apple-darwin, x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu) are built automatically by cargo-dist via .github/workflows/release.yml on the v0.2.1 tag.
  • The original triage produced 4 fix milestones (M1–M4); the broader audit milestones M6 + M7 were dispatched after M5's release prep when latent flags surfaced during M1's and M4's work were promoted into the v0.2.1 scope rather than deferred to v0.2.2. Total v0.2.1 fixes shipped: 6 (4 originally-triaged + 2 audit-discovered).

Download tldr-cli 0.2.1

File Platform Checksum
tldr-cli-aarch64-apple-darwin.tar.xz Apple Silicon macOS checksum
tldr-cli-x86_64-apple-darwin.tar.xz Intel macOS checksum
tldr-cli-aarch64-unknown-linux-gnu.tar.xz ARM64 Linux checksum
tldr-cli-x86_64-unknown-linux-gnu.tar.xz x64 Linux checksum