@anthropic-ai/sandbox-runtime 0.0.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.
Files changed (62) hide show
  1. package/README.md +497 -0
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +75 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/index.d.ts +4 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +4 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/sandbox/http-proxy.d.ts +7 -0
  11. package/dist/sandbox/http-proxy.d.ts.map +1 -0
  12. package/dist/sandbox/http-proxy.js +118 -0
  13. package/dist/sandbox/http-proxy.js.map +1 -0
  14. package/dist/sandbox/linux-sandbox-utils.d.ts +60 -0
  15. package/dist/sandbox/linux-sandbox-utils.d.ts.map +1 -0
  16. package/dist/sandbox/linux-sandbox-utils.js +333 -0
  17. package/dist/sandbox/linux-sandbox-utils.js.map +1 -0
  18. package/dist/sandbox/macos-sandbox-utils.d.ts +53 -0
  19. package/dist/sandbox/macos-sandbox-utils.d.ts.map +1 -0
  20. package/dist/sandbox/macos-sandbox-utils.js +496 -0
  21. package/dist/sandbox/macos-sandbox-utils.js.map +1 -0
  22. package/dist/sandbox/sandbox-manager.d.ts +34 -0
  23. package/dist/sandbox/sandbox-manager.d.ts.map +1 -0
  24. package/dist/sandbox/sandbox-manager.js +655 -0
  25. package/dist/sandbox/sandbox-manager.js.map +1 -0
  26. package/dist/sandbox/sandbox-schemas.d.ts +93 -0
  27. package/dist/sandbox/sandbox-schemas.d.ts.map +1 -0
  28. package/dist/sandbox/sandbox-schemas.js +231 -0
  29. package/dist/sandbox/sandbox-schemas.js.map +1 -0
  30. package/dist/sandbox/sandbox-utils.d.ts +49 -0
  31. package/dist/sandbox/sandbox-utils.d.ts.map +1 -0
  32. package/dist/sandbox/sandbox-utils.js +345 -0
  33. package/dist/sandbox/sandbox-utils.js.map +1 -0
  34. package/dist/sandbox/sandbox-violation-store.d.ts +19 -0
  35. package/dist/sandbox/sandbox-violation-store.d.ts.map +1 -0
  36. package/dist/sandbox/sandbox-violation-store.js +54 -0
  37. package/dist/sandbox/sandbox-violation-store.js.map +1 -0
  38. package/dist/sandbox/socks-proxy.d.ts +13 -0
  39. package/dist/sandbox/socks-proxy.d.ts.map +1 -0
  40. package/dist/sandbox/socks-proxy.js +95 -0
  41. package/dist/sandbox/socks-proxy.js.map +1 -0
  42. package/dist/utils/debug.d.ts +7 -0
  43. package/dist/utils/debug.d.ts.map +1 -0
  44. package/dist/utils/debug.js +22 -0
  45. package/dist/utils/debug.js.map +1 -0
  46. package/dist/utils/exec.d.ts +13 -0
  47. package/dist/utils/exec.d.ts.map +1 -0
  48. package/dist/utils/exec.js +38 -0
  49. package/dist/utils/exec.js.map +1 -0
  50. package/dist/utils/platform.d.ts +6 -0
  51. package/dist/utils/platform.d.ts.map +1 -0
  52. package/dist/utils/platform.js +16 -0
  53. package/dist/utils/platform.js.map +1 -0
  54. package/dist/utils/ripgrep.d.ts +16 -0
  55. package/dist/utils/ripgrep.d.ts.map +1 -0
  56. package/dist/utils/ripgrep.js +57 -0
  57. package/dist/utils/ripgrep.js.map +1 -0
  58. package/dist/utils/settings.d.ts +147 -0
  59. package/dist/utils/settings.d.ts.map +1 -0
  60. package/dist/utils/settings.js +244 -0
  61. package/dist/utils/settings.js.map +1 -0
  62. package/package.json +72 -0
@@ -0,0 +1,118 @@
1
+ import { createServer } from 'node:http';
2
+ import { request as httpRequest } from 'node:http';
3
+ import { request as httpsRequest } from 'node:https';
4
+ import { connect } from 'node:net';
5
+ import { URL } from 'node:url';
6
+ import { logForDebugging } from '../utils/debug.js';
7
+ export function createHttpProxyServer(options) {
8
+ const server = createServer();
9
+ // Handle CONNECT requests for HTTPS traffic
10
+ server.on('connect', async (req, socket) => {
11
+ // Attach error handler immediately to prevent unhandled errors
12
+ socket.on('error', err => {
13
+ logForDebugging(`Client socket error: ${err.message}`, { level: 'error' });
14
+ });
15
+ try {
16
+ const [hostname, portStr] = req.url.split(':');
17
+ const port = portStr === undefined ? undefined : parseInt(portStr, 10);
18
+ if (!hostname || !port) {
19
+ logForDebugging(`Invalid CONNECT request: ${req.url}`, {
20
+ level: 'error',
21
+ });
22
+ socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
23
+ return;
24
+ }
25
+ const allowed = await options.filter(port, hostname, socket);
26
+ if (!allowed) {
27
+ logForDebugging(`Connection blocked to ${hostname}:${port}`, {
28
+ level: 'error',
29
+ });
30
+ socket.end('HTTP/1.1 403 Forbidden\r\n' +
31
+ 'Content-Type: text/plain\r\n' +
32
+ 'X-Proxy-Error: blocked-by-allowlist\r\n' +
33
+ '\r\n' +
34
+ 'Connection blocked by network allowlist');
35
+ return;
36
+ }
37
+ const serverSocket = connect(port, hostname, () => {
38
+ socket.write('HTTP/1.1 200 Connection Established\r\n\r\n');
39
+ serverSocket.pipe(socket);
40
+ socket.pipe(serverSocket);
41
+ });
42
+ serverSocket.on('error', err => {
43
+ logForDebugging(`CONNECT tunnel failed: ${err.message}`, {
44
+ level: 'error',
45
+ });
46
+ socket.end('HTTP/1.1 502 Bad Gateway\r\n\r\n');
47
+ });
48
+ socket.on('error', err => {
49
+ logForDebugging(`Client socket error: ${err.message}`, {
50
+ level: 'error',
51
+ });
52
+ serverSocket.destroy();
53
+ });
54
+ socket.on('end', () => serverSocket.end());
55
+ serverSocket.on('end', () => socket.end());
56
+ }
57
+ catch (err) {
58
+ logForDebugging(`Error handling CONNECT: ${err}`, { level: 'error' });
59
+ socket.end('HTTP/1.1 500 Internal Server Error\r\n\r\n');
60
+ }
61
+ });
62
+ // Handle regular HTTP requests
63
+ server.on('request', async (req, res) => {
64
+ try {
65
+ const url = new URL(req.url);
66
+ const hostname = url.hostname;
67
+ const port = url.port
68
+ ? parseInt(url.port, 10)
69
+ : url.protocol === 'https:'
70
+ ? 443
71
+ : 80;
72
+ const allowed = await options.filter(port, hostname, req.socket);
73
+ if (!allowed) {
74
+ logForDebugging(`HTTP request blocked to ${hostname}:${port}`, {
75
+ level: 'error',
76
+ });
77
+ res.writeHead(403, {
78
+ 'Content-Type': 'text/plain',
79
+ 'X-Proxy-Error': 'blocked-by-allowlist',
80
+ });
81
+ res.end('Connection blocked by network allowlist');
82
+ return;
83
+ }
84
+ // Choose http or https module
85
+ const requestFn = url.protocol === 'https:' ? httpsRequest : httpRequest;
86
+ const proxyReq = requestFn({
87
+ hostname,
88
+ port,
89
+ path: url.pathname + url.search,
90
+ method: req.method,
91
+ headers: {
92
+ ...req.headers,
93
+ host: url.host,
94
+ },
95
+ }, proxyRes => {
96
+ res.writeHead(proxyRes.statusCode, proxyRes.headers);
97
+ proxyRes.pipe(res);
98
+ });
99
+ proxyReq.on('error', err => {
100
+ logForDebugging(`Proxy request failed: ${err.message}`, {
101
+ level: 'error',
102
+ });
103
+ if (!res.headersSent) {
104
+ res.writeHead(502, { 'Content-Type': 'text/plain' });
105
+ res.end('Bad Gateway');
106
+ }
107
+ });
108
+ req.pipe(proxyReq);
109
+ }
110
+ catch (err) {
111
+ logForDebugging(`Error handling HTTP request: ${err}`, { level: 'error' });
112
+ res.writeHead(500, { 'Content-Type': 'text/plain' });
113
+ res.end('Internal Server Error');
114
+ }
115
+ });
116
+ return server;
117
+ }
118
+ //# sourceMappingURL=http-proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-proxy.js","sourceRoot":"","sources":["../../src/sandbox/http-proxy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,YAAY,CAAA;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAA;AAClC,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAUnD,MAAM,UAAU,qBAAqB,CAAC,OAA+B;IACnE,MAAM,MAAM,GAAG,YAAY,EAAE,CAAA;IAE7B,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;QACzC,+DAA+D;QAC/D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;YACvB,eAAe,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;QAC5E,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC;YACH,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,GAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;YAC/C,MAAM,IAAI,GAAG,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAA;YAEtE,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;gBACvB,eAAe,CAAC,4BAA4B,GAAG,CAAC,GAAG,EAAE,EAAE;oBACrD,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;gBAC9C,OAAM;YACR,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC5D,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,eAAe,CAAC,yBAAyB,QAAQ,IAAI,IAAI,EAAE,EAAE;oBAC3D,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,MAAM,CAAC,GAAG,CACR,4BAA4B;oBAC1B,8BAA8B;oBAC9B,yCAAyC;oBACzC,MAAM;oBACN,yCAAyC,CAC5C,CAAA;gBACD,OAAM;YACR,CAAC;YAED,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE;gBAChD,MAAM,CAAC,KAAK,CAAC,6CAA6C,CAAC,CAAA;gBAC3D,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBACzB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YAC3B,CAAC,CAAC,CAAA;YAEF,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;gBAC7B,eAAe,CAAC,0BAA0B,GAAG,CAAC,OAAO,EAAE,EAAE;oBACvD,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;YAChD,CAAC,CAAC,CAAA;YAEF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;gBACvB,eAAe,CAAC,wBAAwB,GAAG,CAAC,OAAO,EAAE,EAAE;oBACrD,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,YAAY,CAAC,OAAO,EAAE,CAAA;YACxB,CAAC,CAAC,CAAA;YAEF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,CAAA;YAC1C,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,CAAA;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,eAAe,CAAC,2BAA2B,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;YACrE,MAAM,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAA;QAC1D,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,+BAA+B;IAC/B,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAI,CAAC,CAAA;YAC7B,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAA;YAC7B,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI;gBACnB,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC;gBACxB,CAAC,CAAC,GAAG,CAAC,QAAQ,KAAK,QAAQ;oBACzB,CAAC,CAAC,GAAG;oBACL,CAAC,CAAC,EAAE,CAAA;YAER,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;YAChE,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,eAAe,CAAC,2BAA2B,QAAQ,IAAI,IAAI,EAAE,EAAE;oBAC7D,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE;oBACjB,cAAc,EAAE,YAAY;oBAC5B,eAAe,EAAE,sBAAsB;iBACxC,CAAC,CAAA;gBACF,GAAG,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;gBAClD,OAAM;YACR,CAAC;YAED,8BAA8B;YAC9B,MAAM,SAAS,GAAG,GAAG,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,CAAA;YAExE,MAAM,QAAQ,GAAG,SAAS,CACxB;gBACE,QAAQ;gBACR,IAAI;gBACJ,IAAI,EAAE,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC,MAAM;gBAC/B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,OAAO,EAAE;oBACP,GAAG,GAAG,CAAC,OAAO;oBACd,IAAI,EAAE,GAAG,CAAC,IAAI;iBACf;aACF,EACD,QAAQ,CAAC,EAAE;gBACT,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,UAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACrD,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;YACpB,CAAC,CACF,CAAA;YAED,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,CAAC,EAAE;gBACzB,eAAe,CAAC,yBAAyB,GAAG,CAAC,OAAO,EAAE,EAAE;oBACtD,KAAK,EAAE,OAAO;iBACf,CAAC,CAAA;gBACF,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC;oBACrB,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;oBACpD,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,CAAA;gBACxB,CAAC;YACH,CAAC,CAAC,CAAA;YAEF,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QACpB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,eAAe,CAAC,gCAAgC,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAA;YAC1E,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,YAAY,EAAE,CAAC,CAAA;YACpD,GAAG,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAA;QAClC,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { ChildProcess } from 'node:child_process';
2
+ import type { FsReadRestrictionConfig, FsWriteRestrictionConfig } from './sandbox-schemas.js';
3
+ export interface LinuxNetworkBridgeContext {
4
+ httpSocketPath: string;
5
+ socksSocketPath: string;
6
+ httpBridgeProcess: ChildProcess;
7
+ socksBridgeProcess: ChildProcess;
8
+ httpProxyPort: number;
9
+ socksProxyPort: number;
10
+ }
11
+ export interface LinuxSandboxParams {
12
+ command: string;
13
+ hasNetworkRestrictions: boolean;
14
+ hasFilesystemRestrictions: boolean;
15
+ httpSocketPath?: string;
16
+ socksSocketPath?: string;
17
+ httpProxyPort?: number;
18
+ socksProxyPort?: number;
19
+ readConfig?: FsReadRestrictionConfig;
20
+ writeConfig?: FsWriteRestrictionConfig;
21
+ enableWeakerNestedSandbox?: boolean;
22
+ }
23
+ /**
24
+ * Check if Linux sandbox dependencies are available (synchronous)
25
+ * Returns true if bwrap, socat, and rg are installed, false otherwise
26
+ * Cached to avoid repeated system calls
27
+ */
28
+ export declare function hasLinuxSandboxDependenciesSync(): boolean;
29
+ /**
30
+ * Initialize the Linux network bridge for sandbox networking
31
+ *
32
+ * ARCHITECTURE NOTE:
33
+ * Linux network sandboxing uses bwrap --unshare-net which creates a completely isolated
34
+ * network namespace with NO network access. To enable network access, we:
35
+ *
36
+ * 1. Host side: Run socat bridges that listen on Unix sockets and forward to host proxy servers
37
+ * - HTTP bridge: Unix socket -> host HTTP proxy (for HTTP/HTTPS traffic)
38
+ * - SOCKS bridge: Unix socket -> host SOCKS5 proxy (for SSH/git traffic)
39
+ *
40
+ * 2. Sandbox side: Bind the Unix sockets into the isolated namespace and run socat listeners
41
+ * - HTTP listener on port 3128 -> HTTP Unix socket -> host HTTP proxy
42
+ * - SOCKS listener on port 1080 -> SOCKS Unix socket -> host SOCKS5 proxy
43
+ *
44
+ * 3. Configure environment:
45
+ * - HTTP_PROXY=http://localhost:3128 for HTTP/HTTPS tools
46
+ * - GIT_SSH_COMMAND with socat for SSH through SOCKS5
47
+ *
48
+ * LIMITATION: Unlike macOS sandbox which can enforce domain-based allowlists at the kernel level,
49
+ * Linux's --unshare-net provides only all-or-nothing network isolation. Domain filtering happens
50
+ * at the host proxy level, not the sandbox boundary. This means network restrictions on Linux
51
+ * depend on the proxy's filtering capabilities.
52
+ *
53
+ * DEPENDENCIES: Requires bwrap (bubblewrap) and socat
54
+ */
55
+ export declare function initializeLinuxNetworkBridge(httpProxyPort: number, socksProxyPort: number): Promise<LinuxNetworkBridgeContext>;
56
+ /**
57
+ * Wrap a command with sandbox restrictions on Linux
58
+ */
59
+ export declare function wrapCommandWithSandboxLinux(params: LinuxSandboxParams): Promise<string>;
60
+ //# sourceMappingURL=linux-sandbox-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux-sandbox-utils.d.ts","sourceRoot":"","sources":["../../src/sandbox/linux-sandbox-utils.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAQtD,OAAO,KAAK,EACV,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,sBAAsB,CAAA;AAE7B,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,MAAM,CAAA;IACtB,eAAe,EAAE,MAAM,CAAA;IACvB,iBAAiB,EAAE,YAAY,CAAA;IAC/B,kBAAkB,EAAE,YAAY,CAAA;IAChC,aAAa,EAAE,MAAM,CAAA;IACrB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,sBAAsB,EAAE,OAAO,CAAA;IAC/B,yBAAyB,EAAE,OAAO,CAAA;IAClC,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,UAAU,CAAC,EAAE,uBAAuB,CAAA;IACpC,WAAW,CAAC,EAAE,wBAAwB,CAAA;IACtC,yBAAyB,CAAC,EAAE,OAAO,CAAA;CACpC;AAKD;;;;GAIG;AACH,wBAAgB,+BAA+B,IAAI,OAAO,CA4BzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,4BAA4B,CAChD,aAAa,EAAE,MAAM,EACrB,cAAc,EAAE,MAAM,GACrB,OAAO,CAAC,yBAAyB,CAAC,CAqGpC;AA4ID;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CAgHjB"}
@@ -0,0 +1,333 @@
1
+ import shellquote from 'shell-quote';
2
+ import { logForDebugging } from '../utils/debug.js';
3
+ import { randomBytes } from 'node:crypto';
4
+ import * as fs from 'fs';
5
+ import { spawn, spawnSync } from 'node:child_process';
6
+ import { tmpdir } from 'node:os';
7
+ import { join } from 'node:path';
8
+ import { generateProxyEnvVars, normalizePathForSandbox, getMandatoryDenyWithinAllow, } from './sandbox-utils.js';
9
+ // Cache for Linux sandbox dependencies check
10
+ let linuxDepsCache;
11
+ /**
12
+ * Check if Linux sandbox dependencies are available (synchronous)
13
+ * Returns true if bwrap, socat, and rg are installed, false otherwise
14
+ * Cached to avoid repeated system calls
15
+ */
16
+ export function hasLinuxSandboxDependenciesSync() {
17
+ if (linuxDepsCache !== undefined) {
18
+ return linuxDepsCache;
19
+ }
20
+ try {
21
+ const bwrapResult = spawnSync('which', ['bwrap'], {
22
+ stdio: 'ignore',
23
+ timeout: 1000,
24
+ });
25
+ const socatResult = spawnSync('which', ['socat'], {
26
+ stdio: 'ignore',
27
+ timeout: 1000,
28
+ });
29
+ const rgResult = spawnSync('which', ['rg'], {
30
+ stdio: 'ignore',
31
+ timeout: 1000,
32
+ });
33
+ linuxDepsCache =
34
+ bwrapResult.status === 0 &&
35
+ socatResult.status === 0 &&
36
+ rgResult.status === 0;
37
+ return linuxDepsCache;
38
+ }
39
+ catch {
40
+ linuxDepsCache = false;
41
+ return false;
42
+ }
43
+ }
44
+ /**
45
+ * Initialize the Linux network bridge for sandbox networking
46
+ *
47
+ * ARCHITECTURE NOTE:
48
+ * Linux network sandboxing uses bwrap --unshare-net which creates a completely isolated
49
+ * network namespace with NO network access. To enable network access, we:
50
+ *
51
+ * 1. Host side: Run socat bridges that listen on Unix sockets and forward to host proxy servers
52
+ * - HTTP bridge: Unix socket -> host HTTP proxy (for HTTP/HTTPS traffic)
53
+ * - SOCKS bridge: Unix socket -> host SOCKS5 proxy (for SSH/git traffic)
54
+ *
55
+ * 2. Sandbox side: Bind the Unix sockets into the isolated namespace and run socat listeners
56
+ * - HTTP listener on port 3128 -> HTTP Unix socket -> host HTTP proxy
57
+ * - SOCKS listener on port 1080 -> SOCKS Unix socket -> host SOCKS5 proxy
58
+ *
59
+ * 3. Configure environment:
60
+ * - HTTP_PROXY=http://localhost:3128 for HTTP/HTTPS tools
61
+ * - GIT_SSH_COMMAND with socat for SSH through SOCKS5
62
+ *
63
+ * LIMITATION: Unlike macOS sandbox which can enforce domain-based allowlists at the kernel level,
64
+ * Linux's --unshare-net provides only all-or-nothing network isolation. Domain filtering happens
65
+ * at the host proxy level, not the sandbox boundary. This means network restrictions on Linux
66
+ * depend on the proxy's filtering capabilities.
67
+ *
68
+ * DEPENDENCIES: Requires bwrap (bubblewrap) and socat
69
+ */
70
+ export async function initializeLinuxNetworkBridge(httpProxyPort, socksProxyPort) {
71
+ const socketId = randomBytes(8).toString('hex');
72
+ const httpSocketPath = join(tmpdir(), `claude-http-${socketId}.sock`);
73
+ const socksSocketPath = join(tmpdir(), `claude-socks-${socketId}.sock`);
74
+ // Start HTTP bridge
75
+ const httpSocatArgs = [
76
+ `UNIX-LISTEN:${httpSocketPath},fork,reuseaddr`,
77
+ `TCP:localhost:${httpProxyPort},keepalive,keepidle=10,keepintvl=5,keepcnt=3`,
78
+ ];
79
+ logForDebugging(`Starting HTTP bridge: socat ${httpSocatArgs.join(' ')}`);
80
+ const httpBridgeProcess = spawn('socat', httpSocatArgs, {
81
+ stdio: 'ignore',
82
+ });
83
+ if (!httpBridgeProcess.pid) {
84
+ throw new Error('Failed to start HTTP bridge process');
85
+ }
86
+ // Start SOCKS bridge
87
+ const socksSocatArgs = [
88
+ `UNIX-LISTEN:${socksSocketPath},fork,reuseaddr`,
89
+ `TCP:localhost:${socksProxyPort},keepalive,keepidle=10,keepintvl=5,keepcnt=3`,
90
+ ];
91
+ logForDebugging(`Starting SOCKS bridge: socat ${socksSocatArgs.join(' ')}`);
92
+ const socksBridgeProcess = spawn('socat', socksSocatArgs, {
93
+ stdio: 'ignore',
94
+ });
95
+ if (!socksBridgeProcess.pid) {
96
+ // Clean up HTTP bridge
97
+ if (httpBridgeProcess.pid) {
98
+ try {
99
+ process.kill(httpBridgeProcess.pid, 'SIGTERM');
100
+ }
101
+ catch {
102
+ // Ignore errors
103
+ }
104
+ }
105
+ throw new Error('Failed to start SOCKS bridge process');
106
+ }
107
+ // Wait for both sockets to be ready
108
+ const maxAttempts = 5;
109
+ for (let i = 0; i < maxAttempts; i++) {
110
+ if (!httpBridgeProcess.pid ||
111
+ httpBridgeProcess.killed ||
112
+ !socksBridgeProcess.pid ||
113
+ socksBridgeProcess.killed) {
114
+ throw new Error('Linux bridge process died unexpectedly');
115
+ }
116
+ try {
117
+ // fs already imported
118
+ if (fs.existsSync(httpSocketPath) && fs.existsSync(socksSocketPath)) {
119
+ logForDebugging(`Linux bridges ready after ${i + 1} attempts`);
120
+ break;
121
+ }
122
+ }
123
+ catch (err) {
124
+ logForDebugging(`Error checking sockets (attempt ${i + 1}): ${err}`, {
125
+ level: 'error',
126
+ });
127
+ }
128
+ if (i === maxAttempts - 1) {
129
+ // Clean up both processes
130
+ if (httpBridgeProcess.pid) {
131
+ try {
132
+ process.kill(httpBridgeProcess.pid, 'SIGTERM');
133
+ }
134
+ catch {
135
+ // Ignore errors
136
+ }
137
+ }
138
+ if (socksBridgeProcess.pid) {
139
+ try {
140
+ process.kill(socksBridgeProcess.pid, 'SIGTERM');
141
+ }
142
+ catch {
143
+ // Ignore errors
144
+ }
145
+ }
146
+ throw new Error(`Failed to create bridge sockets after ${maxAttempts} attempts`);
147
+ }
148
+ await new Promise(resolve => setTimeout(resolve, i * 100));
149
+ }
150
+ return {
151
+ httpSocketPath,
152
+ socksSocketPath,
153
+ httpBridgeProcess,
154
+ socksBridgeProcess,
155
+ httpProxyPort,
156
+ socksProxyPort,
157
+ };
158
+ }
159
+ /**
160
+ * Build the command that runs inside the sandbox.
161
+ * Sets up HTTP proxy on port 3128 and SOCKS proxy on port 1080
162
+ */
163
+ function buildSandboxCommand(httpSocketPath, socksSocketPath, userCommand) {
164
+ // Use a single trap that kills all jobs on EXIT
165
+ // This avoids issues with $! variable expansion through shellquote
166
+ const innerScript = [
167
+ `socat TCP-LISTEN:3128,fork,reuseaddr UNIX-CONNECT:${httpSocketPath} >/dev/null 2>&1 &`,
168
+ `socat TCP-LISTEN:1080,fork,reuseaddr UNIX-CONNECT:${socksSocketPath} >/dev/null 2>&1 &`,
169
+ 'trap "kill %1 %2 2>/dev/null; exit" EXIT',
170
+ `eval ${shellquote.quote([userCommand])}`,
171
+ ].join('\n');
172
+ return `bash -c ${shellquote.quote([innerScript])}`;
173
+ }
174
+ /**
175
+ * Generate filesystem bind mount arguments for bwrap
176
+ */
177
+ async function generateFilesystemArgs(readConfig, writeConfig) {
178
+ const args = [];
179
+ // fs already imported
180
+ // Determine initial root mount based on write restrictions
181
+ if (writeConfig) {
182
+ // Write restrictions: Start with read-only root, then allow writes to specific paths
183
+ args.push('--ro-bind', '/', '/');
184
+ // Collect normalized allowed write paths for later checking
185
+ const allowedWritePaths = [];
186
+ // Allow writes to specific paths
187
+ for (const pathPattern of writeConfig.allowOnly || []) {
188
+ const normalizedPath = normalizePathForSandbox(pathPattern);
189
+ logForDebugging(`[Sandbox Linux] Processing write path: ${pathPattern} -> ${normalizedPath}`);
190
+ // Skip /dev/* paths since --dev /dev already handles them
191
+ if (normalizedPath.startsWith('/dev/')) {
192
+ logForDebugging(`[Sandbox Linux] Skipping /dev path: ${normalizedPath}`);
193
+ continue;
194
+ }
195
+ if (!fs.existsSync(normalizedPath)) {
196
+ logForDebugging(`[Sandbox Linux] Skipping non-existent write path: ${normalizedPath}`);
197
+ continue;
198
+ }
199
+ args.push('--bind', normalizedPath, normalizedPath);
200
+ allowedWritePaths.push(normalizedPath);
201
+ }
202
+ // Deny writes within allowed paths (user-specified + mandatory denies)
203
+ const denyPaths = [
204
+ ...(writeConfig.denyWithinAllow || []),
205
+ ...(await getMandatoryDenyWithinAllow()),
206
+ ];
207
+ for (const pathPattern of denyPaths) {
208
+ const normalizedPath = normalizePathForSandbox(pathPattern);
209
+ // Skip /dev/* paths since --dev /dev already handles them
210
+ if (normalizedPath.startsWith('/dev/')) {
211
+ continue;
212
+ }
213
+ // Skip non-existent paths
214
+ if (!fs.existsSync(normalizedPath)) {
215
+ logForDebugging(`[Sandbox Linux] Skipping non-existent deny path: ${normalizedPath}`);
216
+ continue;
217
+ }
218
+ // Only add deny binding if this path is within an allowed write path
219
+ // Otherwise it's already read-only from the initial --ro-bind / /
220
+ const isWithinAllowedPath = allowedWritePaths.some(allowedPath => normalizedPath.startsWith(allowedPath + '/') ||
221
+ normalizedPath === allowedPath);
222
+ if (isWithinAllowedPath) {
223
+ args.push('--ro-bind', normalizedPath, normalizedPath);
224
+ }
225
+ else {
226
+ logForDebugging(`[Sandbox Linux] Skipping deny path not within allowed paths: ${normalizedPath}`);
227
+ }
228
+ }
229
+ }
230
+ else {
231
+ // No write restrictions: Allow all writes
232
+ args.push('--bind', '/', '/');
233
+ }
234
+ // Handle read restrictions by mounting tmpfs over denied paths
235
+ const readDenyPaths = [...(readConfig?.denyOnly || [])];
236
+ // Always hide /etc/ssh/ssh_config.d to avoid permission issues with OrbStack
237
+ // SSH is very strict about config file permissions and ownership, and they can
238
+ // appear wrong inside the sandbox causing "Bad owner or permissions" errors
239
+ if (fs.existsSync('/etc/ssh/ssh_config.d')) {
240
+ readDenyPaths.push('/etc/ssh/ssh_config.d');
241
+ }
242
+ for (const pathPattern of readDenyPaths) {
243
+ const normalizedPath = normalizePathForSandbox(pathPattern);
244
+ if (!fs.existsSync(normalizedPath)) {
245
+ logForDebugging(`[Sandbox Linux] Skipping non-existent read deny path: ${normalizedPath}`);
246
+ continue;
247
+ }
248
+ const readDenyStat = fs.statSync(normalizedPath);
249
+ if (readDenyStat.isDirectory()) {
250
+ args.push('--tmpfs', normalizedPath);
251
+ }
252
+ else {
253
+ // For files, bind /dev/null instead of tmpfs
254
+ args.push('--ro-bind', '/dev/null', normalizedPath);
255
+ }
256
+ }
257
+ return args;
258
+ }
259
+ /**
260
+ * Wrap a command with sandbox restrictions on Linux
261
+ */
262
+ export async function wrapCommandWithSandboxLinux(params) {
263
+ const { command, hasNetworkRestrictions, hasFilesystemRestrictions, httpSocketPath, socksSocketPath, httpProxyPort, socksProxyPort, readConfig, writeConfig, enableWeakerNestedSandbox, } = params;
264
+ // Check if we need any sandboxing
265
+ if (!hasNetworkRestrictions && !hasFilesystemRestrictions) {
266
+ return command;
267
+ }
268
+ const bwrapArgs = [];
269
+ // By default, always unshare PID namespace and mount fresh /proc.
270
+ // If we don't have --unshare-pid, it is possible to escape the sandbox.
271
+ // If we don't have --proc, it is possible to read host /proc and leak information about code running
272
+ // outside the sandbox. But, --proc is not available when running in unprivileged docker containers
273
+ // so we support running without it if explicitly requested.
274
+ bwrapArgs.push('--unshare-pid');
275
+ if (!enableWeakerNestedSandbox) {
276
+ // Mount fresh /proc if PID namespace is isolated (secure mode)
277
+ bwrapArgs.push('--proc', '/proc');
278
+ }
279
+ // ========== NETWORK RESTRICTIONS ==========
280
+ if (hasNetworkRestrictions) {
281
+ // Only sandbox if we have network config and Linux bridges
282
+ if (!httpSocketPath || !socksSocketPath) {
283
+ throw new Error('Linux network sandboxing was requested but bridge socket paths are not available');
284
+ }
285
+ bwrapArgs.push('--unshare-net');
286
+ // Bind both sockets into the sandbox
287
+ bwrapArgs.push('--bind', httpSocketPath, httpSocketPath);
288
+ bwrapArgs.push('--bind', socksSocketPath, socksSocketPath);
289
+ // Add proxy environment variables
290
+ // HTTP_PROXY points to the socat listener inside the sandbox (port 3128)
291
+ // which forwards to the Unix socket that bridges to the host's proxy server
292
+ const proxyEnv = generateProxyEnvVars(3128, // Internal HTTP listener port
293
+ 1080);
294
+ bwrapArgs.push(...proxyEnv.flatMap((env) => {
295
+ const firstEq = env.indexOf('=');
296
+ const key = env.slice(0, firstEq);
297
+ const value = env.slice(firstEq + 1);
298
+ return ['--setenv', key, value];
299
+ }));
300
+ // Add host proxy port environment variables for debugging/transparency
301
+ // These show which host ports the Unix socket bridges connect to
302
+ if (httpProxyPort !== undefined) {
303
+ bwrapArgs.push('--setenv', 'CLAUDE_CODE_HOST_HTTP_PROXY_PORT', String(httpProxyPort));
304
+ }
305
+ if (socksProxyPort !== undefined) {
306
+ bwrapArgs.push('--setenv', 'CLAUDE_CODE_HOST_SOCKS_PROXY_PORT', String(socksProxyPort));
307
+ }
308
+ }
309
+ // ========== FILESYSTEM RESTRICTIONS ==========
310
+ const fsArgs = await generateFilesystemArgs(readConfig, writeConfig);
311
+ bwrapArgs.push(...fsArgs);
312
+ // Always bind /dev
313
+ bwrapArgs.push('--dev', '/dev');
314
+ // ========== COMMAND ==========
315
+ bwrapArgs.push('--', 'bash', '-c');
316
+ // If we have network restrictions, use the network bridge setup
317
+ // Otherwise, just run the command directly
318
+ if (hasNetworkRestrictions && httpSocketPath && socksSocketPath) {
319
+ bwrapArgs.push(buildSandboxCommand(httpSocketPath, socksSocketPath, command));
320
+ }
321
+ else {
322
+ bwrapArgs.push(command);
323
+ }
324
+ const wrappedCommand = shellquote.quote(['bwrap', ...bwrapArgs]);
325
+ const restrictions = [];
326
+ if (hasNetworkRestrictions)
327
+ restrictions.push('network');
328
+ if (hasFilesystemRestrictions)
329
+ restrictions.push('filesystem');
330
+ logForDebugging(`[Sandbox Linux] Wrapped command with bwrap (${restrictions.join(', ')} restrictions)`);
331
+ return wrappedCommand;
332
+ }
333
+ //# sourceMappingURL=linux-sandbox-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linux-sandbox-utils.js","sourceRoot":"","sources":["../../src/sandbox/linux-sandbox-utils.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { IgnoreViolationsConfig, FsReadRestrictionConfig, FsWriteRestrictionConfig } from './sandbox-schemas.js';
2
+ /**
3
+ * Check if macOS sandbox dependencies are available (synchronous)
4
+ * Returns true if rg (ripgrep) is installed, false otherwise
5
+ * Cached to avoid repeated system calls
6
+ */
7
+ export declare function hasMacOSSandboxDependenciesSync(): boolean;
8
+ export interface MacOSSandboxParams {
9
+ command: string;
10
+ httpProxyPort?: number;
11
+ socksProxyPort?: number;
12
+ needsNetworkRestriction: boolean;
13
+ allowUnixSockets?: string[];
14
+ allowLocalBinding?: boolean;
15
+ readConfig: FsReadRestrictionConfig | undefined;
16
+ writeConfig: FsWriteRestrictionConfig | undefined;
17
+ ignoreViolations?: IgnoreViolationsConfig | undefined;
18
+ }
19
+ export interface SandboxViolationEvent {
20
+ line: string;
21
+ command?: string;
22
+ encodedCommand?: string;
23
+ timestamp: Date;
24
+ }
25
+ export type SandboxViolationCallback = (violation: SandboxViolationEvent) => void;
26
+ /**
27
+ * Convert a glob pattern to a regular expression for macOS sandbox profiles
28
+ *
29
+ * This implements gitignore-style pattern matching to match the behavior of the
30
+ * `ignore` library used by the permission system/
31
+ *
32
+ * Supported patterns:
33
+ * - * matches any characters except / (e.g., *.ts matches foo.ts but not foo/bar.ts)
34
+ * - ** matches any characters including / (e.g., src/** /*.ts matches all .ts files in src/)
35
+ * - ? matches any single character except / (e.g., file?.txt matches file1.txt)
36
+ * - [abc] matches any character in the set (e.g., file[0-9].txt matches file3.txt)
37
+ *
38
+ * Note: This is designed for macOS sandbox (regex ...) syntax. The resulting regex
39
+ * will be used in sandbox profiles like: (deny file-write* (regex "pattern"))
40
+ *
41
+ * Exported for testing purposes.
42
+ */
43
+ export declare function globToRegex(globPattern: string): string;
44
+ /**
45
+ * Wrap command with macOS sandbox
46
+ */
47
+ export declare function wrapCommandWithSandboxMacOS(params: MacOSSandboxParams): Promise<string>;
48
+ /**
49
+ * Start monitoring macOS system logs for sandbox violations
50
+ * Look for sandbox-related kernel deny events ending in {logTag}
51
+ */
52
+ export declare function startMacOSSandboxLogMonitor(callback: SandboxViolationCallback, ignoreViolations?: IgnoreViolationsConfig): () => void;
53
+ //# sourceMappingURL=macos-sandbox-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macos-sandbox-utils.d.ts","sourceRoot":"","sources":["../../src/sandbox/macos-sandbox-utils.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,sBAAsB,CAAA;AAK7B;;;;GAIG;AACH,wBAAgB,+BAA+B,IAAI,OAAO,CAiBzD;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,uBAAuB,EAAE,OAAO,CAAA;IAChC,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,UAAU,EAAE,uBAAuB,GAAG,SAAS,CAAA;IAC/C,WAAW,EAAE,wBAAwB,GAAG,SAAS,CAAA;IACjD,gBAAgB,CAAC,EAAE,sBAAsB,GAAG,SAAS,CAAA;CACtD;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,SAAS,EAAE,IAAI,CAAA;CAChB;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,SAAS,EAAE,qBAAqB,KAC7B,IAAI,CAAA;AAIT;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAkBvD;AAmYD;;GAEG;AACH,wBAAsB,2BAA2B,CAC/C,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CA2DjB;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,wBAAwB,EAClC,gBAAgB,CAAC,EAAE,sBAAsB,GACxC,MAAM,IAAI,CA8GZ"}