Skip to content

fix: tighten antigravity RPC trust boundary, codebuff parser edges, and client filter UX#467

Merged
junhoyeo merged 5 commits intomainfrom
fix/pre-release-hardening
Apr 26, 2026
Merged

fix: tighten antigravity RPC trust boundary, codebuff parser edges, and client filter UX#467
junhoyeo merged 5 commits intomainfrom
fix/pre-release-hardening

Conversation

@junhoyeo
Copy link
Copy Markdown
Owner

@junhoyeo junhoyeo commented Apr 26, 2026

Pre-release correctness and security fixes across the four PRs landed this cycle (#464, #454, #359, #355). All issues surfaced by post-merge review.

Antigravity (fix(antigravity): ...)

Trust boundary around the local language-server RPC was too loose:

  • Bound RPC body sizes at 16 MiB (Content-Length, chunked, and read-to-end paths). A malicious or compromised same-user local responder could otherwise OOM tokscale or fill the disk via unbounded chunked encoding.
  • Verify process identity by checking the actual executable path (`lsof` on macOS, `/proc//exe` on Linux) on top of the existing CLI-substring heuristic, so other same-user processes can't impersonate the language server just by having the right argv shape.
  • Probe candidate endpoints with a real RPC call and JSON-shape check (probe body capped at 4 KiB) instead of trusting any 200 response, so a rogue listener can't poison the sync cache.
  • Lock concurrent syncs on a per-cache PID lock file with stale-pid recovery so two `tokscale antigravity sync` runs don't race on the manifest or delete artifacts the other run just produced.
  • Enforce manifest version on load: future versions abort, older versions start fresh.
  • Recover corrupted manifests by moving them aside as `manifest.json.corrupt-` instead of failing every subsequent sync until the user intervenes.

Codebuff (fix(codebuff): ...)

Parser correctness around silent data loss:

  • Accumulate run-state usage across the full reverse `messageHistory` walk instead of returning on the first signal-bearing entry. Previously a newest assistant entry carrying only a model id (no usage) would short-circuit the walk and silently drop real token counts on earlier entries.
  • Include the source-array ordinal in the fallback dedup key so two id-less assistant messages with identical session/timestamp/model/tokens no longer collapse into a single record.
  • Reject non-positive numeric timestamps in the shared `parse_timestamp_value`/`parse_timestamp_str` helpers so messages with `timestamp: 0` or negative epochs fall through to the chat-id / file-mtime fallback chain instead of being pinned at the unix epoch.

TUI + parsing (fix(tui): ...)

User-visible client-filter UX:

  • Move `SYNTHETIC_HOTKEY` from `'x'` to `'n'`. `'x'` collided with Mux's hotkey, and the dispatch order made the displayed `[x]` for Synthetic purely cosmetic — pressing it always toggled Mux. Documented the cross-file invariant so future client additions don't reintroduce the collision.
  • Enable `ignore_case` on `--client/-c` so `OPENCODE`, `Codebuff`, and `antigravity` all parse as the same canonical filter. Added end-to-end clap tests for uppercase, mixed-case, unknown values, empty strings, and duplicated legacy bool flags so the parser surface has explicit coverage.

Test results

  • `cargo test -p tokscale-cli` — 415 unit + 83 integration, all pass
  • `cargo test -p tokscale-core` — 566 unit + 10 codebuff + 3 hermes, all pass
  • `cargo check --workspace` — clean

Each fix has a regression test where applicable.

Commits

  • `1cbdd05 fix(antigravity): cap RPC bodies, verify process identity, lock concurrent syncs, recover corrupted manifest`
  • `680c1da fix(codebuff): accumulate run-state usage across history entries and harden timestamp/dedup edges`
  • `a1833b4 fix(tui): reassign Synthetic picker hotkey and accept case-insensitive --client values`

Open in Devin Review

Summary by cubic

Hardens the local antigravity RPC boundary, fixes codebuff parsing that could drop usage, and improves the TUI client filter. Lowers local attack surface and prevents silent token loss.

  • Bug Fixes
    • antigravity: Cap RPC bodies at 16 MiB (Content-Length, chunked, read-to-end). Verify process executable path; accept language_server as well as antigravity. Probe endpoints with a real RPC and JSON-shape check with a 4 KiB body cap, consuming headers first. Lock concurrent syncs with a PID lock; only evict when the owner PID is dead and use a bounded (3-attempt) retry. Enforce manifest version; auto-backup corrupted manifests.
    • codebuff: Accumulate usage across the full reverse history so tokens aren’t lost when the newest entry only has a model. Add source ordinal to the fallback dedup key to keep identical id-less messages distinct. Reject non‑positive numeric timestamps.
    • TUI: Move Synthetic hotkey to 'n' to avoid the Mux collision. Make --client/-c case-insensitive with end-to-end parser tests.

Written for commit 512930d. Summary will update on new commits.

…rrent syncs, recover corrupted manifest

- bound RPC body reads (Content-Length, chunked, and read-to-end paths) at
  16 MiB so a malicious or compromised local responder cannot OOM tokscale
  or fill the disk via unbounded chunked encoding
- strengthen Antigravity process identification by checking the actual
  executable path (lsof on macOS, /proc/<pid>/exe on Linux) on top of the
  existing CLI-substring heuristic so other same-user processes can't
  impersonate the language server just by argv shape
- probe candidate endpoints with a real RPC call and JSON-shape check
  (cap probe body at 4 KiB) instead of trusting any 200 response, so a
  rogue local listener can't poison the sync cache
- lock 'antigravity sync' on a per-cache PID lock file with stale-pid
  recovery so two concurrent runs can't race on the manifest or delete
  artifacts the other run just produced
- enforce manifest version on load: future versions abort, older versions
  start fresh, identical version proceeds
- on corrupted manifest, move it aside as manifest.json.corrupt-<ts> and
  start fresh instead of failing every sync until the user intervenes
…harden timestamp/dedup edges

- walk the full reverse messageHistory in extract_usage_from_run_state and
  merge usage signals across entries instead of returning on the first hit;
  this prevents silent token loss when the newest assistant entry only
  carries a model id and an earlier entry holds the actual token counts
- include the source-array ordinal in the fallback dedup key so two
  id-less assistant messages in the same chat with identical timestamp,
  model, and token shape no longer collapse into a single record
- reject non-positive numeric timestamps in parse_timestamp_value and
  parse_timestamp_str so messages with timestamp=0 or negative epochs
  fall through to the chat-id / file-mtime fallback chain instead of
  being pinned at the unix epoch
…e --client values

- move SYNTHETIC_HOTKEY from 'x' (which collided with Mux and silently
  shadowed Synthetic in the picker dispatch) to 'n', a free letter not
  used by any current client. Document the cross-file invariant so future
  client additions don't reintroduce the same collision.
- enable ignore_case on the --client/-c value enum so 'OPENCODE',
  'Codebuff', and 'antigravity' all parse as the same canonical filter.
  Add end-to-end clap tests for uppercase, mixed-case, unknown values,
  empty strings, and duplicated legacy bool flags so the parser surface
  has explicit coverage.
@vercel
Copy link
Copy Markdown
Contributor

vercel Bot commented Apr 26, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
tokscale Ignored Ignored Preview Apr 26, 2026 2:40pm

Request Review

chatgpt-codex-connector[bot]

This comment was marked as resolved.

cubic-dev-ai[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

…nd exe-path heuristic

- only evict the sync lock when its owner PID is provably dead. The
  previous age-based 'stale after 10 minutes' rule could let a second
  sync overlap a long-running first one and corrupt the manifest.
- replace the stale-recovery recursion with a bounded retry loop
  (up to 3 attempts) so a process that keeps recreating the lock can no
  longer trigger an unbounded recursive call stack.
- consume HTTP headers before reading the body in probe_heartbeat so the
  4 KiB probe cap applies only to the JSON body, matching the existing
  identity_probe_request implementation. This prevents valid endpoints
  whose response headers contain '{' from being silently rejected.
- accept exe paths containing 'language_server' as well as 'antigravity'.
  On Linux, /proc/<pid>/exe almost always resolves, but a generic
  language-server binary invoked with '--app_data_dir antigravity' would
  otherwise fail the exe-path check even though is_antigravity_process
  already validated the antigravity affiliation via argv.
cubic-dev-ai[bot]

This comment was marked as resolved.

@junhoyeo junhoyeo merged commit 69f0f29 into main Apr 26, 2026
16 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