Machine-produced by `pnpm run rescope-vendor --apply` plus the regeneration it prints: `pnpm install` for the lockfile, `pnpm run gen-third-party-notices`, `verify-translation-pairing --write` for the touched bilingual pairs, `gen-doc-graphs`, and one typert snapshot whose ids embed character offsets. `pnpm run rescope-vendor --check` verifies the result. Renames nine vendored packages (cordis, cosmokit, schemastery and the six @cordisjs plugins) and every reference that resolves them: manifest names and dependency keys, module specifiers including declare-module merges, cordis.yml plugin names, tsconfig paths, every Markdown fence, and `docs/` prose. Directory names, upstream versions, and dependency ranges are unchanged, so vendor/README.md still reads as an upstream snapshot; its manifest table gains an upstream-name column so THIRD_PARTY_NOTICES keeps MIT attribution pointed at each fork's origin. The tutorial tier follows the rename end to end: its yaml fences named plugins the Loader can no longer resolve, its `ts ignore-check` fences disagreed with the compiled fences beside them, and its prose quoted both. The contracts that told readers to keep upstream names — the root convention and the vendoring cookbook's tree comment and manifest invariant — now say to rescope instead. Two rules read `@deepseek-ai/` as "another workspace plugin": the client bundle purity gate now names the vendored libraries a browser bundle inlines, and the files where a bare `cordis` is an agent-preset id keep that product data.
107 lines
5.1 KiB
TypeScript
107 lines
5.1 KiB
TypeScript
import { spawnSync } from 'node:child_process'
|
|
import { existsSync, readFileSync } 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 '@deepseek-ai/cordis'
|
|
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
|
import { launcherPath } from '@deepseek-ai/node-addon-landlock-run'
|
|
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
|
|
|
/**
|
|
* Keyless backend integration through `confine()` and the workspace `landlock-run` launcher, with
|
|
* bwrap forced off. Tests assert real world effects; consumer coverage lives in dsh-bash-sandbox.
|
|
* Skips when the platform package or enforcing kernel is unavailable. HOME-based workspaces avoid
|
|
* Landlock's wholesale `/tmp` grant, so workspace-write proves the workspace-root grant itself.
|
|
*/
|
|
|
|
const probe = spawnSync(launcherPath(), ['--probe'], { timeout: 5_000, encoding: 'utf8' })
|
|
const landlockUsable = probe.status === 0
|
|
/** The running kernel's enforcement level, from the launcher's probe report — every wrap below must carry exactly this. */
|
|
const enforcement = /partially enforced/.test(probe.stdout ?? '') ? 'partial' : 'full'
|
|
|
|
let ctx: Context | undefined
|
|
const tempDirs: string[] = []
|
|
|
|
afterEach(async () => {
|
|
await ctx?.fiber.dispose()
|
|
ctx = undefined
|
|
await Promise.all(tempDirs.splice(0).map(dir => rm(dir, { recursive: true, force: true })))
|
|
})
|
|
|
|
async function tempDir(base: string): Promise<string> {
|
|
const dir = await mkdtemp(join(base, 'dsh-landlock-e2e-'))
|
|
tempDirs.push(dir)
|
|
return dir
|
|
}
|
|
|
|
async function provider(): Promise<LocalSandboxProvider> {
|
|
ctx = new Context()
|
|
await ctx.plugin(LocalSandboxProvider, {})
|
|
const sandbox = ctx.sandbox as LocalSandboxProvider
|
|
sandbox.internals = { probeBwrap: () => false }
|
|
return sandbox
|
|
}
|
|
|
|
/** Confine a shell command under `policy` and run it for real; returns the spawn result and the wrap's enforcement. */
|
|
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, enforcement: confined.enforcement }
|
|
}
|
|
|
|
describe.skipIf(!landlockUsable)('sandbox-local: real Landlock confinement through the bundled launcher', () => {
|
|
it('read-only denies a write — the file must NOT exist, the wrap reports the probed enforcement', async () => {
|
|
const workdir = await tempDir(tmpdir())
|
|
const sandbox = await provider()
|
|
const { result, enforcement: wrapped } = runConfined(sandbox, `echo hi > ${workdir}/denied.txt`, { mode: 'read-only', workspaceRoot: workdir })
|
|
expect(result.status).not.toBe(0)
|
|
expect(wrapped).toBe(enforcement)
|
|
expect(existsSync(join(workdir, 'denied.txt'))).toBe(false)
|
|
})
|
|
|
|
it('read-only keeps the tree readable/executable and /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('read-only denies a write beneath the host /dev (the /dev/shm tmpfs must stay untouched)', async () => {
|
|
// The grant is /dev/null the FILE, not /dev the directory: /dev/shm is a
|
|
// world-writable host tmpfs, and a write landing there would be exactly
|
|
// the persistent host effect read-only promises never happen.
|
|
const workdir = await tempDir(tmpdir())
|
|
const sandbox = await provider()
|
|
const target = `/dev/shm/dsh-landlock-e2e-${process.pid}`
|
|
const { result } = runConfined(sandbox, `echo hi > ${target}`, { mode: 'read-only', workspaceRoot: workdir })
|
|
expect(result.status).not.toBe(0)
|
|
expect(existsSync(target)).toBe(false)
|
|
})
|
|
|
|
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 landlock-ok > ${workdir}/allowed.txt`, { mode: 'workspace-write', workspaceRoot: workdir })
|
|
expect(inside.result.status).toBe(0)
|
|
expect(readFileSync(join(workdir, 'allowed.txt'), 'utf8')).toBe('landlock-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 grants the host /tmp (the documented Landlock-profile difference)', async () => {
|
|
const workdir = await tempDir(homedir())
|
|
const scratch = await tempDir(tmpdir())
|
|
const sandbox = await provider()
|
|
const { result } = runConfined(sandbox, `printf tmp-ok > ${scratch}/scratch.txt`, { mode: 'workspace-write', workspaceRoot: workdir })
|
|
expect(result.status).toBe(0)
|
|
expect(readFileSync(join(scratch, 'scratch.txt'), 'utf8')).toBe('tmp-ok')
|
|
})
|
|
})
|