@agentick/sandbox-local 0.2.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 (87) hide show
  1. package/README.md +211 -0
  2. package/dist/executor/base.d.ts +15 -0
  3. package/dist/executor/base.d.ts.map +1 -0
  4. package/dist/executor/base.js +20 -0
  5. package/dist/executor/base.js.map +1 -0
  6. package/dist/executor/darwin.d.ts +16 -0
  7. package/dist/executor/darwin.d.ts.map +1 -0
  8. package/dist/executor/darwin.js +44 -0
  9. package/dist/executor/darwin.js.map +1 -0
  10. package/dist/executor/linux.d.ts +22 -0
  11. package/dist/executor/linux.d.ts.map +1 -0
  12. package/dist/executor/linux.js +50 -0
  13. package/dist/executor/linux.js.map +1 -0
  14. package/dist/executor/select.d.ts +12 -0
  15. package/dist/executor/select.d.ts.map +1 -0
  16. package/dist/executor/select.js +23 -0
  17. package/dist/executor/select.js.map +1 -0
  18. package/dist/executor/types.d.ts +29 -0
  19. package/dist/executor/types.d.ts.map +1 -0
  20. package/dist/executor/types.js +4 -0
  21. package/dist/executor/types.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +12 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/linux/bwrap.d.ts +11 -0
  27. package/dist/linux/bwrap.d.ts.map +1 -0
  28. package/dist/linux/bwrap.js +46 -0
  29. package/dist/linux/bwrap.js.map +1 -0
  30. package/dist/linux/cgroup.d.ts +27 -0
  31. package/dist/linux/cgroup.d.ts.map +1 -0
  32. package/dist/linux/cgroup.js +80 -0
  33. package/dist/linux/cgroup.js.map +1 -0
  34. package/dist/linux/unshare.d.ts +11 -0
  35. package/dist/linux/unshare.d.ts.map +1 -0
  36. package/dist/linux/unshare.js +22 -0
  37. package/dist/linux/unshare.js.map +1 -0
  38. package/dist/local-sandbox.d.ts +42 -0
  39. package/dist/local-sandbox.d.ts.map +1 -0
  40. package/dist/local-sandbox.js +235 -0
  41. package/dist/local-sandbox.js.map +1 -0
  42. package/dist/network/ca.d.ts +38 -0
  43. package/dist/network/ca.d.ts.map +1 -0
  44. package/dist/network/ca.js +143 -0
  45. package/dist/network/ca.js.map +1 -0
  46. package/dist/network/proxy.d.ts +46 -0
  47. package/dist/network/proxy.d.ts.map +1 -0
  48. package/dist/network/proxy.js +144 -0
  49. package/dist/network/proxy.js.map +1 -0
  50. package/dist/network/rules.d.ts +23 -0
  51. package/dist/network/rules.d.ts.map +1 -0
  52. package/dist/network/rules.js +64 -0
  53. package/dist/network/rules.js.map +1 -0
  54. package/dist/paths.d.ts +29 -0
  55. package/dist/paths.d.ts.map +1 -0
  56. package/dist/paths.js +129 -0
  57. package/dist/paths.js.map +1 -0
  58. package/dist/platform/detect.d.ts +17 -0
  59. package/dist/platform/detect.d.ts.map +1 -0
  60. package/dist/platform/detect.js +114 -0
  61. package/dist/platform/detect.js.map +1 -0
  62. package/dist/platform/types.d.ts +16 -0
  63. package/dist/platform/types.d.ts.map +1 -0
  64. package/dist/platform/types.js +4 -0
  65. package/dist/platform/types.js.map +1 -0
  66. package/dist/provider.d.ts +33 -0
  67. package/dist/provider.d.ts.map +1 -0
  68. package/dist/provider.js +137 -0
  69. package/dist/provider.js.map +1 -0
  70. package/dist/resources.d.ts +30 -0
  71. package/dist/resources.d.ts.map +1 -0
  72. package/dist/resources.js +94 -0
  73. package/dist/resources.js.map +1 -0
  74. package/dist/seatbelt/profile.d.ts +30 -0
  75. package/dist/seatbelt/profile.d.ts.map +1 -0
  76. package/dist/seatbelt/profile.js +106 -0
  77. package/dist/seatbelt/profile.js.map +1 -0
  78. package/dist/testing.d.ts +22 -0
  79. package/dist/testing.d.ts.map +1 -0
  80. package/dist/testing.js +39 -0
  81. package/dist/testing.js.map +1 -0
  82. package/dist/workspace.d.ts +30 -0
  83. package/dist/workspace.d.ts.map +1 -0
  84. package/dist/workspace.js +68 -0
  85. package/dist/workspace.js.map +1 -0
  86. package/package.json +64 -0
  87. package/src/index.ts +17 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.d.ts","sourceRoot":"","sources":["../../src/network/proxy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGrE,MAAM,WAAW,iBAAiB;IAChC,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,4DAA4D;IAC5D,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IAE1C,wCAAwC;IACxC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IAExC,iDAAiD;IACjD,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IACrD,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAwB;IACxC,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAS;gBAEX,KAAK,EAAE,WAAW,EAAE,EAAE,MAAM,CAAC,EAAE,iBAAiB;IAU5D,8EAA8E;IAC9E,IAAI,QAAQ,IAAI,MAAM,CAGrB;IAED,8BAA8B;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB5B,6BAA6B;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAU3B,iDAAiD;IACjD,WAAW,IAAI,cAAc,EAAE;IAI/B,4CAA4C;IAC5C,OAAO,CAAC,iBAAiB;IAgDzB,+CAA+C;IAC/C,OAAO,CAAC,aAAa;IAmCrB,sDAAsD;IACtD,OAAO,CAAC,UAAU;CAiBnB"}
@@ -0,0 +1,144 @@
1
+ /**
2
+ * HTTP/HTTPS Proxy Server
3
+ *
4
+ * Binds an HTTP proxy server that intercepts HTTP traffic and applies
5
+ * network rules. HTTPS connections are handled at the CONNECT level:
6
+ * allowed connections get a passthrough tunnel, denied ones are rejected.
7
+ *
8
+ * No MITM/TLS termination — HTTPS content is opaque. This covers the
9
+ * primary use case (domain-level allow/deny) without the complexity of
10
+ * CA generation and TLS interception.
11
+ */
12
+ import { createServer, request as httpRequest } from "node:http";
13
+ import * as net from "node:net";
14
+ import { matchRequest } from "./rules";
15
+ export class NetworkProxyServer {
16
+ rules;
17
+ config;
18
+ server;
19
+ auditLog = [];
20
+ _proxyUrl;
21
+ _port;
22
+ constructor(rules, config) {
23
+ this.rules = rules;
24
+ this.config = {
25
+ port: config?.port ?? 0,
26
+ onRequest: config?.onRequest ?? (() => { }),
27
+ onBlock: config?.onBlock ?? (() => { }),
28
+ maxAuditEntries: config?.maxAuditEntries ?? 10000,
29
+ };
30
+ }
31
+ /** The proxy URL (e.g. "http://127.0.0.1:12345"). Available after start(). */
32
+ get proxyUrl() {
33
+ if (!this._proxyUrl)
34
+ throw new Error("Proxy not started");
35
+ return this._proxyUrl;
36
+ }
37
+ /** Start the proxy server. */
38
+ async start() {
39
+ this.server = createServer((req, res) => this.handleHttpRequest(req, res));
40
+ this.server.on("connect", (req, socket, head) => this.handleConnect(req, socket, head));
41
+ await new Promise((resolve, reject) => {
42
+ this.server.listen(this.config.port, "127.0.0.1", () => {
43
+ const addr = this.server.address();
44
+ if (typeof addr === "object" && addr) {
45
+ this._port = addr.port;
46
+ this._proxyUrl = `http://127.0.0.1:${addr.port}`;
47
+ }
48
+ resolve();
49
+ });
50
+ this.server.on("error", reject);
51
+ });
52
+ }
53
+ /** Stop the proxy server. */
54
+ async stop() {
55
+ await new Promise((resolve) => {
56
+ if (this.server) {
57
+ this.server.close(() => resolve());
58
+ }
59
+ else {
60
+ resolve();
61
+ }
62
+ });
63
+ }
64
+ /** Get the audit log of all proxied requests. */
65
+ getAuditLog() {
66
+ return [...this.auditLog];
67
+ }
68
+ /** Handle an HTTP request (non-CONNECT). */
69
+ handleHttpRequest(req, res) {
70
+ const url = req.url ?? "/";
71
+ const host = req.headers.host ?? "unknown";
72
+ const port = parseInt(host.split(":")[1] ?? "80", 10);
73
+ const entry = this.logRequest(url, req.method ?? "GET", host.split(":")[0], port);
74
+ const match = matchRequest({ host: host.split(":")[0], port, method: req.method ?? "GET", url }, this.rules);
75
+ entry.matchedRule = match.rule;
76
+ if (match.action === "deny") {
77
+ entry.blocked = true;
78
+ this.config.onBlock(entry);
79
+ res.writeHead(403, { "Content-Type": "text/plain" });
80
+ res.end("Blocked by sandbox network rules");
81
+ return;
82
+ }
83
+ this.config.onRequest(entry);
84
+ // Forward the request
85
+ const parsedUrl = new URL(url);
86
+ const proxyReq = httpRequest({
87
+ hostname: parsedUrl.hostname,
88
+ port: parsedUrl.port || 80,
89
+ path: parsedUrl.pathname + parsedUrl.search,
90
+ method: req.method,
91
+ headers: { ...req.headers, host: parsedUrl.host },
92
+ }, (proxyRes) => {
93
+ res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
94
+ proxyRes.pipe(res);
95
+ });
96
+ proxyReq.on("error", () => {
97
+ res.writeHead(502, { "Content-Type": "text/plain" });
98
+ res.end("Proxy connection error");
99
+ });
100
+ req.pipe(proxyReq);
101
+ }
102
+ /** Handle a CONNECT request (HTTPS tunnel). */
103
+ handleConnect(req, socket, _head) {
104
+ const [host, portStr] = (req.url ?? "").split(":");
105
+ const port = parseInt(portStr ?? "443", 10);
106
+ const entry = this.logRequest(`https://${host}:${port}`, "CONNECT", host, port);
107
+ const match = matchRequest({ host, port, method: "CONNECT", url: `https://${host}:${port}` }, this.rules);
108
+ entry.matchedRule = match.rule;
109
+ if (match.action === "deny") {
110
+ entry.blocked = true;
111
+ this.config.onBlock(entry);
112
+ socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
113
+ socket.end();
114
+ return;
115
+ }
116
+ this.config.onRequest(entry);
117
+ // Passthrough: direct tunnel to target (no TLS interception)
118
+ const { connect } = net;
119
+ const remote = connect(port, host, () => {
120
+ socket.write("HTTP/1.1 200 Connection Established\r\n\r\n");
121
+ socket.pipe(remote);
122
+ remote.pipe(socket);
123
+ });
124
+ remote.on("error", () => socket.destroy());
125
+ socket.on("error", () => remote.destroy());
126
+ }
127
+ /** Log a request and trim the audit log if needed. */
128
+ logRequest(url, method, host, port) {
129
+ const entry = {
130
+ url,
131
+ method,
132
+ host,
133
+ port,
134
+ timestamp: Date.now(),
135
+ blocked: false,
136
+ };
137
+ this.auditLog.push(entry);
138
+ if (this.auditLog.length > this.config.maxAuditEntries) {
139
+ this.auditLog = this.auditLog.slice(-this.config.maxAuditEntries);
140
+ }
141
+ return entry;
142
+ }
143
+ }
144
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy.js","sourceRoot":"","sources":["../../src/network/proxy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Network Rule Matching Engine
3
+ *
4
+ * Evaluates requests against an ordered list of NetworkRules.
5
+ * First match wins; default action is deny.
6
+ */
7
+ import type { NetworkRule } from "@agentick/sandbox";
8
+ export interface RequestInfo {
9
+ host: string;
10
+ port: number;
11
+ method: string;
12
+ url: string;
13
+ }
14
+ export interface MatchResult {
15
+ action: "allow" | "deny";
16
+ rule?: NetworkRule;
17
+ }
18
+ /**
19
+ * Match a request against an ordered list of rules.
20
+ * First matching rule wins. Default: deny.
21
+ */
22
+ export declare function matchRequest(request: RequestInfo, rules: NetworkRule[]): MatchResult;
23
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/network/rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,GAAG,MAAM,CAAC;IACzB,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,WAAW,CAOpF"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Network Rule Matching Engine
3
+ *
4
+ * Evaluates requests against an ordered list of NetworkRules.
5
+ * First match wins; default action is deny.
6
+ */
7
+ /**
8
+ * Match a request against an ordered list of rules.
9
+ * First matching rule wins. Default: deny.
10
+ */
11
+ export function matchRequest(request, rules) {
12
+ for (const rule of rules) {
13
+ if (ruleMatches(request, rule)) {
14
+ return { action: rule.action, rule };
15
+ }
16
+ }
17
+ return { action: "deny" };
18
+ }
19
+ function ruleMatches(request, rule) {
20
+ // Domain check
21
+ if (rule.domain !== undefined) {
22
+ if (!matchDomain(request.host, rule.domain))
23
+ return false;
24
+ }
25
+ // Port check
26
+ if (rule.port !== undefined) {
27
+ if (request.port !== rule.port)
28
+ return false;
29
+ }
30
+ // Method check
31
+ if (rule.methods !== undefined && rule.methods.length > 0) {
32
+ const upperMethod = request.method.toUpperCase();
33
+ if (!rule.methods.some((m) => m.toUpperCase() === upperMethod))
34
+ return false;
35
+ }
36
+ // URL pattern check
37
+ if (rule.urlPattern !== undefined) {
38
+ try {
39
+ const regex = new RegExp(rule.urlPattern);
40
+ if (!regex.test(request.url))
41
+ return false;
42
+ }
43
+ catch {
44
+ // Invalid regex — treat as no match
45
+ return false;
46
+ }
47
+ }
48
+ return true;
49
+ }
50
+ /**
51
+ * Match a hostname against a domain pattern.
52
+ * Supports wildcards: "*.example.com" matches "sub.example.com" but not "example.com".
53
+ * Exact match: "example.com" matches only "example.com".
54
+ */
55
+ function matchDomain(hostname, pattern) {
56
+ const host = hostname.toLowerCase();
57
+ const pat = pattern.toLowerCase();
58
+ if (pat.startsWith("*.")) {
59
+ const suffix = pat.slice(1); // ".example.com"
60
+ return host.endsWith(suffix) && host.length > suffix.length;
61
+ }
62
+ return host === pat;
63
+ }
64
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/network/rules.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,OAAoB,EAAE,KAAoB;IACrE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,WAAW,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;YAC/B,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;QACvC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;AAC5B,CAAC;AAED,SAAS,WAAW,CAAC,OAAoB,EAAE,IAAiB;IAC1D,eAAe;IACf,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAC;IAC5D,CAAC;IAED,aAAa;IACb,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,OAAO,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAC;IAC/C,CAAC;IAED,eAAe;IACf,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1D,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;QACjD,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,WAAW,CAAC;YAAE,OAAO,KAAK,CAAC;IAC/E,CAAC;IAED,oBAAoB;IACpB,IAAI,IAAI,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;gBAAE,OAAO,KAAK,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,oCAAoC;YACpC,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAAC,QAAgB,EAAE,OAAe;IACpD,MAAM,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAElC,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,iBAAiB;QAC9C,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC9D,CAAC;IAED,OAAO,IAAI,KAAK,GAAG,CAAC;AACtB,CAAC"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Path Safety
3
+ *
4
+ * Resolves paths, follows symlinks, validates bounds against workspace and mounts.
5
+ * Rejects path traversal, null bytes, and escape attempts.
6
+ */
7
+ import type { ResolvedMount } from "./executor/types";
8
+ /** Environment variables that must never be inherited. */
9
+ export declare const ENV_BLOCKLIST: Set<string>;
10
+ /**
11
+ * Resolve and validate a path is within the workspace or an allowed mount.
12
+ *
13
+ * IMPORTANT: workspacePath must already be realpath-resolved (done once at
14
+ * creation time by createWorkspace). Mount hostPaths are also pre-resolved
15
+ * by resolveMounts. This avoids redundant realpath calls on every file op.
16
+ *
17
+ * @param inputPath - The path to resolve (relative to workspace or absolute)
18
+ * @param workspacePath - The sandbox workspace root (must be pre-resolved via realpath)
19
+ * @param mode - Required access mode ("read" or "write")
20
+ * @param mounts - Resolved mounts with host paths and modes
21
+ * @returns The resolved absolute path
22
+ * @throws On null bytes, traversal, or out-of-bounds access
23
+ */
24
+ export declare function resolveSafePath(inputPath: string, workspacePath: string, mode: "read" | "write", mounts?: ResolvedMount[]): Promise<string>;
25
+ /**
26
+ * Filter dangerous environment variables from an env record.
27
+ */
28
+ export declare function filterEnv(env: Record<string, string>): Record<string, string>;
29
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,0DAA0D;AAC1D,eAAO,MAAM,aAAa,aAMxB,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,MAAM,GAAG,OAAO,EACtB,MAAM,GAAE,aAAa,EAAO,GAC3B,OAAO,CAAC,MAAM,CAAC,CAwEjB;AAwBD;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQ7E"}
package/dist/paths.js ADDED
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Path Safety
3
+ *
4
+ * Resolves paths, follows symlinks, validates bounds against workspace and mounts.
5
+ * Rejects path traversal, null bytes, and escape attempts.
6
+ */
7
+ import { realpath } from "node:fs/promises";
8
+ import { resolve, isAbsolute } from "node:path";
9
+ /** Environment variables that must never be inherited. */
10
+ export const ENV_BLOCKLIST = new Set([
11
+ "LD_PRELOAD",
12
+ "LD_LIBRARY_PATH",
13
+ "DYLD_INSERT_LIBRARIES",
14
+ "DYLD_LIBRARY_PATH",
15
+ "DYLD_FRAMEWORK_PATH",
16
+ ]);
17
+ /**
18
+ * Resolve and validate a path is within the workspace or an allowed mount.
19
+ *
20
+ * IMPORTANT: workspacePath must already be realpath-resolved (done once at
21
+ * creation time by createWorkspace). Mount hostPaths are also pre-resolved
22
+ * by resolveMounts. This avoids redundant realpath calls on every file op.
23
+ *
24
+ * @param inputPath - The path to resolve (relative to workspace or absolute)
25
+ * @param workspacePath - The sandbox workspace root (must be pre-resolved via realpath)
26
+ * @param mode - Required access mode ("read" or "write")
27
+ * @param mounts - Resolved mounts with host paths and modes
28
+ * @returns The resolved absolute path
29
+ * @throws On null bytes, traversal, or out-of-bounds access
30
+ */
31
+ export async function resolveSafePath(inputPath, workspacePath, mode, mounts = []) {
32
+ // Reject null bytes
33
+ if (inputPath.includes("\0")) {
34
+ throw new Error("Path contains null bytes");
35
+ }
36
+ // workspacePath is pre-resolved via realpath by createWorkspace()
37
+ const realWorkspace = workspacePath;
38
+ // Resolve to absolute (relative paths are relative to workspace)
39
+ const absolute = isAbsolute(inputPath) ? inputPath : resolve(realWorkspace, inputPath);
40
+ // Try to follow symlinks to the real path. If the file doesn't exist yet
41
+ // (write mode), resolve the parent directory instead.
42
+ let resolved;
43
+ try {
44
+ resolved = await realpath(absolute);
45
+ }
46
+ catch (err) {
47
+ if (err.code === "ENOENT") {
48
+ if (mode === "write") {
49
+ // File (or parent dirs) don't exist yet — walk up to find the
50
+ // closest existing ancestor, then verify it's within bounds
51
+ resolved = await resolveNonExistentPath(absolute);
52
+ }
53
+ else {
54
+ // For reads, if the file doesn't exist, resolve what we can and
55
+ // check bounds using path manipulation (to catch traversal attempts
56
+ // even when target doesn't exist)
57
+ resolved = resolve(absolute);
58
+ // Normalize out .. components
59
+ if (!resolved.startsWith(realWorkspace + "/") && resolved !== realWorkspace) {
60
+ // Check mounts (hostPaths are pre-resolved by resolveMounts)
61
+ let inMount = false;
62
+ for (const mount of mounts) {
63
+ if (resolved === mount.hostPath || resolved.startsWith(mount.hostPath + "/")) {
64
+ inMount = true;
65
+ break;
66
+ }
67
+ }
68
+ if (!inMount) {
69
+ throw new Error(`Path escapes sandbox: ${inputPath} resolves to ${resolved} (workspace: ${realWorkspace})`);
70
+ }
71
+ }
72
+ return resolved;
73
+ }
74
+ }
75
+ else {
76
+ throw err;
77
+ }
78
+ }
79
+ // Check workspace bounds
80
+ if (resolved === realWorkspace || resolved.startsWith(realWorkspace + "/")) {
81
+ return resolved;
82
+ }
83
+ // Check mounts (hostPaths are pre-resolved by resolveMounts)
84
+ for (const mount of mounts) {
85
+ const inMount = resolved === mount.hostPath || resolved.startsWith(mount.hostPath + "/");
86
+ if (inMount) {
87
+ if (mode === "write" && mount.mode === "ro") {
88
+ throw new Error(`Write access denied: ${inputPath} resolves to read-only mount ${mount.sandboxPath}`);
89
+ }
90
+ return resolved;
91
+ }
92
+ }
93
+ throw new Error(`Path escapes sandbox: ${inputPath} resolves to ${resolved} (workspace: ${realWorkspace})`);
94
+ }
95
+ /**
96
+ * Walk up the path tree to find the closest existing ancestor,
97
+ * then append the remaining segments. Used for write mode when
98
+ * the target file (and potentially parent dirs) don't exist yet.
99
+ */
100
+ async function resolveNonExistentPath(absolute) {
101
+ let ancestor = absolute;
102
+ let suffix = "";
103
+ while (ancestor !== "/" && ancestor !== ".") {
104
+ const parent = resolve(ancestor, "..");
105
+ suffix = ancestor.slice(parent.length) + suffix;
106
+ ancestor = parent;
107
+ try {
108
+ const resolvedAncestor = await realpath(ancestor);
109
+ return resolvedAncestor + suffix;
110
+ }
111
+ catch {
112
+ // Keep walking up
113
+ }
114
+ }
115
+ throw new Error(`No accessible ancestor for path: ${absolute}`);
116
+ }
117
+ /**
118
+ * Filter dangerous environment variables from an env record.
119
+ */
120
+ export function filterEnv(env) {
121
+ const filtered = {};
122
+ for (const [key, value] of Object.entries(env)) {
123
+ if (!ENV_BLOCKLIST.has(key)) {
124
+ filtered[key] = value;
125
+ }
126
+ }
127
+ return filtered;
128
+ }
129
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Platform capability detection.
3
+ *
4
+ * Probes the OS for sandbox features: macOS sandbox-exec, Linux bubblewrap/unshare,
5
+ * cgroups v2, user namespaces. Result is cached after first call.
6
+ */
7
+ import type { PlatformCapabilities, SandboxStrategy } from "./types";
8
+ /**
9
+ * Detect platform sandbox capabilities.
10
+ * Result is cached — safe to call multiple times.
11
+ */
12
+ export declare function detectCapabilities(): Promise<PlatformCapabilities>;
13
+ /** Select the best strategy given capabilities and optional override. */
14
+ export declare function selectStrategy(caps: PlatformCapabilities, override?: SandboxStrategy | "auto"): SandboxStrategy;
15
+ /** Reset the capability cache (for testing). */
16
+ export declare function resetCapabilitiesCache(): void;
17
+ //# sourceMappingURL=detect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/platform/detect.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA0BrE;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,oBAAoB,CAAC,CA6CxE;AAED,yEAAyE;AACzE,wBAAgB,cAAc,CAC5B,IAAI,EAAE,oBAAoB,EAC1B,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,GAClC,eAAe,CA0BjB;AAED,gDAAgD;AAChD,wBAAgB,sBAAsB,IAAI,IAAI,CAE7C"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Platform capability detection.
3
+ *
4
+ * Probes the OS for sandbox features: macOS sandbox-exec, Linux bubblewrap/unshare,
5
+ * cgroups v2, user namespaces. Result is cached after first call.
6
+ */
7
+ import { execFile } from "node:child_process";
8
+ import { access, readFile } from "node:fs/promises";
9
+ import { constants } from "node:fs";
10
+ import { promisify } from "node:util";
11
+ const execFileAsync = promisify(execFile);
12
+ let cached;
13
+ /** Check if a binary exists on PATH. */
14
+ async function which(name) {
15
+ try {
16
+ await execFileAsync("which", [name]);
17
+ return true;
18
+ }
19
+ catch {
20
+ return false;
21
+ }
22
+ }
23
+ /** Check if a file exists and is readable. */
24
+ async function readable(path) {
25
+ try {
26
+ await access(path, constants.R_OK);
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Detect platform sandbox capabilities.
35
+ * Result is cached — safe to call multiple times.
36
+ */
37
+ export async function detectCapabilities() {
38
+ if (cached)
39
+ return cached;
40
+ const rawPlatform = process.platform;
41
+ const platform = rawPlatform === "darwin" || rawPlatform === "linux" || rawPlatform === "win32"
42
+ ? rawPlatform
43
+ : "unknown";
44
+ const base = {
45
+ platform,
46
+ arch: process.arch,
47
+ hasSandboxExec: false,
48
+ hasBwrap: false,
49
+ hasUnshare: false,
50
+ hasCgroupsV2: false,
51
+ userNamespaces: false,
52
+ uid: process.getuid?.() ?? -1,
53
+ recommended: "none",
54
+ };
55
+ if (platform === "darwin") {
56
+ base.hasSandboxExec = await readable("/usr/bin/sandbox-exec");
57
+ base.recommended = base.hasSandboxExec ? "seatbelt" : "none";
58
+ }
59
+ else if (platform === "linux") {
60
+ const [hasBwrap, hasUnshare, hasCgroups, userNs] = await Promise.all([
61
+ which("bwrap"),
62
+ which("unshare"),
63
+ readable("/sys/fs/cgroup/cgroup.controllers"),
64
+ readFile("/proc/sys/kernel/unprivileged_userns_clone", "utf-8")
65
+ .then((v) => v.trim() === "1")
66
+ .catch(() => false),
67
+ ]);
68
+ base.hasBwrap = hasBwrap;
69
+ base.hasUnshare = hasUnshare;
70
+ base.hasCgroupsV2 = hasCgroups;
71
+ base.userNamespaces = userNs;
72
+ if (hasBwrap)
73
+ base.recommended = "bwrap";
74
+ else if (hasUnshare && userNs)
75
+ base.recommended = "unshare";
76
+ else
77
+ base.recommended = "none";
78
+ }
79
+ cached = base;
80
+ return base;
81
+ }
82
+ /** Select the best strategy given capabilities and optional override. */
83
+ export function selectStrategy(caps, override) {
84
+ if (!override || override === "auto")
85
+ return caps.recommended;
86
+ // Validate the override is available
87
+ switch (override) {
88
+ case "seatbelt":
89
+ if (!caps.hasSandboxExec) {
90
+ throw new Error("sandbox-exec not available on this platform");
91
+ }
92
+ return "seatbelt";
93
+ case "bwrap":
94
+ if (!caps.hasBwrap) {
95
+ throw new Error("bubblewrap (bwrap) not found on PATH");
96
+ }
97
+ return "bwrap";
98
+ case "unshare":
99
+ if (!caps.hasUnshare) {
100
+ throw new Error("unshare not found on PATH");
101
+ }
102
+ if (!caps.userNamespaces) {
103
+ throw new Error("user namespaces not available");
104
+ }
105
+ return "unshare";
106
+ case "none":
107
+ return "none";
108
+ }
109
+ }
110
+ /** Reset the capability cache (for testing). */
111
+ export function resetCapabilitiesCache() {
112
+ cached = undefined;
113
+ }
114
+ //# sourceMappingURL=detect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"detect.js","sourceRoot":"","sources":["../../src/platform/detect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Platform detection types.
3
+ */
4
+ export type SandboxStrategy = "seatbelt" | "bwrap" | "unshare" | "none";
5
+ export interface PlatformCapabilities {
6
+ platform: "darwin" | "linux" | "win32" | "unknown";
7
+ arch: string;
8
+ hasSandboxExec: boolean;
9
+ hasBwrap: boolean;
10
+ hasUnshare: boolean;
11
+ hasCgroupsV2: boolean;
12
+ userNamespaces: boolean;
13
+ uid: number;
14
+ recommended: SandboxStrategy;
15
+ }
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/platform/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,GAAG,MAAM,CAAC;AAExE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,IAAI,EAAE,MAAM,CAAC;IAGb,cAAc,EAAE,OAAO,CAAC;IAGxB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IAGxB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,eAAe,CAAC;CAC9B"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Platform detection types.
3
+ */
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/platform/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Local Sandbox Provider
3
+ *
4
+ * Factory function that creates a SandboxProvider backed by OS-level sandboxing.
5
+ */
6
+ import type { SandboxProvider } from "@agentick/sandbox";
7
+ import type { SandboxStrategy } from "./platform/types";
8
+ import type { ProxyServerConfig } from "./network/proxy";
9
+ export interface LocalProviderConfig {
10
+ /** Sandbox strategy. "auto" detects the best available. Default: "auto". */
11
+ strategy?: SandboxStrategy | "auto";
12
+ /** Network proxy configuration. */
13
+ network?: ProxyServerConfig;
14
+ /** Base directory for temp workspaces. Default: os.tmpdir(). */
15
+ tmpBase?: string;
16
+ /** Whether to clean up auto-created workspaces on destroy. Default: true. */
17
+ cleanupWorkspace?: boolean;
18
+ }
19
+ /**
20
+ * Create a local sandbox provider.
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import { localProvider } from "@agentick/sandbox-local";
25
+ *
26
+ * const provider = localProvider();
27
+ * const sandbox = await provider.create({ workspace: true });
28
+ * const result = await sandbox.exec("echo hello");
29
+ * await sandbox.destroy();
30
+ * ```
31
+ */
32
+ export declare function localProvider(config?: LocalProviderConfig): SandboxProvider;
33
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,eAAe,EAKhB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AASxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC;IAEpC,mCAAmC;IACnC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAE5B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,mBAAmB,GAAG,eAAe,CAmH3E"}