@sysid/sandbox-runtime-improved 0.0.45-sysid.2 → 0.0.49-sysid.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.md +9 -30
- package/dist/sandbox/generate-seccomp-filter.d.ts +0 -51
- package/dist/sandbox/generate-seccomp-filter.d.ts.map +1 -1
- package/dist/sandbox/generate-seccomp-filter.js +0 -106
- package/dist/sandbox/generate-seccomp-filter.js.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.d.ts +4 -13
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/linux-sandbox-utils.js +84 -105
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.d.ts +1 -1
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -1
- package/dist/sandbox/macos-sandbox-utils.js +41 -20
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -1
- package/dist/sandbox/sandbox-config.d.ts +16 -12
- package/dist/sandbox/sandbox-config.d.ts.map +1 -1
- package/dist/sandbox/sandbox-config.js +17 -10
- package/dist/sandbox/sandbox-config.js.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts +1 -1
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.js +6 -9
- package/dist/sandbox/sandbox-manager.js.map +1 -1
- package/dist/vendor/seccomp/build.ts +91 -0
- package/dist/vendor/seccomp-src/apply-seccomp.c +18 -23
- package/package.json +3 -8
- package/vendor/seccomp/build.ts +91 -0
- package/vendor/seccomp-src/apply-seccomp.c +18 -23
- package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
- package/dist/vendor/seccomp/x64/unix-block.bpf +0 -0
- package/vendor/seccomp/arm64/apply-seccomp +0 -0
- package/vendor/seccomp/arm64/unix-block.bpf +0 -0
- package/vendor/seccomp/x64/apply-seccomp +0 -0
- package/vendor/seccomp/x64/unix-block.bpf +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* apply-seccomp.c - Apply seccomp BPF filter in an isolated PID namespace
|
|
3
3
|
*
|
|
4
|
-
* Usage: apply-seccomp <
|
|
4
|
+
* Usage: apply-seccomp <command> [args...]
|
|
5
5
|
*
|
|
6
|
-
* This program
|
|
6
|
+
* This program applies a baked-in seccomp BPF filter, isolates the
|
|
7
7
|
* target command in a nested user+PID+mount namespace so it cannot see or
|
|
8
8
|
* ptrace any process that lacks the filter, applies the filter with
|
|
9
9
|
* prctl(PR_SET_SECCOMP), and execs the command.
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
#include <linux/seccomp.h>
|
|
46
46
|
#include <linux/filter.h>
|
|
47
47
|
|
|
48
|
+
#include "unix-block-bpf.h"
|
|
49
|
+
|
|
48
50
|
#ifndef PR_SET_NO_NEW_PRIVS
|
|
49
51
|
#define PR_SET_NO_NEW_PRIVS 38
|
|
50
52
|
#endif
|
|
@@ -58,8 +60,6 @@
|
|
|
58
60
|
#define SECCOMP_MODE_FILTER 2
|
|
59
61
|
#endif
|
|
60
62
|
|
|
61
|
-
#define MAX_FILTER_SIZE 4096
|
|
62
|
-
|
|
63
63
|
static void die(const char *msg) {
|
|
64
64
|
perror(msg);
|
|
65
65
|
_exit(1);
|
|
@@ -144,29 +144,18 @@ static int reap_until(pid_t main_child) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
int main(int argc, char *argv[]) {
|
|
147
|
-
if (argc <
|
|
148
|
-
fprintf(stderr, "Usage: %s <
|
|
147
|
+
if (argc < 2) {
|
|
148
|
+
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
|
|
149
149
|
return 1;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
char **command_argv = &argv[2];
|
|
152
|
+
char **command_argv = &argv[1];
|
|
154
153
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (fd < 0) {
|
|
158
|
-
die("apply-seccomp: open(filter)");
|
|
159
|
-
}
|
|
160
|
-
static unsigned char filter_bytes[MAX_FILTER_SIZE];
|
|
161
|
-
ssize_t filter_size = read(fd, filter_bytes, MAX_FILTER_SIZE);
|
|
162
|
-
close(fd);
|
|
163
|
-
if (filter_size <= 0 || filter_size % 8 != 0) {
|
|
164
|
-
fprintf(stderr, "apply-seccomp: invalid BPF filter (size=%zd)\n", filter_size);
|
|
165
|
-
return 1;
|
|
166
|
-
}
|
|
154
|
+
_Static_assert(sizeof(unix_block_bpf) % sizeof(struct sock_filter) == 0,
|
|
155
|
+
"BPF filter size must be a multiple of sock_filter");
|
|
167
156
|
struct sock_fprog prog = {
|
|
168
|
-
.len = (unsigned short)(
|
|
169
|
-
.filter = (struct sock_filter *)
|
|
157
|
+
.len = (unsigned short)(sizeof(unix_block_bpf) / sizeof(struct sock_filter)),
|
|
158
|
+
.filter = (struct sock_filter *)unix_block_bpf,
|
|
170
159
|
};
|
|
171
160
|
|
|
172
161
|
/* ---- New PID + mount namespaces. Children (not us) enter the PID ns. ----
|
|
@@ -246,7 +235,13 @@ int main(int argc, char *argv[]) {
|
|
|
246
235
|
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) {
|
|
247
236
|
die("apply-seccomp: mount(MS_PRIVATE)");
|
|
248
237
|
}
|
|
249
|
-
|
|
238
|
+
/* EPERM here means a masked /proc is underneath (unprivileged Docker)
|
|
239
|
+
* and the kernel domination check refused the overmount. The nested
|
|
240
|
+
* userns above is the isolation boundary; this remount only hides
|
|
241
|
+
* outer PIDs from `ls /proc`. enableWeakerNestedSandbox targets
|
|
242
|
+
* exactly this environment. */
|
|
243
|
+
if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL) < 0
|
|
244
|
+
&& errno != EPERM) {
|
|
250
245
|
die("apply-seccomp: mount(/proc)");
|
|
251
246
|
}
|
|
252
247
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sysid/sandbox-runtime-improved",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.49-sysid.1",
|
|
4
4
|
"description": "Improved Anthropic Sandbox Runtime (ASRT) - A general-purpose tool for wrapping security boundaries around arbitrary processes (sysid fork with several improvements and bug fixes)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,24 +13,19 @@
|
|
|
13
13
|
},
|
|
14
14
|
"scripts": {
|
|
15
15
|
"build": "tsc",
|
|
16
|
-
"
|
|
17
|
-
"build:seccomp": "scripts/build-seccomp-binaries.sh",
|
|
16
|
+
"build:seccomp": "bun vendor/seccomp/build.ts",
|
|
18
17
|
"clean": "rm -rf dist",
|
|
19
18
|
"test": "bun test",
|
|
20
|
-
"test:unit": "bun test test/config-validation.test.ts test/sandbox/seccomp-filter.test.ts",
|
|
21
|
-
"test:integration": "bun test test/sandbox/integration.test.ts test/sandbox/allow-read.test.ts test/sandbox/wrap-with-sandbox.test.ts",
|
|
22
19
|
"typecheck": "tsc --noEmit",
|
|
23
20
|
"lint": "eslint 'src/**/*.ts' --fix --cache --cache-location=node_modules/.cache/.eslintcache",
|
|
24
21
|
"lint:check": "eslint 'src/**/*.ts' --cache --cache-location=node_modules/.cache/.eslintcache",
|
|
25
22
|
"format": "prettier --write 'src/**/*.ts' --cache --log-level warn",
|
|
26
|
-
"prepublishOnly": "npm run clean && npm run build",
|
|
23
|
+
"prepublishOnly": "npm run clean && npm run build && cp -r vendor dist/",
|
|
27
24
|
"prepare": "husky"
|
|
28
25
|
},
|
|
29
26
|
"dependencies": {
|
|
30
27
|
"@pondwader/socks5-server": "^1.0.10",
|
|
31
|
-
"@types/lodash-es": "^4.17.12",
|
|
32
28
|
"commander": "^12.1.0",
|
|
33
|
-
"lodash-es": "^4.17.23",
|
|
34
29
|
"shell-quote": "^1.8.3",
|
|
35
30
|
"zod": "^3.24.1"
|
|
36
31
|
},
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
3
|
+
import { dirname, join } from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
if (process.platform !== 'linux') {
|
|
7
|
+
console.error('seccomp build: Linux only')
|
|
8
|
+
process.exit(1)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const HERE = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const SRC = join(HERE, '..', 'seccomp-src')
|
|
13
|
+
|
|
14
|
+
const nodeArchToDir: Record<string, string> = { x64: 'x64', arm64: 'arm64' }
|
|
15
|
+
const arch = nodeArchToDir[process.arch]
|
|
16
|
+
if (!arch) {
|
|
17
|
+
console.error('seccomp build: unsupported arch ' + process.arch)
|
|
18
|
+
process.exit(1)
|
|
19
|
+
}
|
|
20
|
+
const OUT = join(HERE, arch)
|
|
21
|
+
|
|
22
|
+
function run(argv: string[]): void {
|
|
23
|
+
const [cmd, ...args] = argv
|
|
24
|
+
const r = spawnSync(cmd, args, { stdio: 'inherit' })
|
|
25
|
+
if (r.status !== 0) {
|
|
26
|
+
console.error(argv.join(' ') + ' exited ' + (r.status ?? r.signal))
|
|
27
|
+
process.exit(1)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function toCArray(bytes: Buffer): string {
|
|
32
|
+
const hex = Array.from(bytes, b => '0x' + b.toString(16).padStart(2, '0'))
|
|
33
|
+
const lines: string[] = []
|
|
34
|
+
for (let i = 0; i < hex.length; i += 8) {
|
|
35
|
+
lines.push(' ' + hex.slice(i, i + 8).join(', ') + ',')
|
|
36
|
+
}
|
|
37
|
+
return lines.join('\n')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
mkdirSync(OUT, { recursive: true })
|
|
41
|
+
|
|
42
|
+
const cflags = ['-static', '-O2', '-Wall', '-Wextra']
|
|
43
|
+
|
|
44
|
+
const gen = join(OUT, 'seccomp-unix-block')
|
|
45
|
+
run([
|
|
46
|
+
'gcc',
|
|
47
|
+
...cflags,
|
|
48
|
+
'-o',
|
|
49
|
+
gen,
|
|
50
|
+
join(SRC, 'seccomp-unix-block.c'),
|
|
51
|
+
'-lseccomp',
|
|
52
|
+
])
|
|
53
|
+
|
|
54
|
+
const bpf: Record<string, Buffer> = {}
|
|
55
|
+
for (const target of ['x86_64', 'aarch64']) {
|
|
56
|
+
const tmp = join(OUT, target + '.bpf')
|
|
57
|
+
run([gen, tmp, target])
|
|
58
|
+
bpf[target] = readFileSync(tmp)
|
|
59
|
+
rmSync(tmp)
|
|
60
|
+
}
|
|
61
|
+
rmSync(gen)
|
|
62
|
+
|
|
63
|
+
const header = join(OUT, 'unix-block-bpf.h')
|
|
64
|
+
writeFileSync(
|
|
65
|
+
header,
|
|
66
|
+
'#if defined(__x86_64__)\n' +
|
|
67
|
+
'static const unsigned char unix_block_bpf[] = {\n' +
|
|
68
|
+
toCArray(bpf.x86_64) +
|
|
69
|
+
'\n};\n' +
|
|
70
|
+
'#elif defined(__aarch64__)\n' +
|
|
71
|
+
'static const unsigned char unix_block_bpf[] = {\n' +
|
|
72
|
+
toCArray(bpf.aarch64) +
|
|
73
|
+
'\n};\n' +
|
|
74
|
+
'#else\n' +
|
|
75
|
+
'#error "unsupported architecture for unix-block BPF filter"\n' +
|
|
76
|
+
'#endif\n',
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
run([
|
|
80
|
+
'gcc',
|
|
81
|
+
...cflags,
|
|
82
|
+
'-I',
|
|
83
|
+
OUT,
|
|
84
|
+
'-o',
|
|
85
|
+
join(OUT, 'apply-seccomp'),
|
|
86
|
+
join(SRC, 'apply-seccomp.c'),
|
|
87
|
+
])
|
|
88
|
+
run(['strip', join(OUT, 'apply-seccomp')])
|
|
89
|
+
rmSync(header)
|
|
90
|
+
|
|
91
|
+
console.log('built ' + join(OUT, 'apply-seccomp'))
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/*
|
|
2
2
|
* apply-seccomp.c - Apply seccomp BPF filter in an isolated PID namespace
|
|
3
3
|
*
|
|
4
|
-
* Usage: apply-seccomp <
|
|
4
|
+
* Usage: apply-seccomp <command> [args...]
|
|
5
5
|
*
|
|
6
|
-
* This program
|
|
6
|
+
* This program applies a baked-in seccomp BPF filter, isolates the
|
|
7
7
|
* target command in a nested user+PID+mount namespace so it cannot see or
|
|
8
8
|
* ptrace any process that lacks the filter, applies the filter with
|
|
9
9
|
* prctl(PR_SET_SECCOMP), and execs the command.
|
|
@@ -45,6 +45,8 @@
|
|
|
45
45
|
#include <linux/seccomp.h>
|
|
46
46
|
#include <linux/filter.h>
|
|
47
47
|
|
|
48
|
+
#include "unix-block-bpf.h"
|
|
49
|
+
|
|
48
50
|
#ifndef PR_SET_NO_NEW_PRIVS
|
|
49
51
|
#define PR_SET_NO_NEW_PRIVS 38
|
|
50
52
|
#endif
|
|
@@ -58,8 +60,6 @@
|
|
|
58
60
|
#define SECCOMP_MODE_FILTER 2
|
|
59
61
|
#endif
|
|
60
62
|
|
|
61
|
-
#define MAX_FILTER_SIZE 4096
|
|
62
|
-
|
|
63
63
|
static void die(const char *msg) {
|
|
64
64
|
perror(msg);
|
|
65
65
|
_exit(1);
|
|
@@ -144,29 +144,18 @@ static int reap_until(pid_t main_child) {
|
|
|
144
144
|
}
|
|
145
145
|
|
|
146
146
|
int main(int argc, char *argv[]) {
|
|
147
|
-
if (argc <
|
|
148
|
-
fprintf(stderr, "Usage: %s <
|
|
147
|
+
if (argc < 2) {
|
|
148
|
+
fprintf(stderr, "Usage: %s <command> [args...]\n", argv[0]);
|
|
149
149
|
return 1;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
char **command_argv = &argv[2];
|
|
152
|
+
char **command_argv = &argv[1];
|
|
154
153
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if (fd < 0) {
|
|
158
|
-
die("apply-seccomp: open(filter)");
|
|
159
|
-
}
|
|
160
|
-
static unsigned char filter_bytes[MAX_FILTER_SIZE];
|
|
161
|
-
ssize_t filter_size = read(fd, filter_bytes, MAX_FILTER_SIZE);
|
|
162
|
-
close(fd);
|
|
163
|
-
if (filter_size <= 0 || filter_size % 8 != 0) {
|
|
164
|
-
fprintf(stderr, "apply-seccomp: invalid BPF filter (size=%zd)\n", filter_size);
|
|
165
|
-
return 1;
|
|
166
|
-
}
|
|
154
|
+
_Static_assert(sizeof(unix_block_bpf) % sizeof(struct sock_filter) == 0,
|
|
155
|
+
"BPF filter size must be a multiple of sock_filter");
|
|
167
156
|
struct sock_fprog prog = {
|
|
168
|
-
.len = (unsigned short)(
|
|
169
|
-
.filter = (struct sock_filter *)
|
|
157
|
+
.len = (unsigned short)(sizeof(unix_block_bpf) / sizeof(struct sock_filter)),
|
|
158
|
+
.filter = (struct sock_filter *)unix_block_bpf,
|
|
170
159
|
};
|
|
171
160
|
|
|
172
161
|
/* ---- New PID + mount namespaces. Children (not us) enter the PID ns. ----
|
|
@@ -246,7 +235,13 @@ int main(int argc, char *argv[]) {
|
|
|
246
235
|
if (mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, NULL) < 0) {
|
|
247
236
|
die("apply-seccomp: mount(MS_PRIVATE)");
|
|
248
237
|
}
|
|
249
|
-
|
|
238
|
+
/* EPERM here means a masked /proc is underneath (unprivileged Docker)
|
|
239
|
+
* and the kernel domination check refused the overmount. The nested
|
|
240
|
+
* userns above is the isolation boundary; this remount only hides
|
|
241
|
+
* outer PIDs from `ls /proc`. enableWeakerNestedSandbox targets
|
|
242
|
+
* exactly this environment. */
|
|
243
|
+
if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL) < 0
|
|
244
|
+
&& errno != EPERM) {
|
|
250
245
|
die("apply-seccomp: mount(/proc)");
|
|
251
246
|
}
|
|
252
247
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|