refactor(e2b): share environment scrubbing
This commit is contained in:
21
packages/e2b/subprocess-e2b/src/environment.ts
Normal file
21
packages/e2b/subprocess-e2b/src/environment.ts
Normal file
@@ -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<string, string> {
|
||||||
|
const environment = new Map<string, string>()
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
quoteE2BShellArg,
|
quoteE2BShellArg,
|
||||||
} from '@deepseek-ai/dsh-e2b'
|
} from '@deepseek-ai/dsh-e2b'
|
||||||
import type { CommandHandle, CommandResult, Sandbox } 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 {
|
import type {
|
||||||
SubprocessCollect,
|
SubprocessCollect,
|
||||||
SubprocessHandle,
|
SubprocessHandle,
|
||||||
@@ -17,6 +16,7 @@ import type {
|
|||||||
SubprocessSpawnSpec,
|
SubprocessSpawnSpec,
|
||||||
} from '@deepseek-ai/dsh-subprocess'
|
} from '@deepseek-ai/dsh-subprocess'
|
||||||
import type E2BSandboxService from '@deepseek-ai/dsh-e2b'
|
import type E2BSandboxService from '@deepseek-ai/dsh-e2b'
|
||||||
|
import { scrubRemoteEnvironment } from './environment.ts'
|
||||||
import { E2BBase64Decoder, E2B_OUTPUT_COMPLETE_FRAME, E2BOutputReader } from './output.ts'
|
import { E2BBase64Decoder, E2B_OUTPUT_COMPLETE_FRAME, E2BOutputReader } from './output.ts'
|
||||||
|
|
||||||
const GROUP_POLL_MS = 20
|
const GROUP_POLL_MS = 20
|
||||||
@@ -74,15 +74,7 @@ interface RemotePaths {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function remoteEnvironment(raw: string, explicit: Readonly<Record<string, string>> | undefined): string {
|
function remoteEnvironment(raw: string, explicit: Readonly<Record<string, string>> | undefined): string {
|
||||||
const environment = new Map<string, string>()
|
const environment = scrubRemoteEnvironment(raw)
|
||||||
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))
|
|
||||||
}
|
|
||||||
for (const [name, value] of Object.entries(explicit ?? {})) environment.set(name, value)
|
for (const [name, value] of Object.entries(explicit ?? {})) environment.set(name, value)
|
||||||
return [...environment].map(([name, value]) => `${name}=${value}\0`).join('')
|
return [...environment].map(([name, value]) => `${name}=${value}\0`).join('')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
quoteE2BShellArg,
|
quoteE2BShellArg,
|
||||||
} from '@deepseek-ai/dsh-e2b'
|
} from '@deepseek-ai/dsh-e2b'
|
||||||
import type { CommandHandle, CommandResult, Sandbox } 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 {
|
import type {
|
||||||
SubprocessOutcome,
|
SubprocessOutcome,
|
||||||
SubprocessTerminalForeground,
|
SubprocessTerminalForeground,
|
||||||
@@ -18,6 +18,7 @@ import type {
|
|||||||
SubprocessTerminalSpawnSpec,
|
SubprocessTerminalSpawnSpec,
|
||||||
} from '@deepseek-ai/dsh-subprocess'
|
} from '@deepseek-ai/dsh-subprocess'
|
||||||
import type E2BSandboxService from '@deepseek-ai/dsh-e2b'
|
import type E2BSandboxService from '@deepseek-ai/dsh-e2b'
|
||||||
|
import { scrubRemoteEnvironment } from './environment.ts'
|
||||||
|
|
||||||
const POLL_MS = 20
|
const POLL_MS = 20
|
||||||
|
|
||||||
@@ -71,15 +72,7 @@ function serializeValues(values: readonly string[], kind: string): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function remoteEnvironment(raw: string, explicit: Readonly<Record<string, string>> | undefined): string {
|
function remoteEnvironment(raw: string, explicit: Readonly<Record<string, string>> | undefined): string {
|
||||||
const environment = new Map<string, string>()
|
const environment = scrubRemoteEnvironment(raw)
|
||||||
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))
|
|
||||||
}
|
|
||||||
for (const [name, value] of Object.entries(explicit ?? {})) {
|
for (const [name, value] of Object.entries(explicit ?? {})) {
|
||||||
if (name.length === 0 || name.includes('=') || name.includes('\0') || value.includes('\0')) {
|
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')
|
throw new Error('subprocess-e2b: terminal environment entries require non-empty NUL-free names without = and NUL-free values')
|
||||||
|
|||||||
Reference in New Issue
Block a user