File size: 4,875 Bytes
488d96b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/** The unchanged sandbox-local → bash-sandbox → subprocess stack over the Worker Node layer. */
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { Context } from '@deepseek-ai/cordis'
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
import LocalSandboxProvider from '@deepseek-ai/dsh-sandbox-local'
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
import SessionProjectionRegistry from '@deepseek-ai/dsh-session-projection'
import LocalSubprocessRuntime from '@deepseek-ai/dsh-subprocess-local'
import { MemoryVfs } from '../../src/storage/memory.ts'
import { setActiveVfs } from '../../src/storage/active.ts'
import { processAlive, signalProcess } from '../../src/node/process-table.ts'

vi.mock('node:child_process', async () => await import('../../src/node/builtin_modules/implemented/child_process.ts'))

const WORKSPACE = '/dsh/workspace'
const OUTSIDE = '/dsh/home'
let vfs: MemoryVfs
const contexts: Context[] = []

beforeEach(() => {
  vfs = new MemoryVfs()
  setActiveVfs(vfs)
  vfs.mkdirSync(WORKSPACE, { recursive: true })
  vfs.mkdirSync(OUTSIDE, { recursive: true })
  vfs.mkdirSync('/dsh/tmp', { recursive: true })
  vi.spyOn(process, 'kill').mockImplementation((pid: number, signal?: string | number): true => {
    if (signal === 0) {
      if (processAlive(pid)) return true
      const error = new Error('kill ESRCH') as NodeJS.ErrnoException
      error.code = 'ESRCH'
      throw error
    }
    signalProcess(pid, (signal ?? 'SIGTERM') as NodeJS.Signals)
    return true
  })
})

afterEach(async () => {
  await Promise.all(contexts.splice(0).map(async (ctx) => { await ctx.fiber.dispose() }))
  vi.restoreAllMocks()
})

/** Boot the production providers while only their platform primitives are replaced. */
async function setup(mode: 'read-only' | 'workspace-write' | 'danger-full-access'): Promise<SandboxBashExecutor> {
  const ctx = new Context()
  contexts.push(ctx)
  await ctx.plugin(LocalSandboxProvider)
  await ctx.plugin(SessionProjectionRegistry)
  await ctx.plugin(SandboxPolicyService, { mode, workspaceRoot: WORKSPACE })
  await ctx.plugin(LocalSubprocessRuntime)
  await ctx.plugin(SandboxBashExecutor, { cwd: WORKSPACE })
  return ctx.shell as SandboxBashExecutor
}

describe('Worker Landlock through the production sandbox stack', () => {
  it('allows workspace and temp writes while classifying an outside write as denied', async () => {
    const bash = await setup('workspace-write')
    const allowed = await bash.run(bash.resolve({
      command: `echo workspace > ${WORKSPACE}/allowed.txt; echo temp > /tmp/allowed.txt`,
    }))
    expect(allowed.sandbox).toEqual({ mode: 'workspace-write', denied: false, enforcement: 'full' })
    expect(vfs.readFileSync(`${WORKSPACE}/allowed.txt`, 'utf8')).toBe('workspace\n')
    expect(vfs.readFileSync('/dsh/tmp/allowed.txt', 'utf8')).toBe('temp\n')

    const denied = await bash.run(bash.resolve({ command: `echo denied > ${OUTSIDE}/denied.txt` }))
    expect(denied.exitCode).toBe(1)
    expect(denied.sandbox).toEqual({ mode: 'workspace-write', denied: true, enforcement: 'full' })
    expect(vfs.existsSync(`${OUTSIDE}/denied.txt`)).toBe(false)
  })

  it('keeps read-only confined and danger-full-access unwrapped', async () => {
    const readOnly = await setup('read-only')
    const strict = await readOnly.run(readOnly.resolve({
      command: `echo discarded > /dev/null; echo denied > ${WORKSPACE}/strict.txt`,
    }))
    expect(strict.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'full' })
    expect(vfs.existsSync(`${WORKSPACE}/strict.txt`)).toBe(false)

    const unrestricted = await setup('danger-full-access')
    const result = await unrestricted.run(unrestricted.resolve({ command: `echo allowed > ${OUTSIDE}/full.txt` }))
    expect(result.sandbox).toEqual({ mode: 'danger-full-access', denied: false })
    expect(vfs.readFileSync(`${OUTSIDE}/full.txt`, 'utf8')).toBe('allowed\n')
  })

  it('does not leak a concurrent command policy into another process', async () => {
    const bash = await setup('read-only')
    const strict = bash.run(bash.resolve({
      command: `sleep 0.02; echo denied > ${WORKSPACE}/strict.txt`,
    }))
    const writable = bash.run(bash.resolve({
      command: `echo allowed > ${WORKSPACE}/writable.txt`,
      sandboxPolicy: { mode: 'workspace-write', workspaceRoot: WORKSPACE },
    }))
    const [strictResult, writableResult] = await Promise.all([strict, writable])
    expect(strictResult.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'full' })
    expect(writableResult.sandbox).toEqual({ mode: 'workspace-write', denied: false, enforcement: 'full' })
    expect(vfs.existsSync(`${WORKSPACE}/strict.txt`)).toBe(false)
    expect(vfs.readFileSync(`${WORKSPACE}/writable.txt`, 'utf8')).toBe('allowed\n')
  })
})