From 1107ff5fe13110e1663ce349083162cad2687087 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 3 Sep 2026 16:26:22 +0800 Subject: [PATCH] test: remove remaining dsh-* spill/temp dirs in shell and fs specs Full-template residue histogram on the CI host surfaced four more normal-exit leaks below the earlier cutoff: tool-bash/tool-pwsh tools.spec (module spill dir / per-test homes), bash-sandbox sandbox.spec (module spill dir and the read-only denial root), and fs-sandbox fs-sandbox.spec (inline tmp dir). All four now remove what they create at the same teardown points as their neighbors. --- .../fs/fs-sandbox/tests/fs-sandbox.spec.ts | 11 ++++++--- .../shell/bash-sandbox/tests/sandbox.spec.ts | 23 +++++++++++++------ packages/shell/tool-bash/tests/tools.spec.ts | 8 +++++-- packages/shell/tool-pwsh/tests/tools.spec.ts | 12 ++++++++-- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/packages/fs/fs-sandbox/tests/fs-sandbox.spec.ts b/packages/fs/fs-sandbox/tests/fs-sandbox.spec.ts index 435086a301..95ae891808 100644 --- a/packages/fs/fs-sandbox/tests/fs-sandbox.spec.ts +++ b/packages/fs/fs-sandbox/tests/fs-sandbox.spec.ts @@ -100,9 +100,14 @@ describe('workspace-write containment', () => { }) it('a write to the platform temp area lands (parity with the bash runner grant)', async () => { - const path = join(await mkdtemp(join(tmpdir(), 'dsh-fssbx-tmp-')), 'temp.txt') - await fs.writeText(await target(path), 'temp') - expect(await readFile(path, 'utf8')).toBe('temp') + const dir = await mkdtemp(join(tmpdir(), 'dsh-fssbx-tmp-')) + try { + const path = join(dir, 'temp.txt') + await fs.writeText(await target(path), 'temp') + expect(await readFile(path, 'utf8')).toBe('temp') + } finally { + await rm(dir, { recursive: true, force: true }) + } }) it('an absolute path outside the workspace is denied, no file created', async () => { diff --git a/packages/shell/bash-sandbox/tests/sandbox.spec.ts b/packages/shell/bash-sandbox/tests/sandbox.spec.ts index 4029cc4f40..21d2dfceaa 100644 --- a/packages/shell/bash-sandbox/tests/sandbox.spec.ts +++ b/packages/shell/bash-sandbox/tests/sandbox.spec.ts @@ -8,7 +8,7 @@ import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, resolve } from 'node:path' -import { describe, expect, it, vi } from 'vitest' +import { afterAll, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import type { ShellRunResult, CollectedOutput } from '@deepseek-ai/dsh-shell' import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection' @@ -23,6 +23,10 @@ import type { Config } from '@deepseek-ai/dsh-bash-sandbox' const spillDir = mkdtempSync(join(tmpdir(), 'dsh-bash-sandbox-spec-')) +afterAll(() => { + rmSync(spillDir, { recursive: true, force: true }) +}) + /** One recorded provider call: the argv handed over and the policy it rode with. */ interface ConfineCall { argv: string[] @@ -518,12 +522,17 @@ describe('result facts', () => { it('reports a real permission failure as a sandbox denial with the mode it ran under', async () => { const { bash } = await setup() - const lockedDir = join(mkdtempSync(join(tmpdir(), 'dsh-sandbox-denied-')), 'locked') - mkdirSync(lockedDir) - chmodSync(lockedDir, 0o555) - const result = await bash.run(bash.resolve({ command: `echo x > ${lockedDir}/f` })) - expect(result.exitCode).not.toBe(0) - expect(result.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'full' }) + const deniedRoot = mkdtempSync(join(tmpdir(), 'dsh-sandbox-denied-')) + try { + const lockedDir = join(deniedRoot, 'locked') + mkdirSync(lockedDir) + chmodSync(lockedDir, 0o555) + const result = await bash.run(bash.resolve({ command: `echo x > ${lockedDir}/f` })) + expect(result.exitCode).not.toBe(0) + expect(result.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'full' }) + } finally { + rmSync(deniedRoot, { recursive: true, force: true }) + } }) it('carries the provider\'s partial-enforcement fact through unchanged', async () => { diff --git a/packages/shell/tool-bash/tests/tools.spec.ts b/packages/shell/tool-bash/tests/tools.spec.ts index 747d98b48d..6da7972087 100644 --- a/packages/shell/tool-bash/tests/tools.spec.ts +++ b/packages/shell/tool-bash/tests/tools.spec.ts @@ -1,7 +1,7 @@ -import { mkdtempSync } from 'node:fs' +import { mkdtempSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join } from 'node:path' -import { describe, expect, it, vi } from 'vitest' +import { afterAll, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' import { ToolCallId } from '@deepseek-ai/dsh-llm' import { ShellExecutor } from '@deepseek-ai/dsh-shell' @@ -29,6 +29,10 @@ const testToolSignal = new AbortController().signal const spillDir = mkdtempSync(join(tmpdir(), 'dsh-tool-bash-spec-')) +afterAll(() => { + rmSync(spillDir, { recursive: true, force: true }) +}) + /** Foreground-only harness: no job runtime (backgrounding fails loud here). */ async function setup() { const ctx = new Context() diff --git a/packages/shell/tool-pwsh/tests/tools.spec.ts b/packages/shell/tool-pwsh/tests/tools.spec.ts index 5e27e3c8e0..26194bd9c2 100644 --- a/packages/shell/tool-pwsh/tests/tools.spec.ts +++ b/packages/shell/tool-pwsh/tests/tools.spec.ts @@ -10,9 +10,9 @@ * is pinned separately in integration.spec.ts. */ -import { describe, expect, it, vi } from 'vitest' +import { afterEach, describe, expect, it, vi } from 'vitest' import { Context } from '@deepseek-ai/cordis' -import { mkdtempSync, realpathSync } from 'node:fs' +import { mkdtempSync, realpathSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, resolve as resolvePath } from 'node:path' import { ToolCallId } from '@deepseek-ai/dsh-llm' @@ -38,6 +38,12 @@ import { renderPwshProcessRead, renderPwshResult } from '../src/render.ts' const testToolSignal = new AbortController().signal +/** Per-test temp dirs (session cwd/home fixtures), removed after each test. */ +const tempDirs: string[] = [] +afterEach(() => { + for (const dir of tempDirs.splice(0)) rmSync(dir, { recursive: true, force: true }) +}) + /** * A scriptable fake executor: `resolve()` mirrors the real defaulting, `run()` * returns the armed foreground script, `start()` returns the armed background @@ -388,6 +394,7 @@ describe('argument validation', () => { describe('execution through the bash seam', () => { it('forwards command, session cwd, timeout, and managed DSH_* environment', async () => { const dshHome = mkdtempSync(join(tmpdir(), 'dsh-tool-pwsh-home-')) + tempDirs.push(dshHome) const { ctx, bash } = await setup({}, dshHome) bash.handler = () => runResult('hi\n') const agent = registerFakeAgent(ctx, 'session-1') @@ -538,6 +545,7 @@ describe('per-call sandbox policy resolution', () => { it('stamps the CALLING SESSION\'s resolved policy onto the request (session cwd, not the server launch dir)', async () => { const { ctx, bash } = await setupSandboxed() const sessionCwd = mkdtempSync(join(tmpdir(), 'dsh-tool-pwsh-policy-')) + tempDirs.push(sessionCwd) const agent = registerFakeAgent(ctx, 'policy-session') Object.assign(agent.session.header, { cwd: sessionCwd }) const result = await call(ctx, 'pwsh', { command: 'Write-Output hi', description: 'say hi' }, agent)