@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.
- package/CHANGELOG.md +10 -0
- package/LICENSE +201 -0
- package/NOTICE +11 -0
- package/README.md +17 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +243 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/sandbox/generate-seccomp-filter.d.ts +56 -0
- package/dist/sandbox/generate-seccomp-filter.d.ts.map +1 -0
- package/dist/sandbox/generate-seccomp-filter.js +158 -0
- package/dist/sandbox/generate-seccomp-filter.js.map +1 -0
- package/dist/sandbox/http-proxy.d.ts +12 -0
- package/dist/sandbox/http-proxy.d.ts.map +1 -0
- package/dist/sandbox/http-proxy.js +489 -0
- package/dist/sandbox/http-proxy.js.map +1 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts +111 -0
- package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/linux-sandbox-utils.js +518 -0
- package/dist/sandbox/linux-sandbox-utils.js.map +1 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts +54 -0
- package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/macos-sandbox-utils.js +559 -0
- package/dist/sandbox/macos-sandbox-utils.js.map +1 -0
- package/dist/sandbox/sandbox-config.d.ts +170 -0
- package/dist/sandbox/sandbox-config.d.ts.map +1 -0
- package/dist/sandbox/sandbox-config.js +126 -0
- package/dist/sandbox/sandbox-config.js.map +1 -0
- package/dist/sandbox/sandbox-manager.d.ts +35 -0
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
- package/dist/sandbox/sandbox-manager.js +666 -0
- package/dist/sandbox/sandbox-manager.js.map +1 -0
- package/dist/sandbox/sandbox-schemas.d.ts +17 -0
- package/dist/sandbox/sandbox-schemas.d.ts.map +1 -0
- package/dist/sandbox/sandbox-schemas.js +2 -0
- package/dist/sandbox/sandbox-schemas.js.map +1 -0
- package/dist/sandbox/sandbox-utils.d.ts +53 -0
- package/dist/sandbox/sandbox-utils.d.ts.map +1 -0
- package/dist/sandbox/sandbox-utils.js +368 -0
- package/dist/sandbox/sandbox-utils.js.map +1 -0
- package/dist/sandbox/sandbox-violation-store.d.ts +19 -0
- package/dist/sandbox/sandbox-violation-store.d.ts.map +1 -0
- package/dist/sandbox/sandbox-violation-store.js +54 -0
- package/dist/sandbox/sandbox-violation-store.js.map +1 -0
- package/dist/sandbox/socks-proxy.d.ts +18 -0
- package/dist/sandbox/socks-proxy.d.ts.map +1 -0
- package/dist/sandbox/socks-proxy.js +242 -0
- package/dist/sandbox/socks-proxy.js.map +1 -0
- package/dist/utils/debug.d.ts +7 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +22 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/platform.d.ts +6 -0
- package/dist/utils/platform.d.ts.map +1 -0
- package/dist/utils/platform.js +16 -0
- package/dist/utils/platform.js.map +1 -0
- package/dist/utils/ripgrep.d.ts +20 -0
- package/dist/utils/ripgrep.d.ts.map +1 -0
- package/dist/utils/ripgrep.js +51 -0
- package/dist/utils/ripgrep.js.map +1 -0
- package/dist/utils/telemetry.d.ts +67 -0
- package/dist/utils/telemetry.d.ts.map +1 -0
- package/dist/utils/telemetry.js +249 -0
- package/dist/utils/telemetry.js.map +1 -0
- 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/dist/vendor/seccomp-src/apply-seccomp.c +98 -0
- package/dist/vendor/seccomp-src/seccomp-unix-block.c +97 -0
- package/package.json +80 -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
- package/vendor/seccomp-src/apply-seccomp.c +98 -0
- 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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"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
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
|
+
}
|
|
@@ -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
|
|
Binary file
|
|
Binary file
|
|
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
|
+
}
|