refactor(sandbox): defer native protocol exports (round 3)
This commit is contained in:
@@ -1037,7 +1037,7 @@ export interface Config {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Source: [`packages/sandbox/sandbox-local/src/index.ts:25`](../packages/sandbox/sandbox-local/src/index.ts)
|
Source: [`packages/sandbox/sandbox-local/src/index.ts:24`](../packages/sandbox/sandbox-local/src/index.ts)
|
||||||
|
|
||||||
## `@deepseek-ai/dsh-sandbox-policy`
|
## `@deepseek-ai/dsh-sandbox-policy`
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
import type { ConfinedArgv, SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
||||||
import { SandboxProvider } from '@deepseek-ai/dsh-sandbox'
|
import { SandboxProvider } from '@deepseek-ai/dsh-sandbox'
|
||||||
import {
|
|
||||||
LAUNCHER_FAILURE_EXIT,
|
const NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)'
|
||||||
LAUNCHER_FATAL_PREFIX,
|
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
} from 'node-addon-landlock-run'
|
|
||||||
|
|
||||||
/** Snapshot-only provider that reproduces an older-ABI Landlock launch. */
|
/** Snapshot-only provider that reproduces an older-ABI Landlock launch. */
|
||||||
export default class PartialLandlockSandboxProvider extends SandboxProvider {
|
export default class PartialLandlockSandboxProvider extends SandboxProvider {
|
||||||
@@ -13,16 +10,16 @@ export default class PartialLandlockSandboxProvider extends SandboxProvider {
|
|||||||
argv: [
|
argv: [
|
||||||
'bash',
|
'bash',
|
||||||
'-c',
|
'-c',
|
||||||
`printf '%s\\n' '${PARTIAL_ENFORCEMENT_NOTICE}' >&2; exec "$@"`,
|
`printf '%s\\n' '${NOTICE}' >&2; exec "$@"`,
|
||||||
'partial-landlock-run',
|
'partial-landlock-run',
|
||||||
...argv,
|
...argv,
|
||||||
],
|
],
|
||||||
enforcement: 'partial',
|
enforcement: 'partial',
|
||||||
denialSignatures: ['permission denied'],
|
denialSignatures: ['permission denied'],
|
||||||
runnerFailureRules: [{
|
runnerFailureRules: [{
|
||||||
allowedExitCodes: [LAUNCHER_FAILURE_EXIT],
|
allowedExitCodes: [125],
|
||||||
fatalSignatures: [LAUNCHER_FATAL_PREFIX],
|
fatalSignatures: ['landlock-run: '],
|
||||||
informationalLines: [PARTIAL_ENFORCEMENT_NOTICE],
|
informationalLines: [NOTICE],
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,6 @@
|
|||||||
"@deepseek-ai/dsh-web": "workspace:*",
|
"@deepseek-ai/dsh-web": "workspace:*",
|
||||||
"@deepseek-ai/dsh-web-fetch-local": "workspace:*",
|
"@deepseek-ai/dsh-web-fetch-local": "workspace:*",
|
||||||
"@deepseek-ai/dsh-workflow-workerthread": "workspace:*",
|
"@deepseek-ai/dsh-workflow-workerthread": "workspace:*",
|
||||||
"@deepseek-ai/dsh-workspace-context": "workspace:*",
|
"@deepseek-ai/dsh-workspace-context": "workspace:*"
|
||||||
"node-addon-landlock-run": "0.0.0-test.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
||||||
# after editing either side, bring the other along and re-record with:
|
# after editing either side, bring the other along and re-record with:
|
||||||
# pnpm run verify-translation-pairing --write native/landlock-run/README.md
|
# pnpm run verify-translation-pairing --write native/landlock-run/README.md
|
||||||
README.md: 483b14df7cb91fc6486f69efc1ddc560ab635ed5
|
README.md: 19cc18830b90609f648cfb2ce1ee509ad9fe381b
|
||||||
README.zh.md: a239889ec68a8d56bb901b17feaac7f7acbf66fa
|
README.zh.md: c93c33b843e3afc15b1890ce14edf436e051592c
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ The public API is intentionally small:
|
|||||||
- `launcherPath()`: absolute path of this host's launcher (existence deliberately unchecked — the probe is the availability signal).
|
- `launcherPath()`: absolute path of this host's launcher (existence deliberately unchecked — the probe is the availability signal).
|
||||||
- `probe(launcher?, { timeoutMs? })`: functional enforcement probe — `'full' | 'partial' | 'unusable'`.
|
- `probe(launcher?, { timeoutMs? })`: functional enforcement probe — `'full' | 'partial' | 'unusable'`.
|
||||||
- `grantArgs({ readOnly?, readWrite? })`: the launcher's grant argv; everything not granted is denied.
|
- `grantArgs({ readOnly?, readWrite? })`: the launcher's grant argv; everything not granted is denied.
|
||||||
- `LAUNCHER_BIN`, `LAUNCHER_FAILURE_EXIT` (125), `LAUNCHER_FATAL_PREFIX`, and `PARTIAL_ENFORCEMENT_NOTICE`: contract constants. Every launcher failure uses 125, but a successfully exec'd child may also return 125, so failure attribution requires the fatal prefix too.
|
- `LAUNCHER_BIN` and `LAUNCHER_FAILURE_EXIT` (125): contract constants. A successfully exec'd child may also return 125, so consumers need the fatal diagnostic as well as the status to attribute launcher failure.
|
||||||
|
|
||||||
The full binary contract (argv grammar, exit codes, report lines) is pinned in [docs/cli-contract.md](docs/cli-contract.md).
|
The full binary contract (argv grammar, exit codes, report lines) is pinned in [docs/cli-contract.md](docs/cli-contract.md).
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ if (probe(launcher) !== 'unusable') {
|
|||||||
- `launcherPath()`:当前宿主启动器的绝对路径(有意不检查是否存在;探测结果才是可用性信号)。
|
- `launcherPath()`:当前宿主启动器的绝对路径(有意不检查是否存在;探测结果才是可用性信号)。
|
||||||
- `probe(launcher?, { timeoutMs? })`:功能性强制执行探测,返回 `'full' | 'partial' | 'unusable'`。
|
- `probe(launcher?, { timeoutMs? })`:功能性强制执行探测,返回 `'full' | 'partial' | 'unusable'`。
|
||||||
- `grantArgs({ readOnly?, readWrite? })`:启动器的授权 argv;未授予的一切都被拒绝。
|
- `grantArgs({ readOnly?, readWrite? })`:启动器的授权 argv;未授予的一切都被拒绝。
|
||||||
- `LAUNCHER_BIN`、`LAUNCHER_FAILURE_EXIT`(125)、`LAUNCHER_FATAL_PREFIX` 和 `PARTIAL_ENFORCEMENT_NOTICE`:契约常量。所有 launcher 失败的退出码都是 125,但成功完成 exec 的子进程也可能返回 125,因此还必须有致命前缀,才能将结果归因为 launcher 失败。
|
- `LAUNCHER_BIN` 和 `LAUNCHER_FAILURE_EXIT`(125):契约常量。成功完成 exec 的子进程也可能返回 125,因此消费者必须同时看到致命诊断和该状态,才能将结果归因为 launcher 失败。
|
||||||
|
|
||||||
完整的二进制契约(argv 语法、退出码、报告行)锁定在 [docs/cli-contract.md](docs/cli-contract.md) 中。
|
完整的二进制契约(argv 语法、退出码、报告行)锁定在 [docs/cli-contract.md](docs/cli-contract.md) 中。
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# CLI contract: landlock-run
|
# CLI contract: landlock-run
|
||||||
|
|
||||||
This file pins the launcher's externally observable behavior — the cross-repo compatibility surface between the binaries and every consumer. Consumers interact with it only through the entry package (`launcherPath`/`probe`/`grantArgs` and its protocol constants); changing anything below requires a version bump for the whole package family and a note in the release notes.
|
This file pins the launcher's externally observable behavior — the cross-repo compatibility surface between the binaries and every consumer. Consumers interact with it through the entry package (`launcherPath`/`probe`/`grantArgs`) and the launcher protocol; changing anything below requires a version bump for the whole package family and a note in the release notes.
|
||||||
|
|
||||||
## Invocation grammar
|
## Invocation grammar
|
||||||
|
|
||||||
@@ -20,14 +20,14 @@ landlock-run --probe
|
|||||||
## Exit codes
|
## Exit codes
|
||||||
|
|
||||||
- `125` (`LAUNCHER_FAILURE_EXIT`): every launcher-level failure — usage error, kernel that cannot enforce Landlock, unopenable grant root, failed `exec`. The wrapped command was NOT run.
|
- `125` (`LAUNCHER_FAILURE_EXIT`): every launcher-level failure — usage error, kernel that cannot enforce Landlock, unopenable grant root, failed `exec`. The wrapped command was NOT run.
|
||||||
- After a successful `exec`, every child status is passed through unchanged, including 125. Consumers therefore require both status 125 and a `LAUNCHER_FATAL_PREFIX` line to attribute launcher failure.
|
- After a successful `exec`, every child status is passed through unchanged, including 125. Consumers therefore require both status 125 and a `landlock-run: ` fatal line to attribute launcher failure.
|
||||||
- `--probe`: `0` when the kernel enforces (fully or partially), `125` otherwise.
|
- `--probe`: `0` when the kernel enforces (fully or partially), `125` otherwise.
|
||||||
|
|
||||||
## Report lines
|
## Report lines
|
||||||
|
|
||||||
- Probe success prints exactly one stdout line: `landlock: fully enforced` or `landlock: partially enforced (older ABI)`. The entry package's `probe()` maps these to `full`/`partial`; a non-zero probe exit maps to `unusable`.
|
- Probe success prints exactly one stdout line: `landlock: fully enforced` or `landlock: partially enforced (older ABI)`. The entry package's `probe()` maps these to `full`/`partial`; a non-zero probe exit maps to `unusable`.
|
||||||
- A confined run under a partial-ABI kernel prints one stderr line `landlock-run: partial enforcement (older Landlock ABI)` (`PARTIAL_ENFORCEMENT_NOTICE`) and proceeds — still confined for everything the kernel supports.
|
- A confined run under a partial-ABI kernel prints one stderr line `landlock-run: partial enforcement (older Landlock ABI)` and proceeds — still confined for everything the kernel supports.
|
||||||
- Every fatal error prints one stderr line prefixed `landlock-run: ` (`LAUNCHER_FATAL_PREFIX`) before exiting `125`.
|
- Every fatal error prints one stderr line prefixed `landlock-run: ` before exiting `125`.
|
||||||
|
|
||||||
## Confinement semantics
|
## Confinement semantics
|
||||||
|
|
||||||
|
|||||||
@@ -21,18 +21,12 @@ import { fileURLToPath } from 'node:url'
|
|||||||
/** The launcher binary's file name inside each platform package's `bin/`. */
|
/** The launcher binary's file name inside each platform package's `bin/`. */
|
||||||
export const LAUNCHER_BIN = 'landlock-run'
|
export const LAUNCHER_BIN = 'landlock-run'
|
||||||
|
|
||||||
/** Prefix on every launcher-owned fatal stderr line. */
|
|
||||||
export const LAUNCHER_FATAL_PREFIX = 'landlock-run: '
|
|
||||||
|
|
||||||
/** Informational stderr line emitted before child execution under partial enforcement. */
|
|
||||||
export const PARTIAL_ENFORCEMENT_NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The exit code for every launcher-level failure (usage error, unenforcing
|
* The exit code for every launcher-level failure (usage error, unenforcing
|
||||||
* kernel, unopenable grant root, failed exec). After a successful `exec`, the
|
* kernel, unopenable grant root, failed exec). After a successful `exec`, the
|
||||||
* wrapped command may also return 125, so consumers require a matching
|
* wrapped command may also return 125, so consumers also require a matching
|
||||||
* {@link LAUNCHER_FATAL_PREFIX} diagnostic to attribute launcher failure.
|
* launcher-owned fatal diagnostic to attribute launcher failure. Part of the
|
||||||
* Part of the CLI contract.
|
* CLI contract.
|
||||||
*/
|
*/
|
||||||
export const LAUNCHER_FAILURE_EXIT = 125
|
export const LAUNCHER_FAILURE_EXIT = 125
|
||||||
|
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ import path from 'node:path';
|
|||||||
import {
|
import {
|
||||||
LAUNCHER_BIN,
|
LAUNCHER_BIN,
|
||||||
LAUNCHER_FAILURE_EXIT,
|
LAUNCHER_FAILURE_EXIT,
|
||||||
LAUNCHER_FATAL_PREFIX,
|
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
grantArgs,
|
grantArgs,
|
||||||
launcherPath,
|
launcherPath,
|
||||||
probe,
|
probe,
|
||||||
@@ -22,8 +20,6 @@ import {
|
|||||||
// --- constants are part of the CLI contract ---
|
// --- constants are part of the CLI contract ---
|
||||||
assert.equal(LAUNCHER_BIN, 'landlock-run');
|
assert.equal(LAUNCHER_BIN, 'landlock-run');
|
||||||
assert.equal(LAUNCHER_FAILURE_EXIT, 125);
|
assert.equal(LAUNCHER_FAILURE_EXIT, 125);
|
||||||
assert.equal(LAUNCHER_FATAL_PREFIX, 'landlock-run: ');
|
|
||||||
assert.equal(PARTIAL_ENFORCEMENT_NOTICE, 'landlock-run: partial enforcement (older Landlock ABI)');
|
|
||||||
|
|
||||||
// --- grantArgs: flag spelling, ordering, and empty grants ---
|
// --- grantArgs: flag spelling, ordering, and empty grants ---
|
||||||
assert.deepEqual(grantArgs({}), []);
|
assert.deepEqual(grantArgs({}), []);
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ import path from 'node:path';
|
|||||||
import { spawnSync } from 'node:child_process';
|
import { spawnSync } from 'node:child_process';
|
||||||
import {
|
import {
|
||||||
LAUNCHER_FAILURE_EXIT,
|
LAUNCHER_FAILURE_EXIT,
|
||||||
LAUNCHER_FATAL_PREFIX,
|
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
grantArgs,
|
grantArgs,
|
||||||
launcherPath,
|
launcherPath,
|
||||||
probe,
|
probe,
|
||||||
} from 'node-addon-landlock-run';
|
} from 'node-addon-landlock-run';
|
||||||
|
|
||||||
|
const FATAL_PREFIX = 'landlock-run: ';
|
||||||
|
const PARTIAL_NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)';
|
||||||
const requireLandlock = process.env.NALR_REQUIRE_LANDLOCK === '1';
|
const requireLandlock = process.env.NALR_REQUIRE_LANDLOCK === '1';
|
||||||
|
|
||||||
if (process.platform !== 'linux') {
|
if (process.platform !== 'linux') {
|
||||||
@@ -45,7 +45,7 @@ const run = (args, options = {}) => spawnSync(launcher, args, { encoding: 'utf8'
|
|||||||
{
|
{
|
||||||
const noCommand = run([]);
|
const noCommand = run([]);
|
||||||
assert.equal(noCommand.status, LAUNCHER_FAILURE_EXIT);
|
assert.equal(noCommand.status, LAUNCHER_FAILURE_EXIT);
|
||||||
assert.ok(noCommand.stderr.startsWith(LAUNCHER_FATAL_PREFIX));
|
assert.ok(noCommand.stderr.startsWith(FATAL_PREFIX));
|
||||||
assert.match(noCommand.stderr, /usage error: missing `-- <argv>\.\.\.` command/);
|
assert.match(noCommand.stderr, /usage error: missing `-- <argv>\.\.\.` command/);
|
||||||
|
|
||||||
const unknownFlag = run(['--bogus', '--', 'true']);
|
const unknownFlag = run(['--bogus', '--', 'true']);
|
||||||
@@ -78,7 +78,7 @@ if (enforcement === 'unusable') {
|
|||||||
console.log('launcher.test: SKIP enforcement half — kernel does not enforce Landlock');
|
console.log('launcher.test: SKIP enforcement half — kernel does not enforce Landlock');
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
const expectedNotice = enforcement === 'partial' ? `${PARTIAL_ENFORCEMENT_NOTICE}\n` : '';
|
const expectedNotice = enforcement === 'partial' ? `${PARTIAL_NOTICE}\n` : '';
|
||||||
{
|
{
|
||||||
const probeRun = run(['--probe']);
|
const probeRun = run(['--probe']);
|
||||||
assert.equal(probeRun.status, 0);
|
assert.equal(probeRun.status, 0);
|
||||||
@@ -129,7 +129,7 @@ const expectedNotice = enforcement === 'partial' ? `${PARTIAL_ENFORCEMENT_NOTICE
|
|||||||
const marker = path.join(os.tmpdir(), `nalr-should-not-exist-${process.pid}`);
|
const marker = path.join(os.tmpdir(), `nalr-should-not-exist-${process.pid}`);
|
||||||
const badGrant = run(['--ro', '/no/such/grant/root', '--', '/bin/sh', '-c', `echo x > ${marker}`]);
|
const badGrant = run(['--ro', '/no/such/grant/root', '--', '/bin/sh', '-c', `echo x > ${marker}`]);
|
||||||
assert.equal(badGrant.status, LAUNCHER_FAILURE_EXIT);
|
assert.equal(badGrant.status, LAUNCHER_FAILURE_EXIT);
|
||||||
assert.ok(badGrant.stderr.startsWith(LAUNCHER_FATAL_PREFIX));
|
assert.ok(badGrant.stderr.startsWith(FATAL_PREFIX));
|
||||||
assert.match(badGrant.stderr, /cannot open rule path/);
|
assert.match(badGrant.stderr, /cannot open rule path/);
|
||||||
assert.ok(!fs.existsSync(marker), 'the command must never run when the launcher fails');
|
assert.ok(!fs.existsSync(marker), 'the command must never run when the launcher fails');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,18 +9,16 @@ import { tmpdir } from 'node:os'
|
|||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { afterEach, describe, expect, it } from 'vitest'
|
import { afterEach, describe, expect, it } from 'vitest'
|
||||||
import { Context } from 'cordis'
|
import { Context } from 'cordis'
|
||||||
import {
|
import { LAUNCHER_FAILURE_EXIT } from 'node-addon-landlock-run'
|
||||||
LAUNCHER_FAILURE_EXIT,
|
|
||||||
LAUNCHER_FATAL_PREFIX,
|
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
} from 'node-addon-landlock-run'
|
|
||||||
import { SANDBOX_UNAVAILABLE } from '@deepseek-ai/dsh-sandbox'
|
import { SANDBOX_UNAVAILABLE } from '@deepseek-ai/dsh-sandbox'
|
||||||
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
import { LocalSandboxProvider } from '@deepseek-ai/dsh-sandbox-local'
|
||||||
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
import { SandboxPolicyService } from '@deepseek-ai/dsh-sandbox-policy'
|
||||||
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
import { SandboxBashExecutor } from '@deepseek-ai/dsh-bash-sandbox'
|
||||||
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
|
import LocalSubprocessService from '@deepseek-ai/dsh-subprocess-local'
|
||||||
|
|
||||||
const FATAL = `${LAUNCHER_FATAL_PREFIX}landlock ruleset error: Invalid argument`
|
const NOTICE = 'landlock-run: partial enforcement (older Landlock ABI)'
|
||||||
|
const FATAL_PREFIX = 'landlock-run: '
|
||||||
|
const FATAL = `${FATAL_PREFIX}landlock ruleset error: Invalid argument`
|
||||||
|
|
||||||
const contexts: Context[] = []
|
const contexts: Context[] = []
|
||||||
const tempDirs: string[] = []
|
const tempDirs: string[] = []
|
||||||
@@ -41,10 +39,10 @@ while [ "$#" -gt 0 ]; do
|
|||||||
case "$1" in
|
case "$1" in
|
||||||
--ro|--rw) shift 2 ;;
|
--ro|--rw) shift 2 ;;
|
||||||
--) shift; break ;;
|
--) shift; break ;;
|
||||||
*) printf '%s\\n' '${LAUNCHER_FATAL_PREFIX}usage error: unexpected fake argument' >&2; exit ${LAUNCHER_FAILURE_EXIT} ;;
|
*) printf '%s\\n' '${FATAL_PREFIX}usage error: unexpected fake argument' >&2; exit ${LAUNCHER_FAILURE_EXIT} ;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
printf '%s\\n' '${PARTIAL_ENFORCEMENT_NOTICE}' >&2
|
printf '%s\\n' '${NOTICE}' >&2
|
||||||
${fatalBranch}exec "$@"
|
${fatalBranch}exec "$@"
|
||||||
`, { mode: 0o755 })
|
`, { mode: 0o755 })
|
||||||
return launcher
|
return launcher
|
||||||
@@ -113,7 +111,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
const bash = await setup()
|
const bash = await setup()
|
||||||
const result = await bash.run(bash.resolve({ command: `exit ${exitCode}` }))
|
const result = await bash.run(bash.resolve({ command: `exit ${exitCode}` }))
|
||||||
expect(result.exitCode).toBe(exitCode)
|
expect(result.exitCode).toBe(exitCode)
|
||||||
expect(result.stderr.text).toBe(`${PARTIAL_ENFORCEMENT_NOTICE}\n`)
|
expect(result.stderr.text).toBe(`${NOTICE}\n`)
|
||||||
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -122,7 +120,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
const bash = await setup()
|
const bash = await setup()
|
||||||
const result = await bash.run(bash.resolve({ command: `exit ${exitCode}` }))
|
const result = await bash.run(bash.resolve({ command: `exit ${exitCode}` }))
|
||||||
expect(result.exitCode).toBe(exitCode)
|
expect(result.exitCode).toBe(exitCode)
|
||||||
expect(result.stderr.text).toBe(`${PARTIAL_ENFORCEMENT_NOTICE}\n`)
|
expect(result.stderr.text).toBe(`${NOTICE}\n`)
|
||||||
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -130,7 +128,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
const bash = await setup(exitCode)
|
const bash = await setup(exitCode)
|
||||||
const result = await bash.run(bash.resolve({ command: 'true' }))
|
const result = await bash.run(bash.resolve({ command: 'true' }))
|
||||||
expect(result.exitCode).toBe(exitCode)
|
expect(result.exitCode).toBe(exitCode)
|
||||||
expect(result.stderr.text).toBe(`${PARTIAL_ENFORCEMENT_NOTICE}\n${FATAL}\n`)
|
expect(result.stderr.text).toBe(`${NOTICE}\n${FATAL}\n`)
|
||||||
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
expect(result.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -140,13 +138,13 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
expect(error).toMatchObject({ name: 'SandboxUnavailableError', code: SANDBOX_UNAVAILABLE })
|
expect(error).toMatchObject({ name: 'SandboxUnavailableError', code: SANDBOX_UNAVAILABLE })
|
||||||
expect(error).toBeInstanceOf(Error)
|
expect(error).toBeInstanceOf(Error)
|
||||||
expect((error as Error).message).toContain(`Runner failure: ${FATAL}`)
|
expect((error as Error).message).toContain(`Runner failure: ${FATAL}`)
|
||||||
expect((error as Error).message).not.toContain(PARTIAL_ENFORCEMENT_NOTICE)
|
expect((error as Error).message).not.toContain(NOTICE)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('classifies a notice plus child Permission denied as a denial, not runner failure', async () => {
|
it('classifies a notice plus child Permission denied as a denial, not runner failure', async () => {
|
||||||
const bash = await setup()
|
const bash = await setup()
|
||||||
const result = await bash.run(bash.resolve({ command: 'printf "%s\\n" "child: Permission denied" >&2; exit 1' }))
|
const result = await bash.run(bash.resolve({ command: 'printf "%s\\n" "child: Permission denied" >&2; exit 1' }))
|
||||||
expect(result.stderr.text).toBe(`${PARTIAL_ENFORCEMENT_NOTICE}\nchild: Permission denied\n`)
|
expect(result.stderr.text).toBe(`${NOTICE}\nchild: Permission denied\n`)
|
||||||
expect(result.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'partial' })
|
expect(result.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'partial' })
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -156,7 +154,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
const task = bash.start(bash.resolve({ command }))
|
const task = bash.start(bash.resolve({ command }))
|
||||||
await task.done
|
await task.done
|
||||||
expect(task.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
expect(task.sandbox).toEqual({ mode: 'read-only', denied: false, enforcement: 'partial' })
|
||||||
expect(task.readOutput().delta).toContain(PARTIAL_ENFORCEMENT_NOTICE)
|
expect(task.readOutput().delta).toContain(NOTICE)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -165,7 +163,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
const task = bash.start(bash.resolve({ command: 'printf "%s\\n" "child: Permission denied" >&2; exit 1' }))
|
const task = bash.start(bash.resolve({ command: 'printf "%s\\n" "child: Permission denied" >&2; exit 1' }))
|
||||||
await task.done
|
await task.done
|
||||||
expect(task.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'partial' })
|
expect(task.sandbox).toEqual({ mode: 'read-only', denied: true, enforcement: 'partial' })
|
||||||
expect(task.readOutput().delta).toContain(PARTIAL_ENFORCEMENT_NOTICE)
|
expect(task.readOutput().delta).toContain(NOTICE)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('makes a background fatal line outrank denial text after the notice', async () => {
|
it('makes a background fatal line outrank denial text after the notice', async () => {
|
||||||
@@ -179,7 +177,7 @@ describe('partial Landlock runner-failure classification', () => {
|
|||||||
runnerFailed: true,
|
runnerFailed: true,
|
||||||
})
|
})
|
||||||
const output = task.readOutput().delta
|
const output = task.readOutput().delta
|
||||||
expect(output).toContain(PARTIAL_ENFORCEMENT_NOTICE)
|
expect(output).toContain(NOTICE)
|
||||||
expect(output).toContain(FATAL)
|
expect(output).toContain(FATAL)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,9 +8,8 @@
|
|||||||
|
|
||||||
import { spawnSync } from 'node:child_process'
|
import { spawnSync } from 'node:child_process'
|
||||||
import {
|
import {
|
||||||
LAUNCHER_FATAL_PREFIX,
|
LAUNCHER_BIN,
|
||||||
LAUNCHER_FAILURE_EXIT,
|
LAUNCHER_FAILURE_EXIT,
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
launcherPath as landlockLauncherPath,
|
launcherPath as landlockLauncherPath,
|
||||||
probe as defaultProbeLandlock,
|
probe as defaultProbeLandlock,
|
||||||
} from 'node-addon-landlock-run'
|
} from 'node-addon-landlock-run'
|
||||||
@@ -157,8 +156,8 @@ const RUNNER_FAILURE_RULES = {
|
|||||||
bwrap: [{ fatalSignatures: ['bwrap: '] }],
|
bwrap: [{ fatalSignatures: ['bwrap: '] }],
|
||||||
landlock: [{
|
landlock: [{
|
||||||
allowedExitCodes: [LAUNCHER_FAILURE_EXIT],
|
allowedExitCodes: [LAUNCHER_FAILURE_EXIT],
|
||||||
fatalSignatures: [LAUNCHER_FATAL_PREFIX],
|
fatalSignatures: [`${LAUNCHER_BIN}: `],
|
||||||
informationalLines: [PARTIAL_ENFORCEMENT_NOTICE],
|
informationalLines: [`${LAUNCHER_BIN}: partial enforcement (older Landlock ABI)`],
|
||||||
}],
|
}],
|
||||||
seatbelt: [{ fatalSignatures: ['sandbox-exec: '] }],
|
seatbelt: [{ fatalSignatures: ['sandbox-exec: '] }],
|
||||||
} as const satisfies Record<SelectedRunner['runner'], readonly RunnerFailureRule[]>
|
} as const satisfies Record<SelectedRunner['runner'], readonly RunnerFailureRule[]>
|
||||||
|
|||||||
@@ -12,11 +12,7 @@ import { tmpdir } from 'node:os'
|
|||||||
import { join } from 'node:path'
|
import { join } from 'node:path'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { Context } from 'cordis'
|
import { Context } from 'cordis'
|
||||||
import {
|
import { LAUNCHER_FAILURE_EXIT } from 'node-addon-landlock-run'
|
||||||
LAUNCHER_FAILURE_EXIT,
|
|
||||||
LAUNCHER_FATAL_PREFIX,
|
|
||||||
PARTIAL_ENFORCEMENT_NOTICE,
|
|
||||||
} from 'node-addon-landlock-run'
|
|
||||||
import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
|
import { SANDBOX_UNAVAILABLE, SandboxUnavailableError } from '@deepseek-ai/dsh-sandbox'
|
||||||
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
import type { SandboxPolicy } from '@deepseek-ai/dsh-sandbox'
|
||||||
import {
|
import {
|
||||||
@@ -179,8 +175,8 @@ describe('the platform chains', () => {
|
|||||||
denialSignatures: ['permission denied'],
|
denialSignatures: ['permission denied'],
|
||||||
runnerFailureRules: [{
|
runnerFailureRules: [{
|
||||||
allowedExitCodes: [LAUNCHER_FAILURE_EXIT],
|
allowedExitCodes: [LAUNCHER_FAILURE_EXIT],
|
||||||
fatalSignatures: [LAUNCHER_FATAL_PREFIX],
|
fatalSignatures: ['landlock-run: '],
|
||||||
informationalLines: [PARTIAL_ENFORCEMENT_NOTICE],
|
informationalLines: ['landlock-run: partial enforcement (older Landlock ABI)'],
|
||||||
}],
|
}],
|
||||||
})
|
})
|
||||||
expect(probeLandlock).toHaveBeenCalledWith(launcher)
|
expect(probeLandlock).toHaveBeenCalledWith(launcher)
|
||||||
|
|||||||
4
pnpm-lock.yaml
generated
4
pnpm-lock.yaml
generated
@@ -848,10 +848,6 @@ importers:
|
|||||||
'@deepseek-ai/dsh-workspace-context':
|
'@deepseek-ai/dsh-workspace-context':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../packages/context/workspace-context
|
version: link:../packages/context/workspace-context
|
||||||
node-addon-landlock-run:
|
|
||||||
specifier: 0.0.0-test.0
|
|
||||||
version: 0.0.0-test.0
|
|
||||||
|
|
||||||
packages/acp/acp:
|
packages/acp/acp:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@agentclientprotocol/sdk':
|
'@agentclientprotocol/sdk':
|
||||||
|
|||||||
Reference in New Issue
Block a user