@deepseek-ai/dsh-bash-sandbox 0.1.6-alpha.1 → 0.1.7-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +3 -3
- package/README.zh.md +3 -3
- package/lib/index.js +58 -63
- package/lib/types/index.d.ts +8 -3
- package/package.json +14 -14
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/shell/bash-sandbox/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: fea09e4ce13b0e7bd2841ff069c2aa61b30731bd
|
|
6
|
+
README.zh.md: 5daed6969cf1e66cff2241423d010aa0b860beb5
|
package/README.md
CHANGED
|
@@ -81,14 +81,14 @@ The executor is the sandboxing Service Provider for the `ctx.shell` seam: it inh
|
|
|
81
81
|
|
|
82
82
|
| File | Role |
|
|
83
83
|
|---|---|
|
|
84
|
-
| [`src/index.ts`](src/index.ts) | Plugin entry: `SandboxBashExecutor`, per-process fact retention,
|
|
84
|
+
| [`src/index.ts`](src/index.ts) | Plugin entry: `SandboxBashExecutor`, per-process fact retention, execution preparation |
|
|
85
85
|
| [`src/helpers.ts`](src/helpers.ts) | Denial, runner-failure, and runner-spawn-failure classification |
|
|
86
86
|
| — | No runtime invariant companion is published; classification is observable in results, and this package exposes no independent event sequence or mutable data relation beyond contracts enforced at its owning seam. |
|
|
87
87
|
| `tests/` | Exercised behavior across the bwrap, Landlock, and Seatbelt runners |
|
|
88
88
|
|
|
89
89
|
### Main flow
|
|
90
90
|
|
|
91
|
-
For a confined mode, `resolve()` stamps the per-call policy (the session's mode override, or the deployment fallback); `
|
|
91
|
+
For a confined mode, `resolve()` stamps the per-call policy (the session's mode override, or the deployment fallback); `execute` awaits preparation of the bash argv through the provider and hands the confined argv to the inherited subprocess path. At settlement the executor classifies the outcome: a runner failure outranks a denial because the command never ran, a failed run whose stderr carries the backend's denial dialect is reported `denied: true`, and every confined run carries its mode and enforcement facts. `danger-full-access` bypasses the provider entirely and stamps `denied: false`.
|
|
92
92
|
|
|
93
93
|
### Invariants
|
|
94
94
|
|
|
@@ -170,7 +170,7 @@ These limits define when this executor is not a general security boundary. They
|
|
|
170
170
|
|
|
171
171
|
- **Confinement covers file effects only** — network restriction and a uniform process-visibility guarantee are absent, so the modes are not a general-purpose security sandbox.
|
|
172
172
|
- **Denials are inferred from failed-command stderr** — backend signatures make the inference portable, but a matching application error can be classified as a denial and a denial omitted from the retained tail can be missed.
|
|
173
|
-
- **An asynchronously observed background runner failure has no immediate error channel** — it is recorded on the settled process and surfaces when the caller reads the generic task with `job_output`; a synchronous subprocess throw
|
|
173
|
+
- **An asynchronously observed background runner failure has no immediate error channel** — it is recorded on the settled process and surfaces when the caller reads the generic task with `job_output`; a synchronous subprocess throw is contained into the same settled-killed handle, with the runner-attributed failure carried by the `result()` rejection.
|
|
174
174
|
- **`danger-full-access` deliberately bypasses `ctx.sandbox`** — it is an explicit unconfined mode, not a wider sandbox profile.
|
|
175
175
|
|
|
176
176
|
<a id="dev-note"></a>
|
package/README.zh.md
CHANGED
|
@@ -81,14 +81,14 @@ kind: "package-reference"
|
|
|
81
81
|
|
|
82
82
|
| 文件 | 职责 |
|
|
83
83
|
|---|---|
|
|
84
|
-
| [`src/index.ts`](src/index.ts) | 插件入口:`SandboxBashExecutor`、按进程保留事实、
|
|
84
|
+
| [`src/index.ts`](src/index.ts) | 插件入口:`SandboxBashExecutor`、按进程保留事实、execute 包装 |
|
|
85
85
|
| [`src/helpers.ts`](src/helpers.ts) | 拒绝、runner 失败与 runner spawn 失败分类 |
|
|
86
86
|
| — | 不发布运行时不变式伴生入口;分类可在结果中观察,且除归属 seam 所强制执行的约定外,本包不公开独立事件序列或可变数据关系。 |
|
|
87
87
|
| `tests/` | 跨 bwrap、Landlock 与 Seatbelt runner 演练的行为 |
|
|
88
88
|
|
|
89
89
|
### 主要流程
|
|
90
90
|
|
|
91
|
-
对受限模式,`resolve()` 标记每次调用的策略(会话的模式覆盖值,或部署回退);`
|
|
91
|
+
对受限模式,`resolve()` 标记每次调用的策略(会话的模式覆盖值,或部署回退);`execute` 把 bash argv 经提供方包装,再把受限 argv 交给继承的 subprocess 路径。结算时执行器对结果分类:runner 失败优先于拒绝(命令从未运行),stderr 携带后端拒绝方言的失败运行报告 `denied: true`,每次受限运行都携带模式与强制执行事实。`danger-full-access` 完全绕过提供方,并标记 `denied: false`。
|
|
92
92
|
|
|
93
93
|
### 不变式
|
|
94
94
|
|
|
@@ -170,7 +170,7 @@ kind: "package-reference"
|
|
|
170
170
|
|
|
171
171
|
- **限制只覆盖文件影响**——不提供网络限制和统一的进程可见性保证,因此这些模式不是通用安全沙箱。
|
|
172
172
|
- **拒绝从失败命令的 stderr 推断**——后端特征使该推断可跨平台使用,但包含相同特征的应用错误可能被分类为拒绝,也可能遗漏未出现在保留尾部中的拒绝。
|
|
173
|
-
- **异步观测到的后台 runner 失败没有即时错误通道**——它记录在已结算进程上,并在调用方用 `job_output`
|
|
173
|
+
- **异步观测到的后台 runner 失败没有即时错误通道**——它记录在已结算进程上,并在调用方用 `job_output` 读取通用任务时呈现;同步的子进程抛错被收容为同样的已结算 killed 句柄,可归因于 runner 的失败由 `result()` 的 rejection 携带。
|
|
174
174
|
- **`danger-full-access` 有意绕过 `ctx.sandbox`**——它是显式无约束模式,不是更宽的沙箱 profile。
|
|
175
175
|
|
|
176
176
|
<a id="dev-note"></a>
|
package/lib/index.js
CHANGED
|
@@ -29,7 +29,7 @@ function classifyDenial(result, signatures) {
|
|
|
29
29
|
* calls fall back to deployment policy. `result.sandbox` reports the mode and
|
|
30
30
|
* enforcement actually used.
|
|
31
31
|
*/
|
|
32
|
-
var SandboxBashExecutor = class extends LocalBashExecutor {
|
|
32
|
+
var SandboxBashExecutor = class SandboxBashExecutor extends LocalBashExecutor {
|
|
33
33
|
static inject = [
|
|
34
34
|
"subprocess",
|
|
35
35
|
"sandbox",
|
|
@@ -62,79 +62,74 @@ var SandboxBashExecutor = class extends LocalBashExecutor {
|
|
|
62
62
|
sandboxPolicy: request.sandboxPolicy ?? this.ctx.sandboxPolicy.resolve()
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
|
-
async
|
|
65
|
+
async execute(spec) {
|
|
66
66
|
const policy = spec.sandboxPolicy;
|
|
67
67
|
const { mode } = policy;
|
|
68
|
-
if (mode === "danger-full-access") return {
|
|
69
|
-
...
|
|
68
|
+
if (mode === "danger-full-access") return SandboxBashExecutor.decorateResult(await super.execute(spec), (result) => ({
|
|
69
|
+
...result,
|
|
70
70
|
sandbox: {
|
|
71
71
|
mode,
|
|
72
72
|
denied: false
|
|
73
73
|
}
|
|
74
|
-
};
|
|
74
|
+
}));
|
|
75
75
|
let confined;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
76
|
+
const ex = await this.executeArgv(spec, async (signal) => {
|
|
77
|
+
const prepared = await this.confine(spec.command, {
|
|
78
|
+
...policy,
|
|
79
|
+
mode
|
|
80
|
+
}, signal);
|
|
81
|
+
signal.throwIfAborted();
|
|
82
|
+
confined = prepared;
|
|
83
|
+
return prepared.argv;
|
|
84
|
+
}, (process) => {
|
|
85
|
+
const facts = confined;
|
|
86
|
+
this.processFacts.set(process, {
|
|
87
|
+
mode,
|
|
88
|
+
enforcement: facts.enforcement,
|
|
89
|
+
denialSignatures: facts.denialSignatures,
|
|
90
|
+
runnerFailureRules: facts.runnerFailureRules,
|
|
91
|
+
runnerProgram: facts.argv[0],
|
|
92
|
+
workdir: spec.workdir
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
return SandboxBashExecutor.decorateResult(ex, (result) => {
|
|
96
|
+
if (confined === void 0) return {
|
|
97
|
+
...result,
|
|
98
|
+
sandbox: {
|
|
99
|
+
mode,
|
|
100
|
+
denied: false
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const { enforcement, denialSignatures, runnerFailureRules } = confined;
|
|
104
|
+
const runnerFailure = classifyRunnerFailure(result.exitCode, result.stderr.text, runnerFailureRules);
|
|
105
|
+
if (runnerFailure !== void 0) throw new SandboxUnavailableError(mode, runnerFailure.detail);
|
|
106
|
+
return {
|
|
107
|
+
...result,
|
|
108
|
+
sandbox: {
|
|
109
|
+
mode,
|
|
110
|
+
denied: classifyDenial(result, denialSignatures),
|
|
111
|
+
enforcement
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
}, (error) => {
|
|
89
115
|
if (spec.signal?.aborted === true) spec.signal.throwIfAborted();
|
|
90
116
|
if (confined !== void 0 && isRunnerSpawnFailure(error, confined.argv[0], spec.workdir)) throw new SandboxUnavailableError(mode, String(error));
|
|
91
117
|
throw error;
|
|
92
|
-
}
|
|
93
|
-
if (!spawnRequested) return {
|
|
94
|
-
...result,
|
|
95
|
-
sandbox: {
|
|
96
|
-
mode,
|
|
97
|
-
denied: false
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
const facts = confined;
|
|
101
|
-
const runnerFailure = classifyRunnerFailure(result.exitCode, result.stderr.text, facts.runnerFailureRules);
|
|
102
|
-
if (runnerFailure !== void 0) throw new SandboxUnavailableError(mode, runnerFailure.detail);
|
|
103
|
-
return {
|
|
104
|
-
...result,
|
|
105
|
-
sandbox: {
|
|
106
|
-
mode,
|
|
107
|
-
denied: classifyDenial(result, facts.denialSignatures),
|
|
108
|
-
enforcement: facts.enforcement
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
async start(spec) {
|
|
113
|
-
const policy = spec.sandboxPolicy;
|
|
114
|
-
const { mode } = policy;
|
|
115
|
-
if (mode === "danger-full-access") return super.start(spec);
|
|
116
|
-
const confined = await this.confine(spec.command, {
|
|
117
|
-
...policy,
|
|
118
|
-
mode
|
|
119
|
-
}, spec.signal);
|
|
120
|
-
spec.signal?.throwIfAborted();
|
|
121
|
-
let proc;
|
|
122
|
-
try {
|
|
123
|
-
proc = this.startArgv(spec, confined.argv);
|
|
124
|
-
} catch (error) {
|
|
125
|
-
if (isRunnerSpawnFailure(error, confined.argv[0], spec.workdir)) throw new SandboxUnavailableError(mode, String(error));
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
const { enforcement, denialSignatures, runnerFailureRules } = confined;
|
|
129
|
-
this.processFacts.set(proc, {
|
|
130
|
-
mode,
|
|
131
|
-
enforcement,
|
|
132
|
-
denialSignatures,
|
|
133
|
-
runnerFailureRules,
|
|
134
|
-
runnerProgram: confined.argv[0],
|
|
135
|
-
workdir: spec.workdir
|
|
136
118
|
});
|
|
137
|
-
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Decorate the handle's foreground projection in place, memoized once. The
|
|
122
|
+
* handle keeps its identity (never wrapped in a second object) because the
|
|
123
|
+
* per-process facts and `onProcessDone` key on the exact instance.
|
|
124
|
+
*/
|
|
125
|
+
static decorateResult(ex, map, mapError) {
|
|
126
|
+
const base = ex.result.bind(ex);
|
|
127
|
+
let decorated;
|
|
128
|
+
ex.result = () => {
|
|
129
|
+
decorated ??= base().then(map, mapError);
|
|
130
|
+
return decorated;
|
|
131
|
+
};
|
|
132
|
+
return ex;
|
|
138
133
|
}
|
|
139
134
|
/**
|
|
140
135
|
* Stamp per-process sandbox facts before `done` settles. Full-access processes
|
package/lib/types/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* @module @deepseek-ai/dsh-bash-sandbox
|
|
10
10
|
*/
|
|
11
11
|
import { Context } from '@deepseek-ai/cordis';
|
|
12
|
-
import type { ShellExecRequest, ShellExecSpec,
|
|
12
|
+
import type { ShellExecRequest, ShellExecSpec, ShellExecution, ShellProcess } from '@deepseek-ai/dsh-shell';
|
|
13
13
|
import type { SandboxMode } from '@deepseek-ai/dsh-sandbox';
|
|
14
14
|
import { LocalBashExecutor } from '@deepseek-ai/dsh-bash-local';
|
|
15
15
|
import type { Config as LocalConfig } from '@deepseek-ai/dsh-bash-local';
|
|
@@ -47,8 +47,13 @@ export declare class SandboxBashExecutor extends LocalBashExecutor {
|
|
|
47
47
|
* the deployment policy.
|
|
48
48
|
*/
|
|
49
49
|
resolve(request: ShellExecRequest): ShellExecSpec;
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
execute(spec: ShellExecSpec): Promise<ShellExecution>;
|
|
51
|
+
/**
|
|
52
|
+
* Decorate the handle's foreground projection in place, memoized once. The
|
|
53
|
+
* handle keeps its identity (never wrapped in a second object) because the
|
|
54
|
+
* per-process facts and `onProcessDone` key on the exact instance.
|
|
55
|
+
*/
|
|
56
|
+
private static decorateResult;
|
|
52
57
|
/**
|
|
53
58
|
* Stamp per-process sandbox facts before `done` settles. Full-access processes
|
|
54
59
|
* have no facts; signal deaths are not denials.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-bash-sandbox",
|
|
3
3
|
"description": "Sandbox-consuming implementation of the DeepSeek Harness bash executor seam (confines every command via ctx.sandbox, reports denial/enforcement result facts)",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.7-alpha.1",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,21 +27,21 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/dsh-shell": "^0.1.
|
|
31
|
-
"@deepseek-ai/dsh-bash-local": "^0.1.
|
|
32
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
33
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.
|
|
34
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
30
|
+
"@deepseek-ai/dsh-shell": "^0.1.7-alpha.1",
|
|
31
|
+
"@deepseek-ai/dsh-bash-local": "^0.1.7-alpha.1",
|
|
32
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.7-alpha.1",
|
|
33
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.7-alpha.1",
|
|
34
|
+
"@deepseek-ai/cordis": "^4.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@deepseek-ai/dsh-shell": "^0.1.
|
|
38
|
-
"@deepseek-ai/dsh-bash-local": "^0.1.
|
|
39
|
-
"@deepseek-ai/dsh-subprocess-local": "^0.1.
|
|
40
|
-
"@deepseek-ai/dsh-sandbox": "^0.1.
|
|
41
|
-
"@deepseek-ai/dsh-sandbox-local": "^0.1.
|
|
42
|
-
"@deepseek-ai/dsh-sandbox-policy": "^0.1.
|
|
43
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
37
|
+
"@deepseek-ai/dsh-shell": "^0.1.7-alpha.1",
|
|
38
|
+
"@deepseek-ai/dsh-bash-local": "^0.1.7-alpha.1",
|
|
39
|
+
"@deepseek-ai/dsh-subprocess-local": "^0.1.7-alpha.1",
|
|
40
|
+
"@deepseek-ai/dsh-sandbox": "^0.1.7-alpha.1",
|
|
41
|
+
"@deepseek-ai/dsh-sandbox-local": "^0.1.7-alpha.1",
|
|
42
|
+
"@deepseek-ai/dsh-sandbox-policy": "^0.1.7-alpha.1",
|
|
43
|
+
"@deepseek-ai/cordis": "^4.0.3",
|
|
44
44
|
"@deepseek-ai/node-addon-system": "^0.1.2",
|
|
45
|
-
"@deepseek-ai/dsh-session-projection": "^0.1.
|
|
45
|
+
"@deepseek-ai/dsh-session-projection": "^0.1.7-alpha.1"
|
|
46
46
|
}
|
|
47
47
|
}
|