Skip to content

fix(security): stop plugin sidecar children inheriting full env - #78033

Open
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:fix/security-plugin-sidecar-env
Open

fix(security): stop plugin sidecar children inheriting full env#78033
andrexibiza wants to merge 2 commits into
NousResearch:mainfrom
andrexibiza:fix/security-plugin-sidecar-env

Conversation

@andrexibiza

@andrexibiza andrexibiza commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Related #77027

What

Five plugin sidecar spawn sites were still copying os.environ raw into child processes, so every child — Playwright's Chromium, the meet_bot subprocess, the brv CLI, the buzz CLI, and the Photon Node sidecar — inherited the gateway's full credential set (BWS vault token, provider API keys, *_PASSWORD vars) even though the child only needs a handful of plugin-owned values.

Each site now builds its child env through the existing sanitized factory build_subprocess_env() from tools.environments.local (the same builder every other spawn surface already uses), then applies the site's own additions on top:

Site File:line (current main) Child Plugin-owned additions kept
meet_bot Chromium env plugins/google_meet/meet_bot.py:531 Playwright Chromium PULSE_SOURCE, DISPLAY, HERMES_MEET_* (via wholesale env replacement at the merge point so scrubbed credentials can't linger)
meet_bot spawn plugins/google_meet/process_manager.py:135 python -m plugins.google_meet.meet_bot HERMES_MEET_URL/OUT_DIR/… incl. the explicitly-passed HERMES_MEET_REALTIME_KEY
byterover CLI plugins/memory/byterover/__init__.py:137 brv CLI PATH prepend of the resolved CLI's bin dir
buzz CLI plugins/platforms/buzz/adapter.py:293 buzz CLI BUZZ_RELAY_URL, BUZZ_PRIVATE_KEY (the plugin's own child-only secret, applied after sanitizing)
Photon sidecar plugins/platforms/photon/adapter.py:1598 Node sidecar PHOTON_PROJECT_ID/SECRET, PHOTON_SIDECAR_PORT/BIND/TOKEN/WATCH_STDIN

The sanitizer is the long-standing scrub owner: the provider env blocklist, the dynamic internal-secret patterns (GATEWAY_RELAY_*_SECRET/KEY/TOKEN, AUXILIARY_*_API_KEY), and HERMES_HOME/subprocess-HOME propagation. build_subprocess_env() already exists on main and is the documented single factory for child envs — no new machinery.

meet_bot detail: chrome_env is merged back into os.environ before Playwright launches (Playwright's launch() takes no env dict). The merge is now a wholesale replacement (_apply_chrome_env) of the sanitized env rather than a key-by-key merge, so scrubbed credentials cannot remain in the env Chromium inherits. The realtime API key is resolved and passed explicitly before this point, and the only later env read (HERMES_MEET_LOBBY_TIMEOUT) survives sanitizing, so the bot flow is unchanged.

These are the plugin-sidecar sinks flagged in the #77027 credential-boundary review (which hardens the sanitizer itself); this PR applies that sanitizer at the residual plugin spawn sites.

How to test

  • python -m pytest tests/plugins/test_google_meet_plugin.py tests/plugins/memory/test_byterover_provider.py tests/gateway/test_buzz_adapter.py tests/plugins/platforms/photon/test_sidecar_lifecycle.py -q
  • Regression tests plant GATEWAY_RELAY_SECRET, EMAIL_PASSWORD, and OPENAI_API_KEY in the parent env, drive each real spawn seam (mocked Popen/create_subprocess_exec/sidecar spawn; real build_subprocess_env()), and assert the child env excludes all three while keeping each plugin's own keys (HERMES_MEET_*, BUZZ_*, PHOTON_*, PATH prepend).
  • Manual: run hermes meet join, a byterover memory query, a buzz send, or hermes photon setup+gateway connect with a GATEWAY_RELAY_SECRET in the gateway env; the spawned child's /proc/<pid>/environ (or Process Explorer) no longer contains the gateway's vault token or DB passwords.

Platforms

  • Windows 11 (native) — all 1286 tests/plugins/ tests plus the touched test files pass; scripts/check-windows-footguns.py clean on all changed files; git diff --check clean.
  • Code paths are cross-platform (the sanitizer is platform-agnostic; the meet_bot PULSE_SOURCE branch is Linux-only by design and covered by the seam tests).
  • Pre-existing, environment-dependent failures unrelated to this change (tests/plugins/… hindsight/image-gen/a2a/kanban/deepinfra tests and tests/tools/test_local_env_blocklist.py on Windows) reproduce identically on clean main.

Why this matters to users

Before this change, every time you joined a Google Meet (Chromium child), asked ByteRover to remember something (brv CLI child), sent a buzz message (CLI child), or ran Photon (Node sidecar), those child processes received a copy of everything in the gateway's environment: the Bitwarden/BWS vault access token, every provider API key, and any *_PASSWORD database/email secrets. A compromised or buggy child (a malicious meet page driving Chromium, a compromised npm dependency in the sidecar, a plugin CLI parsing an odd flag) could read those credentials and exfiltrate the same secrets the gateway itself holds.

After this change, each of those children gets only what its job needs: the meeting/CLI/sidecar configuration, the plugin's own relay key or project secret, and the normal non-secret environment. A credential leak in any of those children is now contained to that child's own keys instead of the whole gateway vault.

Part of #78207

Part of #83565 — plugin sidecar children stop inheriting full env; Wave E, direct sibling of the anchor.

Signed-off-by: Andrex Ibiza, MBA <84248988+andrexibiza@users.noreply.github.com>
@alt-glitch alt-glitch added type/security Security vulnerability or hardening comp/plugins Plugin system and bundled plugins area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 3, 2026
Signed-off-by: andrexibiza <84248988+andrexibiza@users.noreply.github.com>
@egilewski

Copy link
Copy Markdown
Contributor

suggesting changes

The patch sanitizes several direct child launches, but the child-process boundary remains incomplete.

  • [P2] The Playwright driver starts before the sanitized environment is installed. Entering the driver context first lets secrets from the bot environment reach that third-party runtime even though Chromium receives a later sanitized snapshot.
    Remediation: Install the sanitized environment before driver startup (or use a driver-level environment hook), restore it safely if the process is reused, and add a regression that checks the driver-start environment.

  • [P2] Realtime audio helper subprocesses bypass the sanitizer. Realtime setup runs before the sanitized environment is applied, so helper children inherit bot credentials without an explicit child environment.
    Remediation: Build the child environment before audio setup and pass it explicitly to every audio subprocess, including rollback and teardown, while retaining only audio-specific variables; add Linux and macOS coverage.

  • [P2] Photon preparation subprocesses retain the full gateway environment. Dependency installation, listener/process probes, and patch preparation omit the sanitized environment, so those helpers can inherit gateway/provider credentials.
    Remediation: Pass the sanitized child environment to every Photon preparation and sidecar subprocess, and cover the dependency-install path with regression tests.

  • [P2] The plugin realtime key is copied into Chromium's environment. The key is needed by the realtime client but remains in the browser environment snapshot.
    Remediation: Keep HERMES_MEET_REALTIME_KEY only for the realtime client and remove it from the Chromium/audio environment; add coverage that checks both properties.

Security evidence:

  • trust boundary: The gateway and profile environment are trusted sources; detached browser, driver, audio, Photon, Byterover, and Buzz processes are child sinks. Unrelated secrets must not cross those boundaries except explicitly required plugin values.
  • source/sink/invariant: The intended invariant is a sanitized child environment with explicit plugin values; the four residual paths above violate it.
  • current-main reproduction: Before the patch, direct child launches copied the parent environment; unchanged ordering, helper subprocesses, and the plugin-key projection preserve the four residual paths.
  • PR-head or patch-replay validation: The patch sanitizes the covered direct launches, while the four residual paths remain unsanitized.
  • positive/negative cases: Positive checks remove provider, gateway, and password credentials while retaining required plugin values; negative cases are the four residual paths above.
  • residual bypass search: Related child-process paths are sanitized on the changed direct launches but remain outside the sanitizer for the driver startup, audio helpers, Photon preparation, and browser projection.
  • reviewer validation: Source review and targeted sanitizer tests support these four actionable findings.

Targeted Byterover and Google Meet tests pass, and sanitizer checks remove provider, gateway, and password credentials while retaining required plugin values.

Review setup: I reviewed a run-owned local rebase or patch replay against current GitHub main because the submitted branch is stale or conflicted; this does not mean the submitted branch itself merges cleanly.

Not checked:

  • Buzz adapter pytest
  • Photon sidecar pytest
  • Ruff check
  • Live Playwright driver probe

Signed: GPT-5.6-luna-max in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants