@aalis/plugin-process-local 0.2.0 → 0.4.0
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/dist/sandbox.d.ts +21 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +83 -0
- package/dist/sandbox.js.map +1 -0
- package/package.json +6 -5
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { SandboxPolicy } from '@aalis/plugin-process-api';
|
|
2
|
+
export type SandboxBackend = 'seatbelt' | 'bwrap' | 'none';
|
|
3
|
+
export interface SandboxWrap {
|
|
4
|
+
cmd: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 按平台 + 命令存在性探测可用后端(纯函数;commandExists 注入便于测试)。
|
|
9
|
+
* 注意:仅判「启动器是否存在」,bwrap 是否真能跑(unprivileged userns)由调用方再做功能性 smoke。
|
|
10
|
+
*/
|
|
11
|
+
export declare function detectSandboxBackend(platform: NodeJS.Platform, commandExists: (cmd: string) => boolean): SandboxBackend;
|
|
12
|
+
/**
|
|
13
|
+
* 生成 macOS Seatbelt (SBPL) profile:默认拒绝;放开 进程/读/常见系统调用;
|
|
14
|
+
* 写仅限 fsWrite + /dev 标准设备;网络按 policy。
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildSeatbeltProfile(policy: SandboxPolicy): string;
|
|
17
|
+
/** 生成 Linux bubblewrap 参数:只读绑定根(可读+取解释器/库)、rw 绑定写白名单、按 policy 隔离网络。 */
|
|
18
|
+
export declare function buildBwrapArgs(policy: SandboxPolicy, cmd: string, args: readonly string[], cwd?: string): SandboxWrap;
|
|
19
|
+
/** 按后端把 (cmd,args) 改写为「经沙箱启动器运行」。backend==='none' 不应调用(调用方先 fail-closed)。 */
|
|
20
|
+
export declare function wrapForSandbox(backend: SandboxBackend, policy: SandboxPolicy, cmd: string, args: readonly string[], cwd?: string): SandboxWrap;
|
|
21
|
+
//# sourceMappingURL=sandbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,OAAO,GAAG,MAAM,CAAC;AAE3D,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,MAAM,CAAC,QAAQ,EACzB,aAAa,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GACtC,cAAc,CAIhB;AAOD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAgBlE;AAED,uEAAuE;AACvE,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,CAAC,EAAE,MAAM,GACX,WAAW,CAqBb;AAED,6EAA6E;AAC7E,wBAAgB,cAAc,CAC5B,OAAO,EAAE,cAAc,EACvB,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,GAAG,CAAC,EAAE,MAAM,GACX,WAAW,CAQb"}
|
package/dist/sandbox.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// sandbox.ts — 子进程 OS 沙箱:后端探测 + 命令改写(纯逻辑,便于单测)
|
|
3
|
+
//
|
|
4
|
+
// 把不可信代码的子进程包到 OS 沙箱启动器里运行(仍 shell-free):
|
|
5
|
+
// - macOS → sandbox-exec -p '<SBPL>' <cmd> <args>
|
|
6
|
+
// - Linux → bwrap <flags> -- <cmd> <args>
|
|
7
|
+
// v1 语义:读放开(解释器需系统库)、写限定 fsWrite、网络粗粒度开关。
|
|
8
|
+
// 实际可用性探测(含功能性 smoke)在 index.ts 做;本文件只做纯生成/判定。
|
|
9
|
+
// ============================================================
|
|
10
|
+
/**
|
|
11
|
+
* 按平台 + 命令存在性探测可用后端(纯函数;commandExists 注入便于测试)。
|
|
12
|
+
* 注意:仅判「启动器是否存在」,bwrap 是否真能跑(unprivileged userns)由调用方再做功能性 smoke。
|
|
13
|
+
*/
|
|
14
|
+
export function detectSandboxBackend(platform, commandExists) {
|
|
15
|
+
if (platform === 'darwin')
|
|
16
|
+
return commandExists('sandbox-exec') ? 'seatbelt' : 'none';
|
|
17
|
+
if (platform === 'linux')
|
|
18
|
+
return commandExists('bwrap') ? 'bwrap' : 'none';
|
|
19
|
+
return 'none';
|
|
20
|
+
}
|
|
21
|
+
/** SBPL 字符串字面量转义 */
|
|
22
|
+
function sbplQuote(p) {
|
|
23
|
+
return `"${p.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* 生成 macOS Seatbelt (SBPL) profile:默认拒绝;放开 进程/读/常见系统调用;
|
|
27
|
+
* 写仅限 fsWrite + /dev 标准设备;网络按 policy。
|
|
28
|
+
*/
|
|
29
|
+
export function buildSeatbeltProfile(policy) {
|
|
30
|
+
const lines = [
|
|
31
|
+
'(version 1)',
|
|
32
|
+
'(deny default)',
|
|
33
|
+
'(allow process-fork)',
|
|
34
|
+
'(allow process-exec)',
|
|
35
|
+
'(allow file-read*)',
|
|
36
|
+
'(allow sysctl-read)',
|
|
37
|
+
'(allow mach-lookup)',
|
|
38
|
+
'(allow ipc-posix-shm)',
|
|
39
|
+
'(allow signal)',
|
|
40
|
+
'(allow file-write-data (literal "/dev/null") (literal "/dev/stdout") (literal "/dev/stderr") (literal "/dev/dtracehelper") (literal "/dev/urandom"))',
|
|
41
|
+
];
|
|
42
|
+
for (const dir of policy.fsWrite)
|
|
43
|
+
lines.push(`(allow file-write* (subpath ${sbplQuote(dir)}))`);
|
|
44
|
+
lines.push(policy.network === 'allow' ? '(allow network*)' : '(deny network*)');
|
|
45
|
+
return `${lines.join('\n')}\n`;
|
|
46
|
+
}
|
|
47
|
+
/** 生成 Linux bubblewrap 参数:只读绑定根(可读+取解释器/库)、rw 绑定写白名单、按 policy 隔离网络。 */
|
|
48
|
+
export function buildBwrapArgs(policy, cmd, args, cwd) {
|
|
49
|
+
const a = [
|
|
50
|
+
'--ro-bind',
|
|
51
|
+
'/',
|
|
52
|
+
'/',
|
|
53
|
+
'--dev',
|
|
54
|
+
'/dev',
|
|
55
|
+
'--proc',
|
|
56
|
+
'/proc',
|
|
57
|
+
'--tmpfs',
|
|
58
|
+
'/tmp', // 私有可写 /tmp(解释器/库常用);写白名单的 bind 在其后,故仍可覆盖回真实子目录
|
|
59
|
+
'--unshare-all', // 含 net 命名空间隔离(断网)
|
|
60
|
+
'--die-with-parent',
|
|
61
|
+
'--new-session',
|
|
62
|
+
];
|
|
63
|
+
if (policy.network === 'allow')
|
|
64
|
+
a.push('--share-net');
|
|
65
|
+
// rw 绑定写白名单(顺序在 --tmpfs /tmp 之后,确保 /tmp 下的真实临时目录被重新暴露为可写)
|
|
66
|
+
for (const dir of policy.fsWrite)
|
|
67
|
+
a.push('--bind', dir, dir);
|
|
68
|
+
if (cwd)
|
|
69
|
+
a.push('--chdir', cwd);
|
|
70
|
+
a.push('--', cmd, ...args);
|
|
71
|
+
return { cmd: 'bwrap', args: a };
|
|
72
|
+
}
|
|
73
|
+
/** 按后端把 (cmd,args) 改写为「经沙箱启动器运行」。backend==='none' 不应调用(调用方先 fail-closed)。 */
|
|
74
|
+
export function wrapForSandbox(backend, policy, cmd, args, cwd) {
|
|
75
|
+
if (backend === 'seatbelt') {
|
|
76
|
+
return { cmd: 'sandbox-exec', args: ['-p', buildSeatbeltProfile(policy), cmd, ...args] };
|
|
77
|
+
}
|
|
78
|
+
if (backend === 'bwrap') {
|
|
79
|
+
return buildBwrapArgs(policy, cmd, args, cwd);
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`不支持的沙箱后端: ${backend}`);
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../src/sandbox.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aalis/plugin-process-local",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "进程网关本地实现:受控执行子进程",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"keywords": [
|
|
8
|
+
"aalis",
|
|
8
9
|
"aalis-plugin"
|
|
9
10
|
],
|
|
10
11
|
"main": "dist/index.js",
|
|
@@ -13,16 +14,16 @@
|
|
|
13
14
|
"dist"
|
|
14
15
|
],
|
|
15
16
|
"dependencies": {
|
|
16
|
-
"@aalis/plugin-
|
|
17
|
-
"@aalis/plugin-
|
|
17
|
+
"@aalis/plugin-storage-api": "^0.4.0",
|
|
18
|
+
"@aalis/plugin-process-api": "^0.4.0"
|
|
18
19
|
},
|
|
19
20
|
"devDependencies": {
|
|
20
21
|
"@types/node": "^22.0.0",
|
|
21
22
|
"typescript": "^5.7.0",
|
|
22
|
-
"@aalis/core": "0.
|
|
23
|
+
"@aalis/core": "0.4.0"
|
|
23
24
|
},
|
|
24
25
|
"peerDependencies": {
|
|
25
|
-
"@aalis/core": "
|
|
26
|
+
"@aalis/core": ">=0.2.0 <1.0.0"
|
|
26
27
|
},
|
|
27
28
|
"aalis": {
|
|
28
29
|
"service": {
|