Files
deepseek-harness/packages/sandbox/sandbox-local/tests/acl-grants.spec.ts
imccyu ec601ca13d build(vendor): rescope the vendored Cordis packages into @deepseek-ai
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.
2026-08-10 22:04:13 +08:00

405 lines
17 KiB
TypeScript

/**
* windows-acl write grants: the SERVER-LIFETIME ACE materialization
* (standing workspace grant per workspace, revocable private-temp grant per
* session) plus the derived private-temp identity, through the REAL
* LocalSandboxProvider.confine(). Win32 surface mocked at the package
* boundary (the workspace-derived SID mocked to a constant); the real-FFI
* grant behavior lives in sandbox-windows-acl's win32 tests.
*/
import { existsSync, mkdirSync, mkdtempSync, rmSync, symlinkSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { basename, join } from 'node:path'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
import { SessionId } from '@deepseek-ai/dsh-session'
import { LocalSandboxProvider, sessionTempDir } from '@deepseek-ai/dsh-sandbox-local'
/** Cross-file state shared with the vi.mock factory (hoisting contract). */
const mockState = vi.hoisted(() => ({
grants: [] as Array<{ writeSid: string; added: Array<{ path: string; standing: boolean }>; disposed: boolean }>,
addFailure: undefined as Error | undefined,
/** Restricts {@link addFailure} to this path (undefined = every add throws). */
addFailurePath: undefined as string | undefined,
disposeFailure: undefined as Error | undefined,
}))
vi.mock('@deepseek-ai/dsh-sandbox-windows-acl', () => {
class MockAclWriteGrant {
readonly writeSid: string
readonly added: Array<{ path: string; standing: boolean }> = []
disposed = false
constructor(writeSid: string) {
this.writeSid = writeSid
mockState.grants.push(this)
}
static create(writeSid: string): MockAclWriteGrant {
return new MockAclWriteGrant(writeSid)
}
add(path: string, standing = false): void {
if (mockState.addFailure !== undefined && (mockState.addFailurePath === undefined || mockState.addFailurePath === path)) {
throw mockState.addFailure
}
this.added.push({ path, standing })
}
dispose(): void {
if (mockState.disposeFailure !== undefined) throw mockState.disposeFailure
this.disposed = true
}
}
return { AclWriteGrant: MockAclWriteGrant, workspaceWriteSid: () => 'S-1-4-42-42' }
})
/** The workspace-derived write SID the mock pins for every workspace. */
const DERIVED_SID = 'S-1-4-42-42'
async function setup() {
const ctx = new Context()
const fiber = await ctx.plugin(LocalSandboxProvider, {})
const sandbox = ctx.sandbox as LocalSandboxProvider
sandbox.internals = { platform: 'win32', windowsAclRunnerArgs: ['node', 'windows-acl-runner.js'] }
return { ctx, sandbox, fiber }
}
/** A workspace root the policy carries. */
function workspaceRoot(): string {
return mkdtempSync(join(tmpdir(), 'dsh-acl-grants-ws-'))
}
describe('windows-acl write grants (LocalSandboxProvider)', () => {
const scratch: string[] = []
beforeEach(() => {
mockState.grants = []
mockState.addFailure = undefined
mockState.addFailurePath = undefined
mockState.disposeFailure = undefined
})
const cleanup = () => {
for (const dir of scratch.splice(0)) rmSync(dir, { recursive: true, force: true })
}
it('workspace-write: first confine materializes ONCE (standing workspace + revocable private temp), the derived temp dir rides the argv', async () => {
try {
const { sandbox, fiber } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
const tempDir = sessionTempDir(SessionId('sess-1'), ws)
scratch.push(tempDir)
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-1') }
const confined = sandbox.confine(['pwsh', '/Command', 'x'], policy)
expect(confined.argv).toEqual([
'node', 'windows-acl-runner.js',
'--workspace', ws,
'--temp', tempDir,
'--mode', 'workspace-write',
'--write-sid', DERIVED_SID,
'--',
'pwsh', '/Command', 'x',
])
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[0]).toMatchObject({
writeSid: DERIVED_SID,
added: [{ path: ws, standing: true }], // standing: the reuse cache, never revoked
disposed: false,
})
expect(mockState.grants[1]).toMatchObject({
writeSid: DERIVED_SID,
added: [{ path: tempDir, standing: false }],
disposed: false,
})
expect(existsSync(tempDir)).toBe(true) // created exclusively
// Reuse: the second confine is the map hits.
sandbox.confine(['pwsh', '/Command', 'x'], policy)
expect(mockState.grants).toHaveLength(2)
await fiber.dispose()
// dispose() runs on BOTH grants: the standing workspace ACE is left in
// place (the mock marks it disposed only as instance teardown).
expect(mockState.grants[0]!.disposed).toBe(true)
expect(mockState.grants[1]!.disposed).toBe(true)
} finally {
cleanup()
}
})
it('mode switch: read-only materializes nothing, the upgrade materializes ONCE with the derived SID, the downgrade keeps the standing grant', async () => {
try {
const { sandbox } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
const tempDir = sessionTempDir(SessionId('sess-switch'), ws)
scratch.push(tempDir)
const readOnly: SandboxPolicy = { mode: 'read-only', workspaceRoot: ws, sessionId: SessionId('sess-switch') }
const workspaceWrite: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-switch') }
// read-only first: nothing materialized, ambient temp.
const confinedRo = sandbox.confine(['true'], readOnly)
expect(confinedRo.argv).toEqual([
'node', 'windows-acl-runner.js',
'--workspace', ws,
'--temp', tmpdir(), // NOT the private subdir: read-only grants nothing
'--mode', 'read-only',
'--write-sid', DERIVED_SID,
'--',
'true',
])
expect(mockState.grants).toHaveLength(0)
expect(existsSync(tempDir)).toBe(false)
// Upgrade: first workspace-write materializes with the derived SID.
const upgraded = sandbox.confine(['true'], workspaceWrite)
expect(upgraded.argv).toEqual([
'node', 'windows-acl-runner.js',
'--workspace', ws,
'--temp', tempDir,
'--mode', 'workspace-write',
'--write-sid', DERIVED_SID,
'--',
'true',
])
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[0]).toMatchObject({ writeSid: DERIVED_SID, added: [{ path: ws, standing: true }], disposed: false })
expect(mockState.grants[1]).toMatchObject({
writeSid: DERIVED_SID,
added: [{ path: tempDir, standing: false }],
disposed: false,
})
expect(existsSync(tempDir)).toBe(true)
// Reuse: map hits.
sandbox.confine(['true'], workspaceWrite)
expect(mockState.grants).toHaveLength(2)
// Downgrade: standing grant KEPT (inert under read-only, free re-upgrade).
sandbox.confine(['true'], readOnly)
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[0]!.disposed).toBe(false)
} finally {
cleanup()
}
})
it('resume: a fresh provider derives the SAME temp dir for the same session and workspace and re-grants it', async () => {
try {
const ws = workspaceRoot()
scratch.push(ws)
const first = await setup()
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('resumed') }
const firstConfined = first.sandbox.confine(['true'], policy)
expect(mockState.grants).toHaveLength(2)
// Clean restart: dispose revokes the temp ACE and removes the private
// temp directory, so the fresh provider's exclusive creation succeeds.
await first.fiber.dispose()
mockState.grants = []
const second = await setup()
const secondConfined = second.sandbox.confine(['true'], policy)
expect(secondConfined.argv).toEqual(firstConfined.argv)
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[1]).toMatchObject({
writeSid: DERIVED_SID,
added: [{ path: sessionTempDir(SessionId('resumed'), ws), standing: false }],
})
await second.fiber.dispose()
} finally {
cleanup()
}
})
it('fork: a different session id derives a DIFFERENT private temp identity over the same workspace', async () => {
try {
const { sandbox } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
const parentPolicy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('parent') }
const childPolicy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('child') }
sandbox.confine(['true'], parentPolicy)
const parentTemp = sessionTempDir(SessionId('parent'), ws)
scratch.push(parentTemp)
sandbox.confine(['true'], childPolicy)
const childTemp = sessionTempDir(SessionId('child'), ws)
scratch.push(childTemp)
// Fresh temp identity, NOT the parent's (the workspace SID is shared by
// derivation — the workspace is the same, so the standing grant is the
// map hit and only the child's temp grant joins).
expect(childTemp).not.toBe(parentTemp)
expect(mockState.grants).toHaveLength(3)
expect(mockState.grants[2]).toMatchObject({ added: [{ path: childTemp, standing: false }] })
} finally {
cleanup()
}
})
it('creates the private temp dir EXCLUSIVELY: a pre-existing entry or a reparse point fails EEXIST, never receiving the temp grant', async () => {
try {
const { sandbox } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
// Pre-existing entry: exclusive mkdir throws EEXIST instead of adopting it.
const preexisting = sessionTempDir(SessionId('preexisting'), ws)
mkdirSync(preexisting)
scratch.push(preexisting)
const prePolicy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('preexisting') }
expect(() => sandbox.confine(['true'], prePolicy)).toThrow(/EEXIST/)
// The standing workspace grant is the intended end state and stays; the
// failed temp grant self-disposes.
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[0]!.disposed).toBe(false)
expect(mockState.grants[1]!.disposed).toBe(true) // self-revoked
// Reparse point: same EEXIST (exclusive mkdir never follows links).
const target = mkdtempSync(join(tmpdir(), 'dsh-acl-junction-target-'))
scratch.push(target)
const linkPath = sessionTempDir(SessionId('reparse'), ws)
symlinkSync(target, linkPath)
scratch.push(linkPath)
const linkPolicy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('reparse') }
expect(() => sandbox.confine(['true'], linkPolicy)).toThrow(/EEXIST/)
// Same workspace as the preexisting case: the standing workspace grant
// is the map hit (not recreated) — only the failed temp grant joins.
expect(mockState.grants).toHaveLength(3)
expect(mockState.grants[2]!.disposed).toBe(true)
// Temp-side cleanup failure: the standing workspace grant stays (map
// hit), the exclusive mkdir fails, AND the temp grant's dispose also
// fails — the temp cleanup AggregateError propagates.
mockState.grants = []
mockState.disposeFailure = new Error('temp cleanup exploded')
const dupTemp = sessionTempDir(SessionId('temp-cleanup-fail'), ws)
mkdirSync(dupTemp)
scratch.push(dupTemp)
const dupPolicy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('temp-cleanup-fail') }
expect(() => sandbox.confine(['true'], dupPolicy)).toThrow(/temp grant materialization failed and its cleanup also failed/)
expect(mockState.grants).toHaveLength(1) // only the failed temp grant (the workspace grant was the map hit)
} finally {
cleanup()
}
})
it('a grant failure mid-materialization disposes the failed grant and rethrows (AggregateError when the cleanup also fails)', async () => {
try {
const { sandbox } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
scratch.push(sessionTempDir(SessionId('sess-add-fail'), ws))
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-add-fail') }
// add() throws on the FIRST (workspace) grant: cleanup dispose() runs, original error propagates.
mockState.addFailure = new Error('grant exploded')
expect(() => sandbox.confine(['true'], policy)).toThrow('grant exploded')
expect(mockState.grants).toHaveLength(1)
expect(mockState.grants[0]!.disposed).toBe(true)
// add() AND dispose() both throw: AggregateError.
mockState.grants = []
mockState.addFailure = new Error('grant exploded again')
mockState.disposeFailure = new Error('cleanup exploded')
expect(() => sandbox.confine(['true'], policy)).toThrow(AggregateError)
} finally {
cleanup()
}
})
it('a temp add failure after the exclusive mkdir removed the half-created directory again', async () => {
try {
const { sandbox } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
const tempDir = sessionTempDir(SessionId('sess-temp-add-fail'), ws)
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-temp-add-fail') }
// The workspace grant succeeds; only the TEMP grant's add throws (the
// path-targeted failure keeps the workspace branch intact).
mockState.addFailurePath = tempDir
mockState.addFailure = new Error('temp add exploded')
expect(() => sandbox.confine(['true'], policy)).toThrow('temp add exploded')
expect(existsSync(tempDir)).toBe(false) // the half-created directory is removed again
expect(mockState.grants).toHaveLength(2)
expect(mockState.grants[0]!.disposed).toBe(false) // the standing workspace grant stays
expect(mockState.grants[1]!.disposed).toBe(true) // the failed temp grant self-disposes
} finally {
cleanup()
}
})
it('agentless calls stay self-managed: no --write-sid, the ambient temp root, no grants', async () => {
try {
const { sandbox, fiber } = await setup()
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: '/ws' }
const confined = sandbox.confine(['pwsh', '/Command', 'x'], policy)
expect(confined.argv).toEqual([
'node', 'windows-acl-runner.js',
'--workspace', '/ws',
'--temp', tmpdir(),
'--mode', 'workspace-write',
'--',
'pwsh', '/Command', 'x',
])
expect(mockState.grants).toHaveLength(0)
await fiber.dispose()
} finally {
cleanup()
}
})
it('a failing dispose at provider teardown is reported via ctx.logger.warn and never thrown into teardown', async () => {
try {
const { ctx, sandbox, fiber } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
scratch.push(sessionTempDir(SessionId('sess-dispose'), ws))
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-dispose') }
sandbox.confine(['true'], policy)
expect(mockState.grants).toHaveLength(2)
mockState.disposeFailure = new Error('revoke exploded')
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
await fiber.dispose()
// BOTH grants (standing workspace + revocable temp) fail their dispose.
expect(warn).toHaveBeenCalledWith(expect.stringContaining('cleanup completed with 2 failure(s)'))
expect(warn).toHaveBeenCalledWith(expect.objectContaining({ message: 'revoke exploded' }))
} finally {
cleanup()
}
})
it('a failing private-temp removal at provider teardown is reported via ctx.logger.warn and never thrown into teardown', async () => {
try {
const { ctx, sandbox, fiber } = await setup()
const ws = workspaceRoot()
scratch.push(ws)
scratch.push(sessionTempDir(SessionId('sess-rm-fail'), ws))
const policy: SandboxPolicy = { mode: 'workspace-write', workspaceRoot: ws, sessionId: SessionId('sess-rm-fail') }
sandbox.confine(['true'], policy)
expect(mockState.grants).toHaveLength(2)
sandbox.internals.rmTempDir = () => { throw new Error('rm exploded') }
const warn = vi.spyOn(ctx.logger, 'warn').mockImplementation(() => undefined)
await fiber.dispose()
// Both grants dispose cleanly; only the directory removal fails.
expect(warn).toHaveBeenCalledWith(expect.stringContaining('cleanup completed with 1 failure(s)'))
expect(warn).toHaveBeenCalledWith(expect.objectContaining({ message: 'rm exploded' }))
} finally {
cleanup()
}
})
it('sessionTempDir derives the same well-shaped name for the same session and workspace, distinct otherwise', () => {
const base = sessionTempDir(SessionId('sess-a'), '/ws/a')
expect(basename(base)).toMatch(/^dsh-[0-9a-f]{16}$/)
expect(sessionTempDir(SessionId('sess-a'), '/ws/a')).toBe(base)
expect(sessionTempDir(SessionId('sess-b'), '/ws/a')).not.toBe(base) // different session
expect(sessionTempDir(SessionId('sess-a'), '/ws/b')).not.toBe(base) // different workspace
// The separator prevents id/workspace collisions from merging inputs.
expect(sessionTempDir(SessionId('ab'), '/ws/c')).not.toBe(sessionTempDir(SessionId('a'), '/ws/bc'))
})
})