diff --git a/packages/e2b/subprocess-e2b/src/environment.ts b/packages/e2b/subprocess-e2b/src/environment.ts new file mode 100644 index 0000000000..cc9b41296a --- /dev/null +++ b/packages/e2b/subprocess-e2b/src/environment.ts @@ -0,0 +1,21 @@ +/** Shared remote-environment scrubbing for E2B process and terminal launchers. */ + +import { SENSITIVE_ENV_PATTERN } from '@deepseek-ai/dsh-subprocess' + +/** + * Parse an E2B NUL-delimited environment while removing harness-private and credential-shaped names. + * @param raw - The complete NUL-delimited remote environment. + * @returns Mutable retained entries for the caller to overlay and serialize. + */ +export function scrubRemoteEnvironment(raw: string): Map { + const environment = new Map() + for (const entry of raw.split('\0')) { + if (entry.length === 0) continue + const separator = entry.indexOf('=') + if (separator <= 0) continue + const name = entry.slice(0, separator) + if (name.startsWith('DSH_') || SENSITIVE_ENV_PATTERN.test(name)) continue + environment.set(name, entry.slice(separator + 1)) + } + return environment +} diff --git a/packages/e2b/subprocess-e2b/src/process.ts b/packages/e2b/subprocess-e2b/src/process.ts index 9eeee5d877..5f8a107cf6 100644 --- a/packages/e2b/subprocess-e2b/src/process.ts +++ b/packages/e2b/subprocess-e2b/src/process.ts @@ -8,7 +8,6 @@ import { quoteE2BShellArg, } from '@deepseek-ai/dsh-e2b' import type { CommandHandle, CommandResult, Sandbox } from '@deepseek-ai/dsh-e2b' -import { SENSITIVE_ENV_PATTERN } from '@deepseek-ai/dsh-subprocess' import type { SubprocessCollect, SubprocessHandle, @@ -17,6 +16,7 @@ import type { SubprocessSpawnSpec, } from '@deepseek-ai/dsh-subprocess' import type E2BSandboxService from '@deepseek-ai/dsh-e2b' +import { scrubRemoteEnvironment } from './environment.ts' import { E2BBase64Decoder, E2B_OUTPUT_COMPLETE_FRAME, E2BOutputReader } from './output.ts' const GROUP_POLL_MS = 20 @@ -74,15 +74,7 @@ interface RemotePaths { } function remoteEnvironment(raw: string, explicit: Readonly> | undefined): string { - const environment = new Map() - for (const entry of raw.split('\0')) { - if (entry.length === 0) continue - const separator = entry.indexOf('=') - if (separator <= 0) continue - const name = entry.slice(0, separator) - if (name.startsWith('DSH_') || SENSITIVE_ENV_PATTERN.test(name)) continue - environment.set(name, entry.slice(separator + 1)) - } + const environment = scrubRemoteEnvironment(raw) for (const [name, value] of Object.entries(explicit ?? {})) environment.set(name, value) return [...environment].map(([name, value]) => `${name}=${value}\0`).join('') } diff --git a/packages/e2b/subprocess-e2b/src/terminal.ts b/packages/e2b/subprocess-e2b/src/terminal.ts index 565e406b97..7185899f79 100644 --- a/packages/e2b/subprocess-e2b/src/terminal.ts +++ b/packages/e2b/subprocess-e2b/src/terminal.ts @@ -9,7 +9,7 @@ import { quoteE2BShellArg, } from '@deepseek-ai/dsh-e2b' import type { CommandHandle, CommandResult, Sandbox } from '@deepseek-ai/dsh-e2b' -import { SENSITIVE_ENV_PATTERN, SubprocessTerminalLifecycle } from '@deepseek-ai/dsh-subprocess' +import { SubprocessTerminalLifecycle } from '@deepseek-ai/dsh-subprocess' import type { SubprocessOutcome, SubprocessTerminalForeground, @@ -18,6 +18,7 @@ import type { SubprocessTerminalSpawnSpec, } from '@deepseek-ai/dsh-subprocess' import type E2BSandboxService from '@deepseek-ai/dsh-e2b' +import { scrubRemoteEnvironment } from './environment.ts' const POLL_MS = 20 @@ -71,15 +72,7 @@ function serializeValues(values: readonly string[], kind: string): string { } function remoteEnvironment(raw: string, explicit: Readonly> | undefined): string { - const environment = new Map() - for (const entry of raw.split('\0')) { - if (entry.length === 0) continue - const separator = entry.indexOf('=') - if (separator <= 0) continue - const name = entry.slice(0, separator) - if (name.startsWith('DSH_') || SENSITIVE_ENV_PATTERN.test(name)) continue - environment.set(name, entry.slice(separator + 1)) - } + const environment = scrubRemoteEnvironment(raw) for (const [name, value] of Object.entries(explicit ?? {})) { if (name.length === 0 || name.includes('=') || name.includes('\0') || value.includes('\0')) { throw new Error('subprocess-e2b: terminal environment entries require non-empty NUL-free names without = and NUL-free values')