Skip to content

Fix major correctness, security, and data-loss issues in core + agentic crates#85

Merged
davidrhodus merged 3 commits into
mainfrom
fix/core-agentic-review
Jul 11, 2026
Merged

Fix major correctness, security, and data-loss issues in core + agentic crates#85
davidrhodus merged 3 commits into
mainfrom
fix/core-agentic-review

Conversation

@davidrhodus

Copy link
Copy Markdown
Contributor

Two-pass review of the core and agentic-coding crates (hi-agent, hi-tools, hi-ai, hi-cli, hi-tui, hi-lsp); the hi-mlx/hi-cuda local-inference crates were out of scope. Every finding was verified against the code before fixing. All core-crate tests pass and clippy is clean under -D warnings; regression tests were added for the highest-impact fixes.

Pass 1 — initial fixes (d895a5b)

hi-tools (security)

  • Reject IPv4-mapped IPv6 ([::ffff:169.254.169.254]) in the SSRF blocklist.
  • Resolve symlinks in every existing ancestor so a workspace symlink through a not-yet-existing dir can't escape the path guard.
  • Block rm -r of home/root/system paths even without -f.
  • Cap web_fetch body reads (8 MiB) instead of buffering the whole response.

hi-agent (correctness / data loss)

  • Read-only turns no longer treat the 12k soft elision threshold as the real context window (which durably discarded whole sessions); destructive drops use the real model window.
  • Clamp the persist cursor so nudge-stripping can't panic persist() with an out-of-bounds slice.
  • Enforce ReadOnly/ChatOnly tool_mode at execution time so text-promoted tool calls can't bypass it (explore subagents included).
  • Don't abort the explore subagent before its first model call on common verbs.
  • PID-scope the memory temp file so concurrent writers can't tear it.

hi-cli

  • Skip the first-run wizard (which blind-overwrites config) when profiles exist.
  • Don't apply a delegate child's partial diff on a non-zero exit.
  • Require a name for /provider remove; track active profile on /provider switch.
  • Keep 0600 perms in the api-key migration and don't materialize env secrets to disk.
  • Exclude fleet/loop sessions from hi -c.

hi-ai / hi-lsp / hi-tui

  • Match LSP responses only on method-less messages (request-id collision).
  • Bound the OpenAI post-finish usage grace with a total deadline (was per-chunk → could hang).
  • Keep a finished Anthropic answer on an unclean mid-stream close instead of re-billing.
  • Preserve cache tokens / reference-call usage in fallback and moa; saturating token math.
  • Reclaim the loop single-firer lock atomically to avoid two holders double-firing.

Pass 2 — adversarial audit of pass 1 + deeper review (1a76c5e)

Regressions introduced by pass 1 (caught by an adversarial re-review)

  • fallback.rs: the usage merge dropped the winner's context_occupancy / input_includes_cache, mis-counting cache tokens and tripping early auto-compaction — now preserves the winner's scalars.
  • guard.rs: dropping the -f requirement over-blocked routine deep cleanup (rm -r ~/.cache/x). Tiered it — top-level roots (rm -r ~, /etc) block on -r alone; deeper paths only with -f.

New findings

  • checkpoint.rs: /undo silently skipped non-ASCII filenames (git octal-quotes --name-status paths) — switched to NUL-delimited -z output. Restores the "anything is undoable" guarantee.
  • hi-agent side-call errors (skeptic/curate/memory/plan/finalize/summarize) clobbered the main context_used gauge, disabling the next auto-compaction — added add_side_error_usage.
  • tool_deps didn't serialize a write after an earlier read of the same path ([read X, write X] could read a torn file).
  • hi-tui event_log grew unbounded (one entry per streamed chunk, never trimmed) — capped and cleared on /clear.
  • loops: firings bumped at spawn, so a failed first firing reported "nothing new" against a baseline it never established — roll back on error.
  • best-of/delegate/fleet children re-resolved config and a default-profile literal api_key shadowed the parent's key (silent auth failure) — pass HI_FORCE_API_KEY (env, not argv).
  • provider_form: /provider edit of an Ollama profile opened focused on the hidden API-key field.

Deliberately deferred (low-severity or out of scope)

  • Background-kill nanosecond-window PGID-recycle race and the loop-lock 3-process race (the real 2-process TUI+daemon threat is handled).
  • Anthropic malformed-SSE frames are silently skipped (defensible resilience; hard-erroring risks failing turns on benign vendor frames).
  • Per-profile migration persist, child config-flag forwarding (--compat/--tool-mode), and the hi-tui O(transcript)-per-token render cost — all worth follow-ups.

Testing

cargo test and cargo clippy --all-targets -- -D warnings pass across hi-agent, hi-ai, hi-tools, hi (hi-cli), hi-tui, hi-lsp.

🤖 Generated with Claude Code

davidrhodus and others added 3 commits July 10, 2026 15:57
…ic crates

Reviewed hi-agent, hi-tools, hi-ai, hi-cli, hi-tui, and hi-lsp and fixed the
confirmed major issues (mlx/cuda excluded). All tests pass and clippy is clean
under -D warnings; regression tests added for the highest-impact fixes.

hi-tools (security):
- Reject IPv4-mapped IPv6 (`[::ffff:169.254.169.254]`) in the SSRF blocklist.
- Resolve symlinks in every existing ancestor so a workspace symlink through a
  not-yet-existing dir can't escape the path guard.
- Block `rm -r` of home/root/system paths even without `-f`.
- Cap web_fetch body reads (8 MiB) instead of buffering the whole response.

hi-agent (correctness / data loss):
- Read-only turns no longer treat the 12k soft elision threshold as the real
  context window, which durably discarded whole sessions; destructive drops now
  use the real model window.
- Clamp the persist cursor so nudge-stripping can't panic persist() with an
  out-of-bounds slice.
- Enforce ReadOnly/ChatOnly tool_mode at execution time so text-promoted tool
  calls can't bypass it (explore subagents included).
- Don't abort the explore subagent before its first model call on common verbs.
- PID-scope the memory temp file so concurrent writers can't tear it.

hi-cli:
- Skip the first-run wizard (which blind-overwrites config) when profiles exist.
- Don't apply a delegate child's partial diff on a non-zero exit.
- Require a name for `/provider remove`; track active profile on `/provider` switch.
- Keep 0600 perms in the api-key migration and don't materialize env secrets to disk.
- Exclude fleet/loop sessions from `hi -c`.

hi-ai / hi-lsp / hi-tui:
- Match LSP responses only on method-less messages (request-id collision).
- Bound the OpenAI post-finish usage grace with a total deadline (was per-chunk).
- Keep a finished Anthropic answer on an unclean mid-stream close instead of re-billing.
- Preserve cache tokens / reference-call usage in fallback and moa; saturating token math.
- Reclaim the loop single-firer lock atomically to avoid two holders double-firing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adversarially audited the pass-1 fixes and ran deeper reviewers over the
scheduler, side-calls, hi-tui loops/app, and hi-tools plumbing. Fixed the
confirmed issues (mlx/cuda excluded); all tests pass, clippy clean under
-D warnings.

Regressions introduced by pass-1 fixes:
- fallback.rs: the usage merge dropped the winner's context_occupancy /
  input_includes_cache (Usage::add doesn't carry them), mis-counting cache
  tokens and tripping early auto-compaction. Preserve the winner's scalars.
- guard.rs: dropping the -f requirement over-blocked routine deep cleanup
  (`rm -r ~/.cache/x`). Tier it: top-level roots (`rm -r ~`, `/etc`) block on
  -r alone; deeper paths only with -f (restoring prior protection).

New findings:
- checkpoint.rs: /undo silently skipped non-ASCII filenames (git octal-quotes
  --name-status paths); use NUL-delimited -z output. +test.
- hi-agent side-call errors (skeptic/curate/memory/plan/finalize/summarize)
  clobbered the main context_used gauge via add_error_usage, disabling the next
  auto-compaction. Add add_side_error_usage that leaves the gauge alone.
- tool_deps didn't serialize a write after an earlier read of the same path, so
  `[read X, write X]` ran concurrently and the read could see a torn file. +test.
- hi-tui event_log grew unbounded (one entry per streamed chunk, never trimmed);
  cap it and clear it on /clear.
- loops: firings was bumped at spawn, so a failed first firing left the loop
  reporting "nothing new" against a baseline it never established. Roll back on
  error.
- best-of/delegate/fleet children re-resolved config and a default-profile
  literal api_key shadowed the parent's key (silent auth failure with
  --profile/--api-key). Pass HI_FORCE_API_KEY (env, not argv) so the parent's
  key wins.
- provider_form: /provider edit of an Ollama profile opened focused on the
  hidden API-key field; skip_hidden() in the constructor.

Noted but not changed (low-severity / out of scope): background.rs kill
nanosecond-window race, loop lock 3-process race, per-profile migration persist,
child config-flag forwarding, and the hi-tui O(transcript)-per-token render cost.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The blocking CI job (`cargo fmt --all --check`) rejected the hand-formatted
multi-line boolean/method-chain expressions from the two fix commits. No logic
change — pure `cargo fmt` normalization.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@davidrhodus davidrhodus merged commit 2db5d8e into main Jul 11, 2026
1 of 3 checks passed
@davidrhodus davidrhodus deleted the fix/core-agentic-review branch July 11, 2026 00:22
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