Files
deepseek-harness/packages/sandbox/sandbox-windows-acl/tests/runner.spec.ts
Huanqi Cao f64ba40f43 feat(sandbox): Windows ACL write-restriction sandbox (restricted-token runner)
Confine Windows command execution through a WRITE_RESTRICTED token whose restricting SIDs carry an orphan-SID write allowlist, ported from https://github.com/huoyaoyuan/windows-acl-restrict-poc (@ 10e4dfb). Every Win32 call is checked and fails closed - the POC silently ran children with the FULL token when CreateRestrictedToken failed.

- @deepseek-ai/dsh-sandbox-windows-acl: koffi primitives verified against the MinGW Windows headers (verify/abi-probe.cpp) plus the confinement runner ([node, runner, --workspace, --temp, --mode, --, argv...]: kill-on-close job, stdio passthrough, exit-code mirroring, windows-acl-run: failure signature, grant revocation). read-only = strict zero grants (NUL device not writable; documented). Windows-only execution: exempted from the Linux coverage lane (windowsOnlyCoverageExclusions).
- @deepseek-ai/dsh-sandbox-local: PLATFORM_CHAINS.win32 filled with the windows-acl runner (full enforcement, ACL denial dialect, runner-failure rules).
- @deepseek-ai/dsh-pwsh-sandbox: sandbox-consuming pwsh executor (call-for-call mirror of dsh-bash-sandbox) over a new argv-level seam in dsh-pwsh-local; per-file coverage complete via the fake-provider spec.
- bundle/base: the Windows platform layer mounts the confined pwsh roster - sandbox/policy/fs-sandbox/permission/approval re-enabled, the POSIX bash stack stays disabled.

Co-authored-by: Huo Yaoyuan <huoyaoyuan@hotmail.com>
2026-08-08 01:24:33 +08:00

108 lines
5.0 KiB
TypeScript

/**
* End-to-end runner tests: spawn the REAL runner entry through tsx (exactly
* the argv shape dsh-sandbox-local's confine() builds), with piped stdio
* inherited through the runner into the confined child — the same chain a
* production confined execution walks.
*/
import { spawnSync } from 'node:child_process'
import { existsSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
const isWin32 = process.platform === 'win32'
const runnerEntry = fileURLToPath(new URL('../src/runner.ts', import.meta.url))
function pwshAvailable(): boolean {
try {
spawnSync('where.exe', ['pwsh'], { stdio: 'ignore' })
return true
} catch {
return false
}
}
function runRunner(args: string[], timeoutMs = 30_000) {
return spawnSync(process.execPath, ['--import', 'tsx/esm', runnerEntry, ...args], {
timeout: timeoutMs,
encoding: 'utf8',
})
}
describe.skipIf(!isWin32 || !pwshAvailable())('windows-acl runner', () => {
let scratchRoot!: string
let writableDir!: string
let isolatedTemp!: string
let secretFile!: string
let escapeFile!: string
beforeAll(() => {
scratchRoot = mkdtempSync(join(tmpdir(), 'dsh-acl-runner-'))
writableDir = join(scratchRoot, 'writable')
mkdirSync(writableDir)
isolatedTemp = mkdtempSync(join(tmpdir(), 'dsh-acl-runner-temp-'))
secretFile = join(scratchRoot, 'secret.txt')
writeFileSync(secretFile, 'top secret - must stay readable to prove the read boundary')
escapeFile = join(scratchRoot, 'escaped.txt')
})
afterAll(() => {
rmSync(scratchRoot, { recursive: true, force: true })
rmSync(isolatedTemp, { recursive: true, force: true })
})
it('workspace-write: the confined child writes granted directories only', () => {
const probe = [
"$ErrorActionPreference='SilentlyContinue';",
`try{Set-Content -Path '${writableDir}\\child-wrote.txt' -Value ok -ErrorAction Stop;'TARGET-WRITE: OK'}catch{'TARGET-WRITE: DENIED'};`,
`try{Set-Content -Path '${isolatedTemp}\\child-wrote.txt' -Value ok -ErrorAction Stop;'TEMP-WRITE: OK'}catch{'TEMP-WRITE: DENIED'};`,
`try{Set-Content -Path '${escapeFile}' -Value ok -ErrorAction Stop;'ESCAPE-WRITE: OK (ESCAPE!)'}catch{'ESCAPE-WRITE: DENIED'};`,
`try{Get-Content '${secretFile}' -ErrorAction Stop | Out-Null;'SECRET-READ: OK'}catch{'SECRET-READ: DENIED'}`,
].join('')
const result = runRunner([
'--workspace', writableDir, '--temp', isolatedTemp, '--mode', 'workspace-write',
'--', 'pwsh', '/NoLogo', '/NonInteractive', '/NoProfile', '/Command', probe,
])
expect(result.status, `stderr: ${result.stderr}`).toBe(0)
expect(result.stdout).toContain('TARGET-WRITE: OK')
expect(result.stdout).toContain('TEMP-WRITE: OK')
expect(result.stdout).toContain('ESCAPE-WRITE: DENIED')
expect(result.stdout).toContain('SECRET-READ: OK')
expect(existsSync(escapeFile)).toBe(false)
expect(existsSync(join(writableDir, 'child-wrote.txt'))).toBe(true)
}, 30_000)
it('read-only: strict zero grants — no writes anywhere (not even NUL), reads and $null redirection fine', () => {
const probe = [
"$ErrorActionPreference='SilentlyContinue';",
'\'LANGMODE: \' + $ExecutionContext.SessionState.LanguageMode;',
`try{Set-Content -Path '${writableDir}\\readonly-child-wrote.txt' -Value ok -ErrorAction Stop;'TARGET-WRITE: OK'}catch{'TARGET-WRITE: DENIED'};`,
`try{Set-Content -Path '${isolatedTemp}\\readonly-child-wrote.txt' -Value ok -ErrorAction Stop;'TEMP-WRITE: OK'}catch{'TEMP-WRITE: DENIED'};`,
// The NUL device is a securable object: strict zero grants deny it too.
'try{Set-Content -Path \'NUL\' -Value ok -ErrorAction Stop;\'NUL-WRITE: OK\'}catch{\'NUL-WRITE: DENIED\'};',
// PowerShell's $null redirection discards without opening NUL — must keep working.
'echo hi > $null;\'DOLLAR-NULL: OK\';',
`try{Get-Content '${secretFile}' -ErrorAction Stop | Out-Null;'SECRET-READ: OK'}catch{'SECRET-READ: DENIED'}`,
].join('')
const result = runRunner([
'--workspace', writableDir, '--temp', isolatedTemp, '--mode', 'read-only',
'--', 'pwsh', '/NoLogo', '/NonInteractive', '/NoProfile', '/Command', probe,
])
expect(result.status, `stderr: ${result.stderr}`).toBe(0)
expect(result.stdout).toContain('TARGET-WRITE: DENIED')
expect(result.stdout).toContain('TEMP-WRITE: DENIED')
expect(result.stdout).toContain('NUL-WRITE: DENIED')
expect(result.stdout).toContain('DOLLAR-NULL: OK')
expect(result.stdout).toContain('SECRET-READ: OK')
expect(existsSync(join(writableDir, 'readonly-child-wrote.txt'))).toBe(false)
}, 30_000)
it('runner-side failure: signature on stderr and exit 127, the command never runs', () => {
const result = runRunner(['--workspace', writableDir, '--temp', isolatedTemp, '--mode', 'workspace-write'])
expect(result.status).toBe(127)
expect(result.stderr).toContain('windows-acl-run: ')
}, 15_000)
})