From dec2cfdc78f92e653bde2975c724ac4e273cf68a Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:49:32 -0700 Subject: [PATCH] fix(agent-status): validate the emitter claim and scrub its env from one list Three boundary defects found in review. `ingestRemote` took `reportedExecutionBinding` straight off the envelope while re-validating every sibling field, and the WSL relay reaches that function with a raw wire cast. A malformed value could not mint identity, but it was truthy and non-matching, which is enough to affect how the report is treated. It is parsed now, at the boundary that already exists for this purpose. The daemon kept its own copy of the pane-identity scrub list and did not gain the two new variables, so a pane that did not request them could inherit them from the daemon's environment while the local provider correctly scrubbed them. Both spawn paths now read one shared list, which is what stops the next key from being added to one and not the other. Also merges a duplicate import in the new promotion module that the focused code-quality gate rejects. --- .../agent-hooks/server/server-ingest-remote.ts | 6 +++++- .../daemon/pty-subprocess/spawn-environment.ts | 7 +------ src/main/providers/local-pty-launch-helpers.ts | 10 +--------- src/shared/claimed-agent-pty-owner-promotion.ts | 2 +- src/shared/pane-identity-env.ts | 15 +++++++++++++++ 5 files changed, 23 insertions(+), 17 deletions(-) create mode 100644 src/shared/pane-identity-env.ts diff --git a/src/main/agent-hooks/server/server-ingest-remote.ts b/src/main/agent-hooks/server/server-ingest-remote.ts index f356d97e58f..c8eefeb1a50 100644 --- a/src/main/agent-hooks/server/server-ingest-remote.ts +++ b/src/main/agent-hooks/server/server-ingest-remote.ts @@ -24,6 +24,7 @@ import { } from '../../../shared/agent-status-legacy-adapter' import { isValidPiProviderSessionOnly } from './server-status-identity' import { AgentHookServerIngestStructured } from './server-ingest-structured' +import { parseAgentStatusReportedExecutionBinding } from '../../../shared/agent-status-execution-binding' export abstract class AgentHookServerIngestRemote extends AgentHookServerIngestStructured { /** Ingest a payload from the relay JSON-RPC channel (not the local HTTP server); connectionId is stamped here. Main is still the SSH trust boundary, so re-run the canonical normalizer before caching. */ @@ -271,7 +272,10 @@ export abstract class AgentHookServerIngestRemote extends AgentHookServerIngestS paneKey, source, launchToken: statusDisposition === 'restart' ? undefined : envelope.launchToken, - reportedExecutionBinding: envelope.reportedExecutionBinding, + // Parsed here, not trusted: every sibling field on this envelope is re-validated because the + // relay crosses a trust boundary, and the WSL relay reaches this with a raw wire cast. + reportedExecutionBinding: + parseAgentStatusReportedExecutionBinding(envelope.reportedExecutionBinding) ?? undefined, tabId, worktreeId, connectionId: trimmedConnectionId, diff --git a/src/main/daemon/pty-subprocess/spawn-environment.ts b/src/main/daemon/pty-subprocess/spawn-environment.ts index cbace940f46..d98d08d35a1 100644 --- a/src/main/daemon/pty-subprocess/spawn-environment.ts +++ b/src/main/daemon/pty-subprocess/spawn-environment.ts @@ -18,13 +18,8 @@ import { } from '../../../shared/windows-environment-expansion' import type { TuiAgent } from '../../../shared/tui-agent' import type { PtySubprocessOptions } from '../pty-subprocess' +import { PANE_IDENTITY_ENV_KEYS } from '../../../shared/pane-identity-env' -const PANE_IDENTITY_ENV_KEYS = [ - 'ORCA_PANE_KEY', - 'ORCA_TAB_ID', - 'ORCA_WORKTREE_ID', - 'ORCA_AGENT_LAUNCH_TOKEN' -] as const const WINDOWS_PATH_ENV_KEY_RE = /^path$/i function composeGuardedDaemonGitConfigEnv( diff --git a/src/main/providers/local-pty-launch-helpers.ts b/src/main/providers/local-pty-launch-helpers.ts index b82e6a32bbb..fdc2ee0b5b4 100644 --- a/src/main/providers/local-pty-launch-helpers.ts +++ b/src/main/providers/local-pty-launch-helpers.ts @@ -4,15 +4,7 @@ import { parseWslPath } from '../wsl' import { resolvePathEnvKey } from '../pty/windows-environment-path' import { expandWindowsEnvironmentVariables } from '../../shared/windows-environment-expansion' import { resolveSafePtyDefaultCwd } from './pty-default-cwd' - -const PANE_IDENTITY_ENV_KEYS = [ - 'ORCA_PANE_KEY', - 'ORCA_TAB_ID', - 'ORCA_WORKTREE_ID', - 'ORCA_AGENT_LAUNCH_TOKEN', - 'ORCA_AGENT_STATUS_RUN_ID', - 'ORCA_AGENT_STATUS_EXECUTION_ID' -] as const +import { PANE_IDENTITY_ENV_KEYS } from '../../shared/pane-identity-env' export function getDefaultCwd(): string { return resolveSafePtyDefaultCwd() diff --git a/src/shared/claimed-agent-pty-owner-promotion.ts b/src/shared/claimed-agent-pty-owner-promotion.ts index 09042a24567..4b1b498bd9c 100644 --- a/src/shared/claimed-agent-pty-owner-promotion.ts +++ b/src/shared/claimed-agent-pty-owner-promotion.ts @@ -1,4 +1,3 @@ -import type { AgentSessionClaimedSpawnResult } from './agent-session-host-authority' import type { AgentStatusExecutionBinding } from './agent-status-execution-binding' import { agentSessionSurfacesEqual, @@ -10,6 +9,7 @@ import { type LiveAgentSessionOwner } from './claimed-agent-pty-owner-snapshot' import type { + AgentSessionClaimedSpawnResult, AgentSessionExecutionClaim, AgentSessionSurfaceBinding } from './agent-session-host-authority' diff --git a/src/shared/pane-identity-env.ts b/src/shared/pane-identity-env.ts new file mode 100644 index 00000000000..237a7f91ddc --- /dev/null +++ b/src/shared/pane-identity-env.ts @@ -0,0 +1,15 @@ +/** + * Environment variables that name which pane and which launched execution a PTY belongs to. + * + * A pane that did not request these must not inherit them from the spawning process, so every + * spawn path scrubs the same list. It lives here because the local provider and the daemon each + * scrub independently, and a key added to one list and not the other stops being scrubbed at all. + */ +export const PANE_IDENTITY_ENV_KEYS = [ + 'ORCA_PANE_KEY', + 'ORCA_TAB_ID', + 'ORCA_WORKTREE_ID', + 'ORCA_AGENT_LAUNCH_TOKEN', + 'ORCA_AGENT_STATUS_RUN_ID', + 'ORCA_AGENT_STATUS_EXECUTION_ID' +] as const