Files
deepseek-harness/packages/sandbox/sandbox-local/tests/bwrap.e2e.ts
T
kingwl 7b8c3a9b40 feat(sandbox): the confinement seam and the per-platform native runner chains
ctx.sandbox (dsh-sandbox): confine(argv, policy) returns the argv to spawn
instead — wrapped so the process and its children run confined — plus the
enforcement completeness and the backend denial/runner-failure dialects;
no usable backend throws the fail-closed SANDBOX_UNAVAILABLE. Policy rides
per call. dsh-sandbox-local selects by platform and caches the verdict:
multi-candidate chains probe FUNCTIONALLY in preference order (Linux:
bwrap → the registry-installed node-addon-landlock-run launcher), a sole
candidate is selected unprobed (darwin: sandbox-exec/Seatbelt) and fails
closed at execution via runnerFailureSignatures; win32 is a reserved empty
chain. Profile parity is honest per backend (documented temp-area and ABI
differences; enforcement full|partial is a structured result fact).

CI: the sandbox-e2e matrix proves real-kernel confinement per rung (bwrap,
Landlock per architecture through the registry-installed launcher,
Seatbelt), failing on a silent all-skip; the packed-install rehearsal
installs the launcher family from the registry and asserts the binary
executable apart from kernel enforcement.
2026-07-10 15:43:02 +08:00

119 lines
5.7 KiB
TypeScript

import { spawnSync } from 'node:child_process'
import { existsSync, readFileSync, rmSync } from 'node:fs'
import { mkdtemp, rm } from 'node:fs/promises'
import { homedir, tmpdir } from 'node:os'
import { join } from 'node:path'
import { afterEach, describe, expect, it } from 'vitest'
import { Context } from 'cordis'
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
import { bwrapProfileArgs, LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
/**
* KEYLESS bwrap integration proof for the BACKEND: the REAL `bwrap` confining
* REAL processes through `confine()` + a direct spawn of the returned argv.
* Nothing is forced off: bwrap is the ladder's FIRST rung, so a passing probe
* selects it naturally — the wrap shape assertion pins that. Verifies the
* WORLD (files exist or don't) and that the kernel's denial text matches the
* dialect the wrap advertises; the through-`ctx.bash` consumer proof lives
* with `@deepseek-ai/dsh-bash-sandbox`.
*
* Self-skips wherever the functional probe fails — no `bwrap` on PATH, or a
* host that denies unprivileged user namespaces (the probe is the same
* profile the provider enforces, so skip conditions match runtime exactly).
*
* Workspaces for the workspace-write tests live under the HOME directory on
* purpose: bwrap's `/tmp` is an EPHEMERAL mount (the documented
* bwrap-profile difference — pinned by its own test below), so only a
* workspace OUTSIDE `/tmp` proves the workspace-root rebind itself.
*/
const probe = spawnSync('bwrap', [...bwrapProfileArgs({ mode: 'read-only', workspaceRoot: '/' }), '--', 'true'], { timeout: 5_000, stdio: 'ignore' })
const bwrapUsable = probe.status === 0
let ctx: Context | undefined
const tempDirs: string[] = []
const tempFiles: string[] = []
afterEach(async () => {
await ctx?.fiber.dispose()
ctx = undefined
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
for (const file of tempFiles.splice(0)) rmSync(file, { force: true })
})
async function tempDir(base: string): Promise<string> {
const dir = await mkdtemp(join(base, 'dsh-bwrap-e2e-'))
tempDirs.push(dir)
return dir
}
async function provider(): Promise<LocalSandboxProvider> {
ctx = new Context()
await ctx.plugin(LocalSandboxProvider, {})
return ctx.sandbox as LocalSandboxProvider
}
/** Confine a shell command under `policy` and run it for real; returns the spawn result and the wrap's facts. */
function runConfined(sandbox: LocalSandboxProvider, command: string, policy: SandboxPolicy) {
const confined = sandbox.confine(['bash', '-c', command], policy)
const result = spawnSync(confined.argv[0] as string, confined.argv.slice(1), { timeout: 30_000, encoding: 'utf8' })
return { result, confined }
}
describe.skipIf(!bwrapUsable)('sandbox-local: real bwrap confinement', () => {
it('the passing probe selects the bwrap rung naturally — first in the ladder, full enforcement, EROFS dialect', async () => {
const workdir = await tempDir(tmpdir())
const sandbox = await provider()
const confined = sandbox.confine(['true'], { mode: 'read-only', workspaceRoot: workdir })
expect(confined.argv[0]).toBe('bwrap')
expect(confined.enforcement).toBe('full')
expect(confined.denialSignatures).toEqual(['read-only file system'])
})
it('read-only denies a write — the file must NOT exist, and the kernel speaks the advertised dialect', async () => {
const workdir = await tempDir(tmpdir())
const sandbox = await provider()
const { result } = runConfined(sandbox, `echo hi > ${workdir}/denied.txt`, { mode: 'read-only', workspaceRoot: workdir })
expect(result.status).not.toBe(0)
// The wrap's denialSignatures must be what the kernel actually prints.
expect(result.stderr.toLowerCase()).toContain('read-only file system')
expect(existsSync(join(workdir, 'denied.txt'))).toBe(false)
})
it('read-only keeps the tree readable/executable and the fresh /dev/null writable', async () => {
const workdir = await tempDir(tmpdir())
const sandbox = await provider()
const { result } = runConfined(sandbox, 'ls / > /dev/null && echo dev-ok', { mode: 'read-only', workspaceRoot: workdir })
expect(result.status).toBe(0)
expect(result.stdout).toBe('dev-ok\n')
})
it('workspace-write lands a write inside the workspace root and still denies one beside it', async () => {
const workdir = await tempDir(homedir())
const outside = await tempDir(homedir())
const sandbox = await provider()
const inside = runConfined(sandbox, `printf bwrap-ok > ${workdir}/allowed.txt`, { mode: 'workspace-write', workspaceRoot: workdir })
expect(inside.result.status).toBe(0)
expect(readFileSync(join(workdir, 'allowed.txt'), 'utf8')).toBe('bwrap-ok')
const denied = runConfined(sandbox, `echo hi > ${outside}/denied.txt`, { mode: 'workspace-write', workspaceRoot: workdir })
expect(denied.result.status).not.toBe(0)
expect(existsSync(join(outside, 'denied.txt'))).toBe(false)
})
it('workspace-write mounts an EPHEMERAL /tmp: the write succeeds inside, the host /tmp stays untouched', async () => {
// The documented bwrap-profile difference: Landlock and Seatbelt grant
// the HOST temp areas, bwrap swaps in a fresh tmpfs that dies with the
// process — the strongest of the three temp semantics.
const workdir = await tempDir(homedir())
const target = `/tmp/dsh-bwrap-e2e-ephemeral-${process.pid}.txt`
tempFiles.push(target)
const sandbox = await provider()
const { result } = runConfined(sandbox, `printf tmp-ok > ${target} && cat ${target}`, { mode: 'workspace-write', workspaceRoot: workdir })
expect(result.status).toBe(0)
expect(result.stdout).toBe('tmp-ok')
expect(existsSync(target)).toBe(false)
})
})