From d0cbf825a592537f5a861767b7ad4170e95e20ff Mon Sep 17 00:00:00 2001 From: ZiyaZhang <199893125+ZiyaZhang@users.noreply.github.com> Date: Tue, 28 Jul 2026 02:19:18 -0700 Subject: [PATCH] fix(subprocess): share password scrub with direct spawners --- .../src/package-managers/package-manager.ts | 4 +-- packages/sdk/helper/tests/documents.spec.ts | 7 ++++- packages/ui/tui/package.json | 2 ++ packages/ui/tui/src/chat/helpers.ts | 6 ++-- packages/ui/tui/tests/chat-helpers.spec.ts | 29 +++++++++++++++++++ packages/ui/tui/tsconfig.json | 3 ++ pnpm-lock.yaml | 3 ++ 7 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 packages/ui/tui/tests/chat-helpers.spec.ts diff --git a/packages/sdk/helper/src/package-managers/package-manager.ts b/packages/sdk/helper/src/package-managers/package-manager.ts index 3d67d194d3..5c070afce7 100644 --- a/packages/sdk/helper/src/package-managers/package-manager.ts +++ b/packages/sdk/helper/src/package-managers/package-manager.ts @@ -5,7 +5,7 @@ */ import { execFile, spawn } from 'node:child_process' -import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess' +import { scrubbedParentEnv, SENSITIVE_ENV_PATTERN } from '@deepseek-ai/dsh-subprocess' import { promisify } from 'node:util' import type { PackageJsonFile } from '../documents/package-json-file.ts' import { PnpmWorkspaceFile } from '../documents/pnpm-workspace-file.ts' @@ -60,7 +60,7 @@ export async function probePackageManagerVersion(name: PackageManagerName, cwd: */ export function scrubEnvironment(environment?: NodeJS.ProcessEnv): NodeJS.ProcessEnv { if (environment === undefined) return scrubbedParentEnv() - return Object.fromEntries(Object.entries(environment).filter(([name]) => !/(?:KEY|SECRET|TOKEN)/i.test(name))) + return Object.fromEntries(Object.entries(environment).filter(([name]) => !SENSITIVE_ENV_PATTERN.test(name))) } /** Node child-process command runner with inherited stdio and quiescent completion. */ diff --git a/packages/sdk/helper/tests/documents.spec.ts b/packages/sdk/helper/tests/documents.spec.ts index 1e4b944f00..6ef18b12a2 100644 --- a/packages/sdk/helper/tests/documents.spec.ts +++ b/packages/sdk/helper/tests/documents.spec.ts @@ -309,7 +309,12 @@ describe('package manager strategies', () => { await expect(npm.install('/tmp', failed)).rejects.toThrow('exited with code 2') const killed: CommandRunner = { run: async () => ({ exitCode: null, signal: 'SIGTERM' }) } await expect(npm.build('/tmp', killed)).rejects.toThrow('killed by SIGTERM') - expect(scrubEnvironment({ PATH: '/bin', API_KEY: 'secret', TOKEN_VALUE: 'secret' })).toEqual({ PATH: '/bin' }) + expect(scrubEnvironment({ + PATH: '/bin', + API_KEY: 'secret', + DB_PASSWORD: 'secret', + TOKEN_VALUE: 'secret', + })).toEqual({ PATH: '/bin' }) }) it('probes versions and runs real child-process boundaries', async () => { diff --git a/packages/ui/tui/package.json b/packages/ui/tui/package.json index f9260e3231..ca12063262 100644 --- a/packages/ui/tui/package.json +++ b/packages/ui/tui/package.json @@ -45,6 +45,7 @@ "@deepseek-ai/dsh-session-reference": "^0.0.1", "@deepseek-ai/dsh-session-title": "^0.0.1", "@deepseek-ai/dsh-skill": "^0.0.1", + "@deepseek-ai/dsh-subprocess": "^0.0.1", "@deepseek-ai/dsh-system-prompt": "^0.0.1", "@deepseek-ai/dsh-token-meter": "^0.0.1", "@deepseek-ai/dsh-tools": "^0.0.1", @@ -82,6 +83,7 @@ "@deepseek-ai/dsh-session-reference": "workspace:^", "@deepseek-ai/dsh-session-title": "workspace:^", "@deepseek-ai/dsh-skill": "workspace:^", + "@deepseek-ai/dsh-subprocess": "workspace:^", "@deepseek-ai/dsh-system-prompt": "workspace:^", "@deepseek-ai/dsh-token-meter": "workspace:^", "@deepseek-ai/dsh-tool-cordis": "workspace:^", diff --git a/packages/ui/tui/src/chat/helpers.ts b/packages/ui/tui/src/chat/helpers.ts index 5721774451..fca9768bf8 100644 --- a/packages/ui/tui/src/chat/helpers.ts +++ b/packages/ui/tui/src/chat/helpers.ts @@ -16,6 +16,7 @@ import { visibleWidth, } from '@earendil-works/pi-tui' import type { Session } from '@deepseek-ai/dsh-session' +import { scrubbedParentEnv } from '@deepseek-ai/dsh-subprocess' /** Editor that shows a placeholder without making it editable content. */ export class HintEditor extends Editor { @@ -65,13 +66,10 @@ export function formatCwd(cwd: string | undefined): string { */ export function gitBranch(cwd: string): string | undefined { try { - const env = Object.fromEntries( - Object.entries(process.env).filter(([name]) => !/(?:KEY|SECRET|TOKEN)/iu.test(name)), - ) const branch = execFileSync('git', ['branch', '--show-current'], { cwd, encoding: 'utf8', - env, + env: scrubbedParentEnv(), stdio: ['ignore', 'pipe', 'ignore'], timeout: 1_000, }).trim() diff --git a/packages/ui/tui/tests/chat-helpers.spec.ts b/packages/ui/tui/tests/chat-helpers.spec.ts new file mode 100644 index 0000000000..17b8c29c0d --- /dev/null +++ b/packages/ui/tui/tests/chat-helpers.spec.ts @@ -0,0 +1,29 @@ +import { execFileSync } from 'node:child_process' +import { afterEach, describe, expect, it, vi } from 'vitest' +import { gitBranch } from '../src/chat/helpers.ts' + +vi.mock('node:child_process', () => ({ + execFileSync: vi.fn(() => 'main\n'), +})) + +afterEach(() => { + vi.unstubAllEnvs() + vi.clearAllMocks() +}) + +describe('chat helpers', () => { + it('scrubs ambient credentials and DSH names from the Git child', () => { + vi.stubEnv('TUI_TEST_PASSWORD', 'ambient-password') + vi.stubEnv('DSH_TUI_TEST_FLAG', 'ambient-harness-state') + expect(gitBranch('/workspace')).toBe('main') + const call = vi.mocked(execFileSync).mock.calls[0] as unknown as [ + string, + string[], + { env: NodeJS.ProcessEnv }, + ] + expect(call[0]).toBe('git') + expect(call[1]).toEqual(['branch', '--show-current']) + expect(call[2].env).not.toHaveProperty('TUI_TEST_PASSWORD') + expect(call[2].env).not.toHaveProperty('DSH_TUI_TEST_FLAG') + }) +}) diff --git a/packages/ui/tui/tsconfig.json b/packages/ui/tui/tsconfig.json index 3560d6bc9d..1cfc20fd88 100644 --- a/packages/ui/tui/tsconfig.json +++ b/packages/ui/tui/tsconfig.json @@ -56,6 +56,9 @@ { "path": "../../skill/skill" }, + { + "path": "../../subprocess/subprocess" + }, { "path": "../user-interaction" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b9538051cd..4325bfb77e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4611,6 +4611,9 @@ importers: '@deepseek-ai/dsh-skill': specifier: workspace:^ version: link:../../skill/skill + '@deepseek-ai/dsh-subprocess': + specifier: workspace:^ + version: link:../../subprocess/subprocess '@deepseek-ai/dsh-system-prompt': specifier: workspace:^ version: link:../../core/system-prompt