@voratiq/sandbox-runtime 0.7.0-voratiq1

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/LICENSE +201 -0
  3. package/NOTICE +11 -0
  4. package/README.md +17 -0
  5. package/dist/cli.d.ts +3 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +243 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/index.d.ts +8 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +7 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/sandbox/generate-seccomp-filter.d.ts +56 -0
  14. package/dist/sandbox/generate-seccomp-filter.d.ts.map +1 -0
  15. package/dist/sandbox/generate-seccomp-filter.js +158 -0
  16. package/dist/sandbox/generate-seccomp-filter.js.map +1 -0
  17. package/dist/sandbox/http-proxy.d.ts +12 -0
  18. package/dist/sandbox/http-proxy.d.ts.map +1 -0
  19. package/dist/sandbox/http-proxy.js +489 -0
  20. package/dist/sandbox/http-proxy.js.map +1 -0
  21. package/dist/sandbox/linux-sandbox-utils.d.ts +111 -0
  22. package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -0
  23. package/dist/sandbox/linux-sandbox-utils.js +518 -0
  24. package/dist/sandbox/linux-sandbox-utils.js.map +1 -0
  25. package/dist/sandbox/macos-sandbox-utils.d.ts +54 -0
  26. package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -0
  27. package/dist/sandbox/macos-sandbox-utils.js +559 -0
  28. package/dist/sandbox/macos-sandbox-utils.js.map +1 -0
  29. package/dist/sandbox/sandbox-config.d.ts +170 -0
  30. package/dist/sandbox/sandbox-config.d.ts.map +1 -0
  31. package/dist/sandbox/sandbox-config.js +126 -0
  32. package/dist/sandbox/sandbox-config.js.map +1 -0
  33. package/dist/sandbox/sandbox-manager.d.ts +35 -0
  34. package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
  35. package/dist/sandbox/sandbox-manager.js +666 -0
  36. package/dist/sandbox/sandbox-manager.js.map +1 -0
  37. package/dist/sandbox/sandbox-schemas.d.ts +17 -0
  38. package/dist/sandbox/sandbox-schemas.d.ts.map +1 -0
  39. package/dist/sandbox/sandbox-schemas.js +2 -0
  40. package/dist/sandbox/sandbox-schemas.js.map +1 -0
  41. package/dist/sandbox/sandbox-utils.d.ts +53 -0
  42. package/dist/sandbox/sandbox-utils.d.ts.map +1 -0
  43. package/dist/sandbox/sandbox-utils.js +368 -0
  44. package/dist/sandbox/sandbox-utils.js.map +1 -0
  45. package/dist/sandbox/sandbox-violation-store.d.ts +19 -0
  46. package/dist/sandbox/sandbox-violation-store.d.ts.map +1 -0
  47. package/dist/sandbox/sandbox-violation-store.js +54 -0
  48. package/dist/sandbox/sandbox-violation-store.js.map +1 -0
  49. package/dist/sandbox/socks-proxy.d.ts +18 -0
  50. package/dist/sandbox/socks-proxy.d.ts.map +1 -0
  51. package/dist/sandbox/socks-proxy.js +242 -0
  52. package/dist/sandbox/socks-proxy.js.map +1 -0
  53. package/dist/utils/debug.d.ts +7 -0
  54. package/dist/utils/debug.d.ts.map +1 -0
  55. package/dist/utils/debug.js +22 -0
  56. package/dist/utils/debug.js.map +1 -0
  57. package/dist/utils/platform.d.ts +6 -0
  58. package/dist/utils/platform.d.ts.map +1 -0
  59. package/dist/utils/platform.js +16 -0
  60. package/dist/utils/platform.js.map +1 -0
  61. package/dist/utils/ripgrep.d.ts +20 -0
  62. package/dist/utils/ripgrep.d.ts.map +1 -0
  63. package/dist/utils/ripgrep.js +51 -0
  64. package/dist/utils/ripgrep.js.map +1 -0
  65. package/dist/utils/telemetry.d.ts +67 -0
  66. package/dist/utils/telemetry.d.ts.map +1 -0
  67. package/dist/utils/telemetry.js +249 -0
  68. package/dist/utils/telemetry.js.map +1 -0
  69. package/dist/vendor/seccomp/arm64/apply-seccomp +0 -0
  70. package/dist/vendor/seccomp/arm64/unix-block.bpf +0 -0
  71. package/dist/vendor/seccomp/x64/apply-seccomp +0 -0
  72. package/dist/vendor/seccomp/x64/unix-block.bpf +0 -0
  73. package/dist/vendor/seccomp-src/apply-seccomp.c +98 -0
  74. package/dist/vendor/seccomp-src/seccomp-unix-block.c +97 -0
  75. package/package.json +80 -0
  76. package/vendor/seccomp/arm64/apply-seccomp +0 -0
  77. package/vendor/seccomp/arm64/unix-block.bpf +0 -0
  78. package/vendor/seccomp/x64/apply-seccomp +0 -0
  79. package/vendor/seccomp/x64/unix-block.bpf +0 -0
  80. package/vendor/seccomp-src/apply-seccomp.c +98 -0
  81. package/vendor/seccomp-src/seccomp-unix-block.c +97 -0
@@ -0,0 +1,249 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ const DEFAULT_HEADER_WHITELIST = new Set([
3
+ 'retry-after',
4
+ 'x-request-id',
5
+ 'x-trace-id',
6
+ 'traceparent',
7
+ 'tracestate',
8
+ 'content-encoding',
9
+ 'content-type',
10
+ 'content-length',
11
+ ]);
12
+ const SENSITIVE_FLAG_NAMES = new Set([
13
+ '--token',
14
+ '--secret',
15
+ '--password',
16
+ '--passwd',
17
+ '--api-key',
18
+ '--api_key',
19
+ '--apikey',
20
+ '--authorization',
21
+ '--auth',
22
+ '--bearer',
23
+ ]);
24
+ const SENSITIVE_KEY_PATTERN = /token|secret|password|passwd|api[-_]?key|authorization|bearer/i;
25
+ const SECRET_VALUE_PATTERN = /(bearer\s+[a-z0-9+/=_-]{8,}|sk-[a-z0-9]{16,}|[a-z0-9]{24,}|AIza[0-9a-z_-]{35})/i;
26
+ const defaultSink = event => {
27
+ process.stderr.write(`${JSON.stringify(event)}\n`);
28
+ };
29
+ const context = {
30
+ enabled: false,
31
+ traceId: null,
32
+ command: null,
33
+ userContext: null,
34
+ sink: defaultSink,
35
+ };
36
+ export function initializeTelemetry(options) {
37
+ context.enabled = Boolean(options.debugEnabled);
38
+ if (!context.enabled) {
39
+ context.traceId = null;
40
+ context.command = null;
41
+ context.userContext = options.userContext ?? null;
42
+ context.sink = defaultSink;
43
+ return null;
44
+ }
45
+ const traceId = randomUUID();
46
+ context.traceId = traceId;
47
+ context.command = options.commandArgs
48
+ ? sanitizeCommandArgs(options.commandArgs)
49
+ : null;
50
+ context.userContext = options.userContext ?? null;
51
+ context.sink = context.sink || defaultSink;
52
+ process.env.DEBUG = process.env.DEBUG || 'true';
53
+ process.env.SRT_TRACE_ID = traceId;
54
+ const bannerMessage = `Debug telemetry active (trace_id=${traceId})`;
55
+ process.stderr.write(`${bannerMessage}\n`);
56
+ return traceId;
57
+ }
58
+ export function isTelemetryEnabled() {
59
+ return context.enabled;
60
+ }
61
+ export function getTelemetryTraceId() {
62
+ return context.traceId;
63
+ }
64
+ export function setTelemetrySink(sink) {
65
+ context.sink = sink ?? defaultSink;
66
+ }
67
+ export function emitTelemetryEvent(options) {
68
+ if (!context.enabled || !context.traceId) {
69
+ return null;
70
+ }
71
+ const attempt = options.attempt ?? 0;
72
+ const command = options.command ?? context.command ?? null;
73
+ const event = {
74
+ event: 'sandbox_request',
75
+ trace_id: context.traceId,
76
+ stage: options.stage,
77
+ attempt,
78
+ command,
79
+ status: options.status,
80
+ status_code: options.status_code === undefined ? null : options.status_code,
81
+ sandbox_verdict: normalizeSandboxVerdict(options.sandbox_verdict),
82
+ network: normalizeNetwork(options.network),
83
+ http_metadata: normalizeHttp(options.http_metadata),
84
+ latency_ms: options.latency_ms === undefined || options.latency_ms === null
85
+ ? null
86
+ : Math.round(options.latency_ms),
87
+ queue_latency_ms: options.queue_latency_ms === undefined ||
88
+ options.queue_latency_ms === null
89
+ ? null
90
+ : Math.round(options.queue_latency_ms),
91
+ user_context: options.user_context === undefined
92
+ ? context.userContext
93
+ : options.user_context,
94
+ egress_type: options.egress_type === undefined ? null : options.egress_type,
95
+ timestamp: new Date().toISOString(),
96
+ };
97
+ scrubSecretsInEvent(event);
98
+ context.sink(event);
99
+ return event;
100
+ }
101
+ export function sanitizeCommandArgs(args) {
102
+ const sanitizedArgs = [];
103
+ let redactNext = false;
104
+ for (const arg of args) {
105
+ if (redactNext) {
106
+ sanitizedArgs.push(redactValue(arg));
107
+ redactNext = false;
108
+ continue;
109
+ }
110
+ if (SENSITIVE_FLAG_NAMES.has(arg.toLowerCase())) {
111
+ sanitizedArgs.push(arg);
112
+ redactNext = true;
113
+ continue;
114
+ }
115
+ sanitizedArgs.push(sanitizeArg(arg));
116
+ }
117
+ return sanitizedArgs.join(' ');
118
+ }
119
+ export function sanitizeHeaders(headers, whitelist = DEFAULT_HEADER_WHITELIST) {
120
+ const sanitized = {};
121
+ for (const [key, value] of Object.entries(headers)) {
122
+ const lowercaseKey = key.toLowerCase();
123
+ if (value === undefined) {
124
+ sanitized[lowercaseKey] = null;
125
+ continue;
126
+ }
127
+ if (!whitelist.has(lowercaseKey)) {
128
+ sanitized[lowercaseKey] = '[REDACTED]';
129
+ continue;
130
+ }
131
+ const normalizedValue = Array.isArray(value)
132
+ ? value.join(',')
133
+ : String(value);
134
+ sanitized[lowercaseKey] = autoRedactValue(normalizedValue);
135
+ }
136
+ return sanitized;
137
+ }
138
+ export function redactValue(value) {
139
+ const hash = createHash('sha256').update(value).digest('hex');
140
+ return `sha256:${hash}`;
141
+ }
142
+ function sanitizeArg(arg) {
143
+ const trimmed = arg.trim();
144
+ if (!trimmed) {
145
+ return trimmed;
146
+ }
147
+ const equalIndex = trimmed.indexOf('=');
148
+ if (equalIndex > -1) {
149
+ const key = trimmed.slice(0, equalIndex);
150
+ const value = trimmed.slice(equalIndex + 1);
151
+ if (isSensitiveKey(key) || looksSensitiveValue(value)) {
152
+ return `${key}=${redactValue(value)}`;
153
+ }
154
+ return trimmed;
155
+ }
156
+ if (isSensitiveKey(trimmed) || looksSensitiveValue(trimmed)) {
157
+ return redactValue(trimmed);
158
+ }
159
+ return trimmed;
160
+ }
161
+ function isSensitiveKey(key) {
162
+ const normalized = key.replace(/^-+/, '');
163
+ return SENSITIVE_KEY_PATTERN.test(normalized);
164
+ }
165
+ function looksSensitiveValue(value) {
166
+ if (!value) {
167
+ return false;
168
+ }
169
+ if (SECRET_VALUE_PATTERN.test(value)) {
170
+ return true;
171
+ }
172
+ // Long base64/hex strings are suspicious
173
+ const compact = value.replace(/[^a-z0-9]/gi, '');
174
+ if (compact.length >= 24) {
175
+ if (/^[a-f0-9]+$/i.test(compact) || /^[a-z0-9+/=]+$/i.test(value)) {
176
+ return true;
177
+ }
178
+ }
179
+ return false;
180
+ }
181
+ function normalizeSandboxVerdict(verdict) {
182
+ if (!verdict) {
183
+ return {
184
+ decision: null,
185
+ reason: null,
186
+ policy_tag: null,
187
+ };
188
+ }
189
+ return {
190
+ decision: verdict.decision ?? null,
191
+ reason: verdict.reason ?? null,
192
+ policy_tag: verdict.policy_tag ?? null,
193
+ };
194
+ }
195
+ function normalizeNetwork(network) {
196
+ if (!network) {
197
+ return {
198
+ resolved_host: null,
199
+ resolved_ip: null,
200
+ tls_outcome: null,
201
+ tls_error: null,
202
+ dns_source: null,
203
+ };
204
+ }
205
+ return {
206
+ resolved_host: network.resolved_host ?? null,
207
+ resolved_ip: network.resolved_ip ?? null,
208
+ tls_outcome: network.tls_outcome ?? null,
209
+ tls_error: network.tls_error ?? null,
210
+ dns_source: network.dns_source ?? null,
211
+ };
212
+ }
213
+ function normalizeHttp(metadata) {
214
+ if (!metadata) {
215
+ return {
216
+ req_headers: null,
217
+ resp_headers: null,
218
+ payload_bytes: null,
219
+ payload_hash: null,
220
+ compression: null,
221
+ };
222
+ }
223
+ return {
224
+ req_headers: metadata.req_headers ?? null,
225
+ resp_headers: metadata.resp_headers ?? null,
226
+ payload_bytes: metadata.payload_bytes === undefined ? null : metadata.payload_bytes,
227
+ payload_hash: metadata.payload_hash ?? null,
228
+ compression: metadata.compression ?? null,
229
+ };
230
+ }
231
+ function autoRedactValue(value) {
232
+ if (looksSensitiveValue(value)) {
233
+ return redactValue(value);
234
+ }
235
+ return value;
236
+ }
237
+ function scrubSecretsInEvent(event) {
238
+ if (event.command &&
239
+ looksSensitiveValue(event.command) &&
240
+ !event.command.includes('sha256:')) {
241
+ event.command = redactValue(event.command);
242
+ }
243
+ if (event.user_context &&
244
+ looksSensitiveValue(event.user_context) &&
245
+ !event.user_context.includes('sha256:')) {
246
+ event.user_context = redactValue(event.user_context);
247
+ }
248
+ }
249
+ //# sourceMappingURL=telemetry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telemetry.js","sourceRoot":"","sources":["../../src/utils/telemetry.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ /*
2
+ * apply-seccomp.c - Apply seccomp BPF filter and exec command
3
+ *
4
+ * Usage: apply-seccomp <filter.bpf> <command> [args...]
5
+ *
6
+ * This program reads a pre-compiled BPF filter from a file, applies it
7
+ * using prctl(PR_SET_SECCOMP), and then execs the specified command.
8
+ *
9
+ * The BPF filter must be in the format expected by SECCOMP_MODE_FILTER:
10
+ * - struct sock_fprog { unsigned short len; struct sock_filter *filter; }
11
+ * - Each filter instruction is 8 bytes (BPF instruction format)
12
+ *
13
+ * Compile: gcc -static -O2 -o apply-seccomp apply-seccomp.c
14
+ */
15
+
16
+ #include <stdio.h>
17
+ #include <stdlib.h>
18
+ #include <string.h>
19
+ #include <unistd.h>
20
+ #include <fcntl.h>
21
+ #include <sys/prctl.h>
22
+ #include <linux/seccomp.h>
23
+ #include <linux/filter.h>
24
+ #include <errno.h>
25
+
26
+ #ifndef PR_SET_NO_NEW_PRIVS
27
+ #define PR_SET_NO_NEW_PRIVS 38
28
+ #endif
29
+
30
+ #ifndef SECCOMP_MODE_FILTER
31
+ #define SECCOMP_MODE_FILTER 2
32
+ #endif
33
+
34
+ #define MAX_FILTER_SIZE 4096 // Maximum BPF filter size in bytes
35
+
36
+ int main(int argc, char *argv[], char *envp[]) {
37
+ if (argc < 3) {
38
+ fprintf(stderr, "Usage: %s <filter.bpf> <command> [args...]\n", argv[0]);
39
+ return 1;
40
+ }
41
+
42
+ const char *filter_path = argv[1];
43
+ char **command_argv = &argv[2];
44
+
45
+ // Open and read BPF filter file
46
+ int fd = open(filter_path, O_RDONLY);
47
+ if (fd < 0) {
48
+ perror("Failed to open BPF filter file");
49
+ return 1;
50
+ }
51
+
52
+ // Read filter into memory
53
+ unsigned char filter_bytes[MAX_FILTER_SIZE];
54
+ ssize_t filter_size = read(fd, filter_bytes, MAX_FILTER_SIZE);
55
+ close(fd);
56
+
57
+ if (filter_size < 0) {
58
+ perror("Failed to read BPF filter");
59
+ return 1;
60
+ }
61
+ if (filter_size == 0) {
62
+ fprintf(stderr, "BPF filter file is empty\n");
63
+ return 1;
64
+ }
65
+ if (filter_size % 8 != 0) {
66
+ fprintf(stderr, "Invalid BPF filter size: %zd (must be multiple of 8)\n", filter_size);
67
+ return 1;
68
+ }
69
+
70
+ // Convert bytes to sock_filter instructions
71
+ unsigned short filter_len = filter_size / 8;
72
+ struct sock_filter *filter = (struct sock_filter *)filter_bytes;
73
+
74
+ // Set up sock_fprog structure
75
+ struct sock_fprog prog = {
76
+ .len = filter_len,
77
+ .filter = filter,
78
+ };
79
+
80
+ // Set NO_NEW_PRIVS to allow seccomp without CAP_SYS_ADMIN
81
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
82
+ perror("prctl(PR_SET_NO_NEW_PRIVS) failed");
83
+ return 1;
84
+ }
85
+
86
+ // Apply seccomp filter
87
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
88
+ perror("prctl(PR_SET_SECCOMP) failed");
89
+ return 1;
90
+ }
91
+
92
+ // Exec the command with seccomp filter active
93
+ execvp(command_argv[0], command_argv);
94
+
95
+ // If we get here, exec failed
96
+ perror("execvp failed");
97
+ return 1;
98
+ }
@@ -0,0 +1,97 @@
1
+ /*
2
+ * Seccomp BPF filter generator to block Unix domain socket creation
3
+ *
4
+ * This program generates a seccomp-bpf filter that blocks the socket() syscall
5
+ * when called with AF_UNIX as the domain argument. This prevents creation of
6
+ * Unix domain sockets while allowing all other socket types (AF_INET, AF_INET6, etc.)
7
+ * and all other syscalls.
8
+ *
9
+ * The filter is exported in a format compatible with bubblewrap's --seccomp flag.
10
+ *
11
+ * SECURITY LIMITATION - 32-bit x86 (ia32):
12
+ * TODO: This filter does NOT block socketcall() syscall, which is a security issue
13
+ * on 32-bit x86 systems. On ia32, the socket() syscall doesn't exist - instead,
14
+ * all socket operations are multiplexed through socketcall():
15
+ * - socketcall(SYS_SOCKET, [AF_UNIX, ...]) - can bypass this filter
16
+ * - socketcall(SYS_SOCKETPAIR, [AF_UNIX, ...]) - can bypass this filter
17
+ *
18
+ * To fix this, we need to add conditional rules that:
19
+ * 1. Check if socketcall() exists on the current architecture (32-bit x86 only)
20
+ * 2. Block socketcall(SYS_SOCKET, ...) when first arg of sub-call is AF_UNIX
21
+ * 3. Block socketcall(SYS_SOCKETPAIR, ...) when first arg of sub-call is AF_UNIX
22
+ *
23
+ * This requires inspecting the arguments passed to socketcall, which is more
24
+ * complex BPF logic. For now, 32-bit x86 is not supported.
25
+ *
26
+ * Compilation:
27
+ * gcc -o seccomp-unix-block seccomp-unix-block.c -lseccomp
28
+ *
29
+ * Usage:
30
+ * ./seccomp-unix-block <output-file>
31
+ *
32
+ * Dependencies:
33
+ * - libseccomp (libseccomp-dev package on Debian/Ubuntu)
34
+ */
35
+
36
+ #include <errno.h>
37
+ #include <fcntl.h>
38
+ #include <stdio.h>
39
+ #include <stdlib.h>
40
+ #include <string.h>
41
+ #include <unistd.h>
42
+ #include <seccomp.h>
43
+ #include <sys/socket.h>
44
+ #include <sys/stat.h>
45
+ #include <sys/types.h>
46
+
47
+ int main(int argc, char *argv[]) {
48
+ scmp_filter_ctx ctx;
49
+ int rc;
50
+
51
+ if (argc != 2) {
52
+ fprintf(stderr, "Usage: %s <output-file>\n", argv[0]);
53
+ return 1;
54
+ }
55
+
56
+ const char *output_file = argv[1];
57
+
58
+ /* Create seccomp context with default action ALLOW */
59
+ ctx = seccomp_init(SCMP_ACT_ALLOW);
60
+ if (ctx == NULL) {
61
+ fprintf(stderr, "Error: Failed to initialize seccomp context\n");
62
+ return 1;
63
+ }
64
+
65
+ /* Add rule to block socket(AF_UNIX, ...) */
66
+ /* socket() syscall signature: int socket(int domain, int type, int protocol) */
67
+ /* arg0 = domain (AF_UNIX = 1) */
68
+ rc = seccomp_rule_add(ctx, SCMP_ACT_ERRNO(EPERM), SCMP_SYS(socket), 1,
69
+ SCMP_A0(SCMP_CMP_EQ, AF_UNIX));
70
+ if (rc < 0) {
71
+ fprintf(stderr, "Error: Failed to add seccomp rule: %s\n", strerror(-rc));
72
+ seccomp_release(ctx);
73
+ return 1;
74
+ }
75
+
76
+ /* Export the filter to a file */
77
+ int fd = open(output_file, O_CREAT | O_WRONLY | O_TRUNC, 0600);
78
+ if (fd < 0) {
79
+ fprintf(stderr, "Error: Failed to open output file: %s\n", strerror(errno));
80
+ seccomp_release(ctx);
81
+ return 1;
82
+ }
83
+
84
+ rc = seccomp_export_bpf(ctx, fd);
85
+ if (rc < 0) {
86
+ fprintf(stderr, "Error: Failed to export seccomp filter: %s\n", strerror(-rc));
87
+ close(fd);
88
+ seccomp_release(ctx);
89
+ return 1;
90
+ }
91
+
92
+ /* Clean up */
93
+ close(fd);
94
+ seccomp_release(ctx);
95
+
96
+ return 0;
97
+ }
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@voratiq/sandbox-runtime",
3
+ "version": "0.7.0-voratiq1",
4
+ "description": "(Voratiq-maintained fork of the) Anthropic Sandbox Runtime (ASRT) - A general-purpose tool for wrapping security boundaries around arbitrary processes",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "bin": {
9
+ "srt": "dist/cli.js"
10
+ },
11
+ "engines": {
12
+ "node": ">=18.0.0"
13
+ },
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "postbuild": "[ -d vendor ] && cp -r vendor dist/ || true",
17
+ "build:seccomp": "scripts/build-seccomp-binaries.sh",
18
+ "clean": "rm -rf dist",
19
+ "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",
22
+ "typecheck": "tsc --noEmit",
23
+ "lint": "eslint 'src/**/*.ts' --fix --cache --cache-location=node_modules/.cache/.eslintcache",
24
+ "lint:check": "eslint 'src/**/*.ts' --cache --cache-location=node_modules/.cache/.eslintcache",
25
+ "format": "prettier --write 'src/**/*.ts' --cache --log-level warn",
26
+ "prepublishOnly": "npm run clean && npm run build"
27
+ },
28
+ "dependencies": {
29
+ "@pondwader/socks5-server": "^1.0.10",
30
+ "@types/lodash-es": "^4.17.12",
31
+ "commander": "^12.1.0",
32
+ "lodash-es": "^4.17.21",
33
+ "shell-quote": "^1.8.3",
34
+ "zod": "^3.24.1"
35
+ },
36
+ "devDependencies": {
37
+ "@eslint/js": "^9.14.0",
38
+ "@types/node": "^18",
39
+ "@types/shell-quote": "^1.7.5",
40
+ "eslint": "^9.14.0",
41
+ "eslint-config-prettier": "^8.10.0",
42
+ "eslint-import-resolver-typescript": "^3.6.3",
43
+ "eslint-plugin-import": "^2.31.0",
44
+ "eslint-plugin-n": "^17.16.2",
45
+ "eslint-plugin-prettier": "^5.1.3",
46
+ "globals": "^15.12.0",
47
+ "prettier": "3.3.3",
48
+ "typescript": "^5.6.3",
49
+ "typescript-eslint": "^8.13.0"
50
+ },
51
+ "files": [
52
+ "dist",
53
+ "vendor",
54
+ "CHANGELOG.md",
55
+ "README.md",
56
+ "LICENSE",
57
+ "NOTICE"
58
+ ],
59
+ "keywords": [
60
+ "sandbox",
61
+ "seatbelt",
62
+ "sandbox-exec",
63
+ "anthropic",
64
+ "claude",
65
+ "security",
66
+ "bubblewrap",
67
+ "network-filtering",
68
+ "filesystem-restrictions"
69
+ ],
70
+ "author": "Voratiq",
71
+ "license": "Apache-2.0",
72
+ "repository": {
73
+ "type": "git",
74
+ "url": "git+https://github.com/voratiq/sandbox-runtime.git"
75
+ },
76
+ "bugs": {
77
+ "url": "https://github.com/voratiq/sandbox-runtime/issues"
78
+ },
79
+ "homepage": "https://github.com/voratiq/sandbox-runtime#readme"
80
+ }
Binary file
@@ -0,0 +1,98 @@
1
+ /*
2
+ * apply-seccomp.c - Apply seccomp BPF filter and exec command
3
+ *
4
+ * Usage: apply-seccomp <filter.bpf> <command> [args...]
5
+ *
6
+ * This program reads a pre-compiled BPF filter from a file, applies it
7
+ * using prctl(PR_SET_SECCOMP), and then execs the specified command.
8
+ *
9
+ * The BPF filter must be in the format expected by SECCOMP_MODE_FILTER:
10
+ * - struct sock_fprog { unsigned short len; struct sock_filter *filter; }
11
+ * - Each filter instruction is 8 bytes (BPF instruction format)
12
+ *
13
+ * Compile: gcc -static -O2 -o apply-seccomp apply-seccomp.c
14
+ */
15
+
16
+ #include <stdio.h>
17
+ #include <stdlib.h>
18
+ #include <string.h>
19
+ #include <unistd.h>
20
+ #include <fcntl.h>
21
+ #include <sys/prctl.h>
22
+ #include <linux/seccomp.h>
23
+ #include <linux/filter.h>
24
+ #include <errno.h>
25
+
26
+ #ifndef PR_SET_NO_NEW_PRIVS
27
+ #define PR_SET_NO_NEW_PRIVS 38
28
+ #endif
29
+
30
+ #ifndef SECCOMP_MODE_FILTER
31
+ #define SECCOMP_MODE_FILTER 2
32
+ #endif
33
+
34
+ #define MAX_FILTER_SIZE 4096 // Maximum BPF filter size in bytes
35
+
36
+ int main(int argc, char *argv[], char *envp[]) {
37
+ if (argc < 3) {
38
+ fprintf(stderr, "Usage: %s <filter.bpf> <command> [args...]\n", argv[0]);
39
+ return 1;
40
+ }
41
+
42
+ const char *filter_path = argv[1];
43
+ char **command_argv = &argv[2];
44
+
45
+ // Open and read BPF filter file
46
+ int fd = open(filter_path, O_RDONLY);
47
+ if (fd < 0) {
48
+ perror("Failed to open BPF filter file");
49
+ return 1;
50
+ }
51
+
52
+ // Read filter into memory
53
+ unsigned char filter_bytes[MAX_FILTER_SIZE];
54
+ ssize_t filter_size = read(fd, filter_bytes, MAX_FILTER_SIZE);
55
+ close(fd);
56
+
57
+ if (filter_size < 0) {
58
+ perror("Failed to read BPF filter");
59
+ return 1;
60
+ }
61
+ if (filter_size == 0) {
62
+ fprintf(stderr, "BPF filter file is empty\n");
63
+ return 1;
64
+ }
65
+ if (filter_size % 8 != 0) {
66
+ fprintf(stderr, "Invalid BPF filter size: %zd (must be multiple of 8)\n", filter_size);
67
+ return 1;
68
+ }
69
+
70
+ // Convert bytes to sock_filter instructions
71
+ unsigned short filter_len = filter_size / 8;
72
+ struct sock_filter *filter = (struct sock_filter *)filter_bytes;
73
+
74
+ // Set up sock_fprog structure
75
+ struct sock_fprog prog = {
76
+ .len = filter_len,
77
+ .filter = filter,
78
+ };
79
+
80
+ // Set NO_NEW_PRIVS to allow seccomp without CAP_SYS_ADMIN
81
+ if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) != 0) {
82
+ perror("prctl(PR_SET_NO_NEW_PRIVS) failed");
83
+ return 1;
84
+ }
85
+
86
+ // Apply seccomp filter
87
+ if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) != 0) {
88
+ perror("prctl(PR_SET_SECCOMP) failed");
89
+ return 1;
90
+ }
91
+
92
+ // Exec the command with seccomp filter active
93
+ execvp(command_argv[0], command_argv);
94
+
95
+ // If we get here, exec failed
96
+ perror("execvp failed");
97
+ return 1;
98
+ }