From 14b62da72990605f2b75e8e27e2768022dfdfed2 Mon Sep 17 00:00:00 2001 From: Hibryda Date: Sat, 7 Mar 2026 01:15:10 +0100 Subject: [PATCH] docs: update meta files for Rust-side CLAUDE* env var stripping - .claude/CLAUDE.md: document dual-layer env var stripping (Rust + JS) - docs/progress.md: add session entry for Rust-side stripping - docs/task_plan.md: add CLAUDE* env var leak to errors table - CHANGELOG.md: add fix entry under Unreleased - .gitignore: exclude debug/, plugins/, projects/ (Claude Code working dirs) --- .claude/CLAUDE.md | 2 +- .gitignore | 3 +++ CHANGELOG.md | 3 +++ docs/progress.md | 8 ++++++++ docs/task_plan.md | 1 + 5 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 97379da..0403f7a 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -26,7 +26,7 @@ - WebKit2GTK has no WebGL — xterm.js must use Canvas addon explicitly. - Agent sessions use `@anthropic-ai/claude-agent-sdk` query() function (migrated from raw CLI spawning due to piped stdio hang bug). SDK handles subprocess management internally. All output goes through the adapter layer (`src/lib/adapters/sdk-messages.ts`) — SDK message format matches CLI stream-json. -- Sidecar uses a single pre-built bundle (`sidecar/dist/agent-runner.mjs`) that runs on both Deno and Node.js. SidecarCommand struct in sidecar.rs abstracts runtime (Deno preferred for faster startup, Node.js fallback). Communicates with Rust via stdio NDJSON. The sidecar MUST strip ALL `CLAUDE*` prefixed env vars via SDK's `env` option — without this, nesting detection triggers when BTerminal is launched from a Claude Code terminal. Session stop uses AbortController.abort() (not process.kill()). +- Sidecar uses a single pre-built bundle (`sidecar/dist/agent-runner.mjs`) that runs on both Deno and Node.js. SidecarCommand struct in sidecar.rs abstracts runtime (Deno preferred for faster startup, Node.js fallback). Communicates with Rust via stdio NDJSON. CLAUDE* env var stripping is dual-layer: (1) Rust SidecarManager uses env_clear() + envs(clean_env) to strip all CLAUDE* vars before spawning the sidecar process, (2) JS runner also strips via SDK's `env` option (defense-in-depth). Without this, nesting detection triggers when BTerminal is launched from a Claude Code terminal. Session stop uses AbortController.abort() (not process.kill()). - AgentPane does NOT stop agents in onDestroy — onDestroy fires on layout remounts, not just explicit close. Stop-on-close is handled by TilingGrid.svelte's onClose handler (checks pane type + session status before calling stopAgent). - Agent dispatcher (`src/lib/agent-dispatcher.ts`) is a singleton that routes sidecar events to the agent store. Also handles subagent pane spawning (SUBAGENT_TOOL_NAMES detection, toolUseToChildPane routing map). - AgentQueryOptions supports `permission_mode` field (flows Rust -> sidecar -> SDK). Defaults to 'bypassPermissions', supports 'default' mode. allowDangerouslySkipPermissions is conditionally set. diff --git a/.gitignore b/.gitignore index 8d16906..605a389 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ __pycache__/ *.pyo /CLAUDE.md v2/target/ +debug/ +plugins/ +projects/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ccf79f..4bda855 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Fixed +- CLAUDE* env var stripping now applied at Rust level in SidecarManager (bterminal-core/src/sidecar.rs): `env_clear()` + `envs(clean_env)` strips all CLAUDE-prefixed vars before spawning sidecar process, providing primary defense against nesting detection (JS-side stripping retained as defense-in-depth) + ### Changed - Sidecar resolution unified: single pre-built `agent-runner.mjs` bundle replaces separate `agent-runner-deno.ts` + `agent-runner.ts` lookup; same `.mjs` file runs under both Deno and Node.js - `resolve_sidecar_command()` in sidecar.rs now checks deno/node availability upfront before searching paths, improved error message with runtime availability note diff --git a/docs/progress.md b/docs/progress.md index 5df31b8..914757a 100644 --- a/docs/progress.md +++ b/docs/progress.md @@ -401,6 +401,14 @@ Design: No separate sidecar process per subagent. Parent's sidecar handles all; - [x] Error message now includes runtime availability note when neither deno nor node found - [x] agent-runner-deno.ts file retained in repo for reference but no longer used by SidecarManager +### Session: 2026-03-07 (continued) — Rust-Side CLAUDE* Env Var Stripping + +#### Dual-Layer Env Var Stripping +- [x] Added CLAUDE* env var stripping in Rust SidecarManager (bterminal-core/src/sidecar.rs) +- [x] Uses env_clear() + envs(clean_env) on Command to strip all CLAUDE-prefixed vars before spawning sidecar process +- [x] This is the primary defense — ensures no CLAUDE* vars reach the sidecar regardless of JS runtime +- [x] JS-side stripping (agent-runner.ts/agent-runner-deno.ts via SDK `env` option) retained as defense-in-depth + ### Next Steps - [ ] Real-world relay testing (2 machines) - [ ] TLS/certificate pinning for relay connections diff --git a/docs/task_plan.md b/docs/task_plan.md index 4881bcc..17ddb5a 100644 --- a/docs/task_plan.md +++ b/docs/task_plan.md @@ -188,4 +188,5 @@ See [phases.md](phases.md) for the full phased implementation plan. |---|---|---|---| | Blank screen, "rune_outside_svelte" runtime error | Store files used `.ts` extension but contain Svelte 5 `$state`/`$derived` runes. Runes only work in `.svelte` and `.svelte.ts` files. Compiler silently passes but fails at runtime. | Renamed stores to `.svelte.ts`, updated all import paths to use `.svelte` suffix | 2026-03-06 | | Agent sessions produce no output (silent hang) | Claude CLI v2.1.69 hangs when spawned via child_process.spawn() with piped stdio. Known bug: github.com/anthropics/claude-code/issues/6775 | Migrated sidecar from raw CLI spawning to `@anthropic-ai/claude-agent-sdk` query() function. SDK handles subprocess management internally. | 2026-03-06 | +| CLAUDE* env vars leak to sidecar | When BTerminal launched from Claude Code terminal, CLAUDE* env vars trigger nesting detection in sidecar | Dual-layer stripping: Rust SidecarManager uses env_clear()+envs(clean_env) before spawn (primary), JS runner strips via SDK env option (defense-in-depth) | 2026-03-07 | | Running agents killed on pane remount | AgentPane.svelte onDestroy called stopAgent() on component unmount, including layout changes and remounts — not just explicit close. | Removed onDestroy from AgentPane. Moved stop-on-close to TilingGrid onClose handler which only fires on explicit user action. | 2026-03-06 |