fix(dashboard): isolate the environment of named-profile actions - #107434
BowmanStephen wants to merge 1 commit into
Conversation
_spawn_hermes_action copied the dashboard's os.environ verbatim into every detached `hermes ...` action. The dashboard runs inside the gateway and has loaded its own profile's .env into the process environment, so `hermes -p <other> gateway restart` (and every other dashboard-driven profile action) started with the DEFAULT profile's platform credentials and ports already present. load_hermes_dotenv does not override keys that are already set, so the named profile's own .env could not displace them: an A2A-only profile ended up claiming the default Discord bot token and binding the default API server / BlueBubbles ports. For actions carrying a profile selector (`-p X`, `--profile X`, `--profile=X`), build the child env from the standard scrubbed subprocess environment, drop _PROFILE_MANAGED_ENV_KEYS plus every key defined by the dashboard/default profile's .env and its hydrated secret sources, and pin HERMES_HOME to the target profile so the child's normal startup loads that profile's .env. Only the leading selector is inspected: argv after the subcommand may legitimately contain -p for a nested process. Actions without a selector keep the historical environment byte-for-byte. Test: the new case asserts the leaked keys are gone, benign keys survive, and (by running the real dotenv loader in a fresh interpreter with the captured env) that the target profile's values load without reviving any default-profile value. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Status note: #108440 (53e32d0) landed the in-process side of this bug class (YAML→env bridges, TERMINAL_* latch, write-guard memo, env_passthrough allowlist) but deliberately did not fold this PR in — the child-process env for dashboard |
|
Landed on |
Summary
_spawn_hermes_actioncopies the dashboard'sos.environverbatim into every detachedhermes ...action. The dashboard runs inside the gateway and has loaded its own profile's.envinto the process environment, sohermes -p <other> gateway restart(and every other dashboard-driven profile action) starts with the default profile's platform credentials and ports already present.load_hermes_dotenvdoes not override keys that are already set, so the named profile's own.envcannot displace them. Observed result: an A2A-only profile claimed the default Discord bot token and bound the default API server / BlueBubbles ports.Change
For actions carrying a leading profile selector (
-p X,--profile X,--profile=X):build_subprocess_env(base=os.environ, scrub_secrets=True);_PROFILE_MANAGED_ENV_KEYSplus every key defined by the dashboard / default profile.envfiles and their hydrated secret sources (get_secret_source_values);HERMES_HOMEto the target profile (_resolve_profile_dir, so the same validation applies) and runapply_subprocess_home_env; the child's normal startup then loads that profile's.env.Only the leading selector is inspected: argv after the subcommand may legitimately contain
-pfor a nested process (mcp add --args ...). Actions without a selector keep the historical environment byte-for-byte._HERMES_GATEWAYis still dropped (#52470).Tests
test_named_profile_action_isolates_parent_env_and_loads_target_envasserts the leaked keys are gone, benign keys survive,HERMES_HOMEis pinned, and (by running the real dotenv loader in a fresh interpreter with the captured env) that the target profile's values load without reviving any default-profile value. The existing loop-guard test additionally checks a default-profile action still inherits the process env.tests/hermes_cli/test_dashboard_admin_endpoints.py: 43 passed.🤖 Generated with Claude Code