@utdk/isolate 0.1.0-dev.646adf4
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/.turbo/turbo-build.log +4 -0
- package/LICENSE +373 -0
- package/README.md +126 -0
- package/__tests__/bridge.test.ts +214 -0
- package/__tests__/isolate.test.ts +275 -0
- package/__tests__/sandbox.test.ts +126 -0
- package/dist/__tests__/bridge.test.d.ts +1 -0
- package/dist/__tests__/bridge.test.js +168 -0
- package/dist/__tests__/bridge.test.js.map +1 -0
- package/dist/__tests__/isolate.test.d.ts +11 -0
- package/dist/__tests__/isolate.test.js +218 -0
- package/dist/__tests__/isolate.test.js.map +1 -0
- package/dist/__tests__/sandbox.test.d.ts +1 -0
- package/dist/__tests__/sandbox.test.js +104 -0
- package/dist/__tests__/sandbox.test.js.map +1 -0
- package/dist/src/bridge.d.ts +71 -0
- package/dist/src/bridge.js +151 -0
- package/dist/src/bridge.js.map +1 -0
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +5 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/isolate.d.ts +97 -0
- package/dist/src/isolate.js +174 -0
- package/dist/src/isolate.js.map +1 -0
- package/dist/src/sandbox.d.ts +40 -0
- package/dist/src/sandbox.js +116 -0
- package/dist/src/sandbox.js.map +1 -0
- package/dist/src/types.d.ts +63 -0
- package/dist/src/types.js +10 -0
- package/dist/src/types.js.map +1 -0
- package/package.json +37 -0
- package/src/bridge.ts +219 -0
- package/src/index.ts +11 -0
- package/src/isolate.ts +202 -0
- package/src/sandbox.ts +139 -0
- package/src/types.ts +73 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credential bridge for the Isolate runtime.
|
|
3
|
+
*
|
|
4
|
+
* The bridge is the ONLY channel through which credentials and provider
|
|
5
|
+
* operations enter the sandboxed vm context. It is created in the host context
|
|
6
|
+
* (where Node.js module loading works normally) and exposed as `__bridge__`
|
|
7
|
+
* inside the sandbox.
|
|
8
|
+
*
|
|
9
|
+
* Design:
|
|
10
|
+
* - The provider module is dynamically imported in the HOST context, so it
|
|
11
|
+
* can resolve its own dependencies normally.
|
|
12
|
+
* - Credentials are mapped to an `AuthProvider` from `@utdk/common`, which
|
|
13
|
+
* injects them into HTTP request headers at call time.
|
|
14
|
+
* - The operation function is resolved via dot-notation path on the client.
|
|
15
|
+
* - The sandbox script calls `await __bridge__.call(args)` — it never
|
|
16
|
+
* touches `process.env` or any host filesystem API.
|
|
17
|
+
*/
|
|
18
|
+
import { ApiKey, BearerToken } from "@utdk/common";
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Credential resolution
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
/**
|
|
23
|
+
* Resolves an `AuthProvider` from a flat credentials map and the provider's
|
|
24
|
+
* `utdk.auth` config array (from its `package.json`).
|
|
25
|
+
*
|
|
26
|
+
* Supported patterns:
|
|
27
|
+
* api_key – Bearer header: `api_key: "Bearer ${TOKEN_NAME}"`
|
|
28
|
+
* api_key – Raw header: `api_key: "${TOKEN_NAME}"`
|
|
29
|
+
* oauth2 – Pre-resolved: looks for `PROVIDER_ACCESS_TOKEN` key
|
|
30
|
+
*
|
|
31
|
+
* Falls back to `BearerToken(firstValue)` when the auth config is absent or
|
|
32
|
+
* the pattern is not recognised.
|
|
33
|
+
*/
|
|
34
|
+
export function resolveAuthProvider(credentials, authConfigs = []) {
|
|
35
|
+
if (Object.keys(credentials).length === 0) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
for (const config of authConfigs) {
|
|
39
|
+
if (config.auth_type === "api_key" && config.api_key) {
|
|
40
|
+
// Pattern: "Bearer ${TOKEN_NAME}"
|
|
41
|
+
const bearerMatch = config.api_key.match(/^Bearer \$\{([^}]+)\}$/);
|
|
42
|
+
if (bearerMatch) {
|
|
43
|
+
const varName = bearerMatch[1];
|
|
44
|
+
if (varName && credentials[varName]) {
|
|
45
|
+
return new BearerToken(credentials[varName]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// Pattern: "${TOKEN_NAME}" (raw header value)
|
|
49
|
+
const rawMatch = config.api_key.match(/^\$\{([^}]+)\}$/);
|
|
50
|
+
if (rawMatch) {
|
|
51
|
+
const varName = rawMatch[1];
|
|
52
|
+
if (varName && credentials[varName]) {
|
|
53
|
+
return new ApiKey({
|
|
54
|
+
headerName: config.var_name ?? "X-Api-Key",
|
|
55
|
+
value: credentials[varName],
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (config.auth_type === "oauth2") {
|
|
61
|
+
// Gateway pre-resolves OAuth2 tokens and passes them as ACCESS_TOKEN
|
|
62
|
+
// e.g. credentials = { SPOTIFY_ACCESS_TOKEN: 'eyJ...' }
|
|
63
|
+
const tokenKey = Object.keys(credentials).find((k) => k.endsWith("_ACCESS_TOKEN") ||
|
|
64
|
+
k.endsWith("_TOKEN"));
|
|
65
|
+
const tokenValue = tokenKey ? credentials[tokenKey] : undefined;
|
|
66
|
+
if (tokenKey && tokenValue) {
|
|
67
|
+
return new BearerToken(tokenValue);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Fallback: treat the first credential value as a Bearer token
|
|
72
|
+
const firstValue = Object.values(credentials)[0];
|
|
73
|
+
if (firstValue !== undefined) {
|
|
74
|
+
return new BearerToken(firstValue);
|
|
75
|
+
}
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Extracts the `utdk.auth` array from a provider module's package.json.
|
|
80
|
+
* Returns an empty array if the module does not export a `packageJson`
|
|
81
|
+
* property or if the config is absent.
|
|
82
|
+
*/
|
|
83
|
+
export function extractAuthConfigs(providerModule) {
|
|
84
|
+
const pkg = providerModule["packageJson"];
|
|
85
|
+
return pkg?.utdk?.auth ?? [];
|
|
86
|
+
}
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Operation resolution
|
|
89
|
+
// ---------------------------------------------------------------------------
|
|
90
|
+
/**
|
|
91
|
+
* Resolves a dot-notation operation path on a client object.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* resolveOperation(client, 'users.getByUsername')
|
|
95
|
+
* // → client.users.getByUsername (as a bound function)
|
|
96
|
+
*/
|
|
97
|
+
export function resolveOperation(client, operationPath) {
|
|
98
|
+
const segments = operationPath.split(".");
|
|
99
|
+
let current = client;
|
|
100
|
+
for (const segment of segments) {
|
|
101
|
+
if (current === null || current === undefined || typeof current !== "object") {
|
|
102
|
+
throw new TypeError(`Cannot resolve operation path "${operationPath}": "${segment}" is not an object`);
|
|
103
|
+
}
|
|
104
|
+
current = current[segment];
|
|
105
|
+
}
|
|
106
|
+
if (typeof current !== "function") {
|
|
107
|
+
throw new TypeError(`Operation "${operationPath}" resolved to ${typeof current}, expected a function`);
|
|
108
|
+
}
|
|
109
|
+
return current;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Creates a bridge object that is safe to expose inside a vm context.
|
|
113
|
+
*
|
|
114
|
+
* The bridge has a single method, `call(args)`, which invokes the pre-resolved
|
|
115
|
+
* provider operation with the given arguments. Credentials are already baked
|
|
116
|
+
* into the operation via the auth provider — the sandbox script never sees
|
|
117
|
+
* them.
|
|
118
|
+
*/
|
|
119
|
+
export function createBridge(options) {
|
|
120
|
+
return {
|
|
121
|
+
async call(args) {
|
|
122
|
+
return options.operation(args);
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
// Provider loading
|
|
128
|
+
// ---------------------------------------------------------------------------
|
|
129
|
+
/** Name of the factory function pattern in provider modules. */
|
|
130
|
+
const CREATE_CLIENT_PATTERN = /^create[A-Z].+Client$/;
|
|
131
|
+
/**
|
|
132
|
+
* Locates and invokes the `create*Client` factory in a provider module.
|
|
133
|
+
*
|
|
134
|
+
* Provider modules export a `create<Name>Client(options?)` function that
|
|
135
|
+
* returns a `Promise<Client>`. This helper finds it and calls it with the
|
|
136
|
+
* supplied auth provider.
|
|
137
|
+
*
|
|
138
|
+
* @throws if no factory function matching `create*Client` is found.
|
|
139
|
+
*/
|
|
140
|
+
export async function loadProviderClient(providerModule, auth) {
|
|
141
|
+
// Find the first create*Client export
|
|
142
|
+
const factoryEntry = Object.entries(providerModule).find(([name, value]) => CREATE_CLIENT_PATTERN.test(name) && typeof value === "function");
|
|
143
|
+
if (!factoryEntry) {
|
|
144
|
+
throw new Error("Provider module does not export a create*Client function. " +
|
|
145
|
+
`Exports found: ${Object.keys(providerModule).join(", ")}`);
|
|
146
|
+
}
|
|
147
|
+
const [, factory] = factoryEntry;
|
|
148
|
+
const client = await factory(auth ? { auth } : {});
|
|
149
|
+
return client;
|
|
150
|
+
}
|
|
151
|
+
//# sourceMappingURL=bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { Isolate } from "./isolate.js";
|
|
2
|
+
export { createSandboxContext } from "./sandbox.js";
|
|
3
|
+
export { createBridge, extractAuthConfigs, loadProviderClient, resolveAuthProvider, resolveOperation, } from "./bridge.js";
|
|
4
|
+
export type { ExecuteOptions, ExecuteResult, TimeoutError, UtdkAuthConfig } from "./types.js";
|
|
5
|
+
export { TimeoutError as IsolateTimeoutError } from "./types.js";
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { Isolate } from "./isolate.js";
|
|
2
|
+
export { createSandboxContext } from "./sandbox.js";
|
|
3
|
+
export { createBridge, extractAuthConfigs, loadProviderClient, resolveAuthProvider, resolveOperation, } from "./bridge.js";
|
|
4
|
+
export { TimeoutError as IsolateTimeoutError } from "./types.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,YAAY,IAAI,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolate — sandboxed Node.js vm execution runtime for @utdk tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Each call to `execute()` runs in a fresh `vm.createContext()` sandbox:
|
|
5
|
+
* - No access to `process.env`, `fs`, `child_process`, `require`, or any
|
|
6
|
+
* other Node.js host API that could leak credentials or touch the disk.
|
|
7
|
+
* - Credentials are injected by the gateway at call time via a controlled
|
|
8
|
+
* bridge object (`__bridge__`). The sandbox script only calls the bridge;
|
|
9
|
+
* it never receives raw credential strings.
|
|
10
|
+
* - Module cache is isolated per call (new context each time, no
|
|
11
|
+
* cross-call state leakage).
|
|
12
|
+
* - Timeout is enforced via `Promise.race`; runaway async operations are
|
|
13
|
+
* rejected with a `TimeoutError`.
|
|
14
|
+
*
|
|
15
|
+
* For synchronous CPU spin-loops an additional `vm.Script` timeout guard is
|
|
16
|
+
* applied; the async watchdog covers everything else.
|
|
17
|
+
*
|
|
18
|
+
* ## Architecture
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* caller
|
|
22
|
+
* │
|
|
23
|
+
* ▼
|
|
24
|
+
* Isolate.execute(options)
|
|
25
|
+
* │
|
|
26
|
+
* ├─ [host] dynamically import provider module
|
|
27
|
+
* ├─ [host] resolve auth provider from credentials + utdk.auth config
|
|
28
|
+
* ├─ [host] create provider client with auth provider
|
|
29
|
+
* ├─ [host] resolve operation function (dot-notation path)
|
|
30
|
+
* ├─ [host] create bridge { call(args) => operationFn(args) }
|
|
31
|
+
* │
|
|
32
|
+
* ├─ [vm] createContext(minimal globals + __bridge__ + __args__)
|
|
33
|
+
* └─ [vm] run `__bridge__.call(__args__)` inside sandbox
|
|
34
|
+
* └─ [host, via bridge] call provider with credentials
|
|
35
|
+
* └─ [network] HTTP request with injected auth headers
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ## ESM / vm.Module note
|
|
39
|
+
*
|
|
40
|
+
* Node.js 20+ supports `vm.SourceTextModule` (ESM) with the
|
|
41
|
+
* `--experimental-vm-modules` flag. The current implementation uses the
|
|
42
|
+
* stable `vm.Script` API for the thin orchestration script, which is
|
|
43
|
+
* sufficient because:
|
|
44
|
+
* - The script is one line (`__bridge__.call(__args__)`).
|
|
45
|
+
* - Full provider code runs outside the sandbox (in host context) behind
|
|
46
|
+
* the bridge abstraction.
|
|
47
|
+
*
|
|
48
|
+
* If future requirements call for running provider code itself inside an ESM
|
|
49
|
+
* module sandbox, upgrade to `vm.SourceTextModule` with a custom linker
|
|
50
|
+
* (see `upgrade notes` in the README).
|
|
51
|
+
*/
|
|
52
|
+
import { type ExecuteOptions, type ExecuteResult } from "./types.js";
|
|
53
|
+
/**
|
|
54
|
+
* Sandboxed execution runtime for @utdk tool calls.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```typescript
|
|
58
|
+
* const isolate = new Isolate();
|
|
59
|
+
* const result = await isolate.execute({
|
|
60
|
+
* module: '@utdk/github',
|
|
61
|
+
* operation: 'users.getByUsername',
|
|
62
|
+
* args: { username: 'octocat' },
|
|
63
|
+
* credentials: { GITHUB_TOKEN: '<injected-by-gateway>' },
|
|
64
|
+
* timeout: 10_000,
|
|
65
|
+
* });
|
|
66
|
+
* console.log(result.data, `(${result.durationMs}ms)`);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare class Isolate {
|
|
70
|
+
/**
|
|
71
|
+
* Executes a single @utdk tool call inside a fresh vm sandbox.
|
|
72
|
+
*
|
|
73
|
+
* Steps:
|
|
74
|
+
* 1. Dynamically import the provider module (host context, cached by Node.js).
|
|
75
|
+
* 2. Resolve an `AuthProvider` from the supplied credentials and the
|
|
76
|
+
* provider's `utdk.auth` config. Credentials are NEVER written to
|
|
77
|
+
* `process.env`.
|
|
78
|
+
* 3. Instantiate the provider client with the auth provider.
|
|
79
|
+
* 4. Resolve the operation function via dot-notation path.
|
|
80
|
+
* 5. Create a bridge and a fresh vm context.
|
|
81
|
+
* 6. Run the sandbox script `__bridge__.call(__args__)` inside the context.
|
|
82
|
+
* 7. Race the execution against the timeout.
|
|
83
|
+
*
|
|
84
|
+
* @throws `TimeoutError` if execution exceeds `options.timeout`.
|
|
85
|
+
* @throws `TypeError` if the operation path does not resolve to a function.
|
|
86
|
+
* @throws `Error` on provider loading or HTTP failures.
|
|
87
|
+
*/
|
|
88
|
+
execute(options: ExecuteOptions): Promise<ExecuteResult>;
|
|
89
|
+
/**
|
|
90
|
+
* Runs `SANDBOX_SCRIPT` in the given context and races against a timeout.
|
|
91
|
+
*
|
|
92
|
+
* The synchronous `vm.Script` timeout (`timeout` option in runInContext)
|
|
93
|
+
* guards against infinite synchronous loops. The async watchdog (Promise.race)
|
|
94
|
+
* guards against long-running await chains.
|
|
95
|
+
*/
|
|
96
|
+
private _runWithTimeout;
|
|
97
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isolate — sandboxed Node.js vm execution runtime for @utdk tool calls.
|
|
3
|
+
*
|
|
4
|
+
* Each call to `execute()` runs in a fresh `vm.createContext()` sandbox:
|
|
5
|
+
* - No access to `process.env`, `fs`, `child_process`, `require`, or any
|
|
6
|
+
* other Node.js host API that could leak credentials or touch the disk.
|
|
7
|
+
* - Credentials are injected by the gateway at call time via a controlled
|
|
8
|
+
* bridge object (`__bridge__`). The sandbox script only calls the bridge;
|
|
9
|
+
* it never receives raw credential strings.
|
|
10
|
+
* - Module cache is isolated per call (new context each time, no
|
|
11
|
+
* cross-call state leakage).
|
|
12
|
+
* - Timeout is enforced via `Promise.race`; runaway async operations are
|
|
13
|
+
* rejected with a `TimeoutError`.
|
|
14
|
+
*
|
|
15
|
+
* For synchronous CPU spin-loops an additional `vm.Script` timeout guard is
|
|
16
|
+
* applied; the async watchdog covers everything else.
|
|
17
|
+
*
|
|
18
|
+
* ## Architecture
|
|
19
|
+
*
|
|
20
|
+
* ```
|
|
21
|
+
* caller
|
|
22
|
+
* │
|
|
23
|
+
* ▼
|
|
24
|
+
* Isolate.execute(options)
|
|
25
|
+
* │
|
|
26
|
+
* ├─ [host] dynamically import provider module
|
|
27
|
+
* ├─ [host] resolve auth provider from credentials + utdk.auth config
|
|
28
|
+
* ├─ [host] create provider client with auth provider
|
|
29
|
+
* ├─ [host] resolve operation function (dot-notation path)
|
|
30
|
+
* ├─ [host] create bridge { call(args) => operationFn(args) }
|
|
31
|
+
* │
|
|
32
|
+
* ├─ [vm] createContext(minimal globals + __bridge__ + __args__)
|
|
33
|
+
* └─ [vm] run `__bridge__.call(__args__)` inside sandbox
|
|
34
|
+
* └─ [host, via bridge] call provider with credentials
|
|
35
|
+
* └─ [network] HTTP request with injected auth headers
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* ## ESM / vm.Module note
|
|
39
|
+
*
|
|
40
|
+
* Node.js 20+ supports `vm.SourceTextModule` (ESM) with the
|
|
41
|
+
* `--experimental-vm-modules` flag. The current implementation uses the
|
|
42
|
+
* stable `vm.Script` API for the thin orchestration script, which is
|
|
43
|
+
* sufficient because:
|
|
44
|
+
* - The script is one line (`__bridge__.call(__args__)`).
|
|
45
|
+
* - Full provider code runs outside the sandbox (in host context) behind
|
|
46
|
+
* the bridge abstraction.
|
|
47
|
+
*
|
|
48
|
+
* If future requirements call for running provider code itself inside an ESM
|
|
49
|
+
* module sandbox, upgrade to `vm.SourceTextModule` with a custom linker
|
|
50
|
+
* (see `upgrade notes` in the README).
|
|
51
|
+
*/
|
|
52
|
+
import vm from "node:vm";
|
|
53
|
+
import { createBridge, extractAuthConfigs, loadProviderClient, resolveAuthProvider, resolveOperation, } from "./bridge.js";
|
|
54
|
+
import { createSandboxContext } from "./sandbox.js";
|
|
55
|
+
import { TimeoutError } from "./types.js";
|
|
56
|
+
/** Script executed inside the sandbox for every tool call. */
|
|
57
|
+
const SANDBOX_SCRIPT = new vm.Script("__bridge__.call(__args__)");
|
|
58
|
+
/**
|
|
59
|
+
* Sandboxed execution runtime for @utdk tool calls.
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const isolate = new Isolate();
|
|
64
|
+
* const result = await isolate.execute({
|
|
65
|
+
* module: '@utdk/github',
|
|
66
|
+
* operation: 'users.getByUsername',
|
|
67
|
+
* args: { username: 'octocat' },
|
|
68
|
+
* credentials: { GITHUB_TOKEN: '<injected-by-gateway>' },
|
|
69
|
+
* timeout: 10_000,
|
|
70
|
+
* });
|
|
71
|
+
* console.log(result.data, `(${result.durationMs}ms)`);
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export class Isolate {
|
|
75
|
+
/**
|
|
76
|
+
* Executes a single @utdk tool call inside a fresh vm sandbox.
|
|
77
|
+
*
|
|
78
|
+
* Steps:
|
|
79
|
+
* 1. Dynamically import the provider module (host context, cached by Node.js).
|
|
80
|
+
* 2. Resolve an `AuthProvider` from the supplied credentials and the
|
|
81
|
+
* provider's `utdk.auth` config. Credentials are NEVER written to
|
|
82
|
+
* `process.env`.
|
|
83
|
+
* 3. Instantiate the provider client with the auth provider.
|
|
84
|
+
* 4. Resolve the operation function via dot-notation path.
|
|
85
|
+
* 5. Create a bridge and a fresh vm context.
|
|
86
|
+
* 6. Run the sandbox script `__bridge__.call(__args__)` inside the context.
|
|
87
|
+
* 7. Race the execution against the timeout.
|
|
88
|
+
*
|
|
89
|
+
* @throws `TimeoutError` if execution exceeds `options.timeout`.
|
|
90
|
+
* @throws `TypeError` if the operation path does not resolve to a function.
|
|
91
|
+
* @throws `Error` on provider loading or HTTP failures.
|
|
92
|
+
*/
|
|
93
|
+
async execute(options) {
|
|
94
|
+
const { module: moduleName, operation, args = {}, credentials = {}, timeout = 30_000, } = options;
|
|
95
|
+
const startMs = performance.now();
|
|
96
|
+
// ------------------------------------------------------------------
|
|
97
|
+
// Step 1: Load the provider module in HOST context
|
|
98
|
+
// ------------------------------------------------------------------
|
|
99
|
+
// Dynamic import is Node.js-cached so repeated calls to the same
|
|
100
|
+
// provider do not re-parse the module. The provider runs in host
|
|
101
|
+
// context (not in the sandbox) — it is trusted code.
|
|
102
|
+
const providerModule = (await import(moduleName));
|
|
103
|
+
// ------------------------------------------------------------------
|
|
104
|
+
// Step 2: Resolve auth provider from credentials
|
|
105
|
+
// ------------------------------------------------------------------
|
|
106
|
+
const authConfigs = extractAuthConfigs(providerModule);
|
|
107
|
+
const auth = resolveAuthProvider(credentials, authConfigs);
|
|
108
|
+
// ------------------------------------------------------------------
|
|
109
|
+
// Step 3: Create the provider client with credential injection
|
|
110
|
+
// ------------------------------------------------------------------
|
|
111
|
+
const client = await loadProviderClient(providerModule, auth);
|
|
112
|
+
// ------------------------------------------------------------------
|
|
113
|
+
// Step 4: Resolve the operation function
|
|
114
|
+
// ------------------------------------------------------------------
|
|
115
|
+
const operationFn = resolveOperation(client, operation);
|
|
116
|
+
// ------------------------------------------------------------------
|
|
117
|
+
// Step 5: Create the credential bridge and sandboxed vm context
|
|
118
|
+
// ------------------------------------------------------------------
|
|
119
|
+
// The bridge is created in host context and passed into the sandbox.
|
|
120
|
+
// The sandbox script can only call bridge.call(args) — it cannot
|
|
121
|
+
// reach process.env, require, fs, or any host API.
|
|
122
|
+
const bridge = createBridge({ operation: operationFn });
|
|
123
|
+
const context = createSandboxContext({
|
|
124
|
+
extraGlobals: {
|
|
125
|
+
__bridge__: bridge,
|
|
126
|
+
__args__: args,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
// ------------------------------------------------------------------
|
|
130
|
+
// Step 6 + 7: Run inside sandbox, race against timeout
|
|
131
|
+
// ------------------------------------------------------------------
|
|
132
|
+
const data = await this._runWithTimeout(context, timeout);
|
|
133
|
+
const durationMs = performance.now() - startMs;
|
|
134
|
+
return { data, durationMs };
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Runs `SANDBOX_SCRIPT` in the given context and races against a timeout.
|
|
138
|
+
*
|
|
139
|
+
* The synchronous `vm.Script` timeout (`timeout` option in runInContext)
|
|
140
|
+
* guards against infinite synchronous loops. The async watchdog (Promise.race)
|
|
141
|
+
* guards against long-running await chains.
|
|
142
|
+
*/
|
|
143
|
+
_runWithTimeout(context, timeoutMs) {
|
|
144
|
+
// The synchronous portion of the script (if any) is bounded by the vm timeout.
|
|
145
|
+
// For the async portion we use Promise.race.
|
|
146
|
+
let resultPromise;
|
|
147
|
+
try {
|
|
148
|
+
// runInContext returns whatever the script evaluates to.
|
|
149
|
+
// For our one-liner that calls an async bridge method, this is a Promise.
|
|
150
|
+
const scriptResult = SANDBOX_SCRIPT.runInContext(context, {
|
|
151
|
+
// Guard against synchronous infinite loops / busy spins.
|
|
152
|
+
// This only applies to the synchronous execution phase; async
|
|
153
|
+
// continuations are handled by the Promise.race below.
|
|
154
|
+
timeout: timeoutMs,
|
|
155
|
+
});
|
|
156
|
+
resultPromise = Promise.resolve(scriptResult);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
return Promise.reject(err);
|
|
160
|
+
}
|
|
161
|
+
// Async watchdog
|
|
162
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
163
|
+
const timer = setTimeout(() => {
|
|
164
|
+
reject(new TimeoutError(timeoutMs));
|
|
165
|
+
}, timeoutMs);
|
|
166
|
+
// Don't keep the process alive if only the timer is pending
|
|
167
|
+
if (typeof timer.unref === "function") {
|
|
168
|
+
timer.unref();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
return Promise.race([resultPromise, timeoutPromise]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=isolate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolate.js","sourceRoot":"","sources":["../../src/isolate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox context creation for the Isolate runtime.
|
|
3
|
+
*
|
|
4
|
+
* A sandboxed vm context is created per tool call. The context provides a
|
|
5
|
+
* minimal set of safe globals and explicitly excludes `process`, `require`,
|
|
6
|
+
* `fs`, `child_process`, and other Node.js host APIs that could leak
|
|
7
|
+
* environment variables or allow filesystem/subprocess access.
|
|
8
|
+
*
|
|
9
|
+
* The credential bridge (`__bridge__`) and call arguments (`__args__`) are
|
|
10
|
+
* the only channels through which external state enters the sandbox.
|
|
11
|
+
*/
|
|
12
|
+
import vm from "node:vm";
|
|
13
|
+
export interface SandboxOptions {
|
|
14
|
+
/**
|
|
15
|
+
* Additional globals to expose inside the vm context.
|
|
16
|
+
* Use sparingly; every addition is a potential escape hatch.
|
|
17
|
+
*/
|
|
18
|
+
extraGlobals?: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a fresh, restricted vm context.
|
|
22
|
+
*
|
|
23
|
+
* Accessible globals:
|
|
24
|
+
* console, JSON, Math, Date, Promise, Array, Object, String, Number,
|
|
25
|
+
* Boolean, Error, TypeError, RangeError, Map, Set, WeakMap, WeakSet,
|
|
26
|
+
* Symbol, typed arrays, ArrayBuffer, URL, URLSearchParams,
|
|
27
|
+
* TextEncoder, TextDecoder, setTimeout, clearTimeout, setInterval,
|
|
28
|
+
* clearInterval, parseInt, parseFloat, isNaN, isFinite, encodeURI,
|
|
29
|
+
* decodeURI, encodeURIComponent, decodeURIComponent, Infinity, NaN,
|
|
30
|
+
* undefined.
|
|
31
|
+
*
|
|
32
|
+
* Deliberately excluded:
|
|
33
|
+
* process, require, global, Buffer, __dirname, __filename,
|
|
34
|
+
* fs, child_process, net, http, https, os, crypto (node:crypto),
|
|
35
|
+
* and any other Node.js built-in that exposes host resources.
|
|
36
|
+
*
|
|
37
|
+
* @param options - Optional extra globals to inject.
|
|
38
|
+
* @returns A contextified vm sandbox object.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createSandboxContext(options?: SandboxOptions): vm.Context;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox context creation for the Isolate runtime.
|
|
3
|
+
*
|
|
4
|
+
* A sandboxed vm context is created per tool call. The context provides a
|
|
5
|
+
* minimal set of safe globals and explicitly excludes `process`, `require`,
|
|
6
|
+
* `fs`, `child_process`, and other Node.js host APIs that could leak
|
|
7
|
+
* environment variables or allow filesystem/subprocess access.
|
|
8
|
+
*
|
|
9
|
+
* The credential bridge (`__bridge__`) and call arguments (`__args__`) are
|
|
10
|
+
* the only channels through which external state enters the sandbox.
|
|
11
|
+
*/
|
|
12
|
+
import vm from "node:vm";
|
|
13
|
+
const PREFIXED_METHODS = ["log", "error", "warn", "info", "debug"];
|
|
14
|
+
function createPrefixedConsole(prefix) {
|
|
15
|
+
const console_ = console;
|
|
16
|
+
return Object.fromEntries(PREFIXED_METHODS.map((method) => [method, (...args) => console_[method](prefix, ...args)]));
|
|
17
|
+
}
|
|
18
|
+
const sandboxConsole = createPrefixedConsole("[sandbox]");
|
|
19
|
+
/**
|
|
20
|
+
* Creates a fresh, restricted vm context.
|
|
21
|
+
*
|
|
22
|
+
* Accessible globals:
|
|
23
|
+
* console, JSON, Math, Date, Promise, Array, Object, String, Number,
|
|
24
|
+
* Boolean, Error, TypeError, RangeError, Map, Set, WeakMap, WeakSet,
|
|
25
|
+
* Symbol, typed arrays, ArrayBuffer, URL, URLSearchParams,
|
|
26
|
+
* TextEncoder, TextDecoder, setTimeout, clearTimeout, setInterval,
|
|
27
|
+
* clearInterval, parseInt, parseFloat, isNaN, isFinite, encodeURI,
|
|
28
|
+
* decodeURI, encodeURIComponent, decodeURIComponent, Infinity, NaN,
|
|
29
|
+
* undefined.
|
|
30
|
+
*
|
|
31
|
+
* Deliberately excluded:
|
|
32
|
+
* process, require, global, Buffer, __dirname, __filename,
|
|
33
|
+
* fs, child_process, net, http, https, os, crypto (node:crypto),
|
|
34
|
+
* and any other Node.js built-in that exposes host resources.
|
|
35
|
+
*
|
|
36
|
+
* @param options - Optional extra globals to inject.
|
|
37
|
+
* @returns A contextified vm sandbox object.
|
|
38
|
+
*/
|
|
39
|
+
export function createSandboxContext(options = {}) {
|
|
40
|
+
const sandbox = {
|
|
41
|
+
// Safe builtins
|
|
42
|
+
console: sandboxConsole,
|
|
43
|
+
JSON,
|
|
44
|
+
Math,
|
|
45
|
+
Date,
|
|
46
|
+
Promise,
|
|
47
|
+
Array,
|
|
48
|
+
Object,
|
|
49
|
+
String,
|
|
50
|
+
Number,
|
|
51
|
+
Boolean,
|
|
52
|
+
Error,
|
|
53
|
+
EvalError,
|
|
54
|
+
RangeError,
|
|
55
|
+
ReferenceError,
|
|
56
|
+
SyntaxError,
|
|
57
|
+
TypeError,
|
|
58
|
+
URIError,
|
|
59
|
+
Map,
|
|
60
|
+
Set,
|
|
61
|
+
WeakMap,
|
|
62
|
+
WeakSet,
|
|
63
|
+
WeakRef,
|
|
64
|
+
Symbol,
|
|
65
|
+
BigInt,
|
|
66
|
+
// Typed arrays
|
|
67
|
+
ArrayBuffer,
|
|
68
|
+
SharedArrayBuffer,
|
|
69
|
+
DataView,
|
|
70
|
+
Int8Array,
|
|
71
|
+
Uint8Array,
|
|
72
|
+
Uint8ClampedArray,
|
|
73
|
+
Int16Array,
|
|
74
|
+
Uint16Array,
|
|
75
|
+
Int32Array,
|
|
76
|
+
Uint32Array,
|
|
77
|
+
Float32Array,
|
|
78
|
+
Float64Array,
|
|
79
|
+
BigInt64Array,
|
|
80
|
+
BigUint64Array,
|
|
81
|
+
// Web-compatible globals
|
|
82
|
+
URL,
|
|
83
|
+
URLSearchParams,
|
|
84
|
+
TextEncoder,
|
|
85
|
+
TextDecoder,
|
|
86
|
+
// Timers (these are host-side and safe to expose)
|
|
87
|
+
setTimeout,
|
|
88
|
+
clearTimeout,
|
|
89
|
+
setInterval,
|
|
90
|
+
clearInterval,
|
|
91
|
+
// Standard globals
|
|
92
|
+
parseInt,
|
|
93
|
+
parseFloat,
|
|
94
|
+
isNaN,
|
|
95
|
+
isFinite,
|
|
96
|
+
encodeURI,
|
|
97
|
+
decodeURI,
|
|
98
|
+
encodeURIComponent,
|
|
99
|
+
decodeURIComponent,
|
|
100
|
+
Infinity,
|
|
101
|
+
NaN,
|
|
102
|
+
undefined,
|
|
103
|
+
// Explicitly do NOT add:
|
|
104
|
+
// process — would expose process.env (credential leak)
|
|
105
|
+
// require — would allow arbitrary module imports
|
|
106
|
+
// global — alias for Node.js global; exposes process
|
|
107
|
+
// Buffer — not needed in sandboxed operations
|
|
108
|
+
// __dirname — filesystem information leak
|
|
109
|
+
// __filename — filesystem information leak
|
|
110
|
+
// fetch — injected separately per-operation via bridge (see bridge.ts)
|
|
111
|
+
// crypto — would allow subtle attacks; excluded for now
|
|
112
|
+
...options.extraGlobals,
|
|
113
|
+
};
|
|
114
|
+
return vm.createContext(sandbox);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=sandbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.js","sourceRoot":"","sources":["../../src/sandbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AAIzB,MAAM,gBAAgB,GAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;AAEpF,SAAS,qBAAqB,CAAC,MAAc;IAC3C,MAAM,QAAQ,GAAiC,OAAO,CAAC;IACvD,OAAO,MAAM,CAAC,WAAW,CACvB,gBAAgB,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,IAAe,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC,CAC/C,CAAC;AAC3D,CAAC;AAED,MAAM,cAAc,GAAG,qBAAqB,CAAC,WAAW,CAAC,CAAC;AAU1D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAAC,UAA0B,EAAE;IAC/D,MAAM,OAAO,GAA4B;QACvC,gBAAgB;QAChB,OAAO,EAAE,cAAc;QACvB,IAAI;QACJ,IAAI;QACJ,IAAI;QACJ,OAAO;QACP,KAAK;QACL,MAAM;QACN,MAAM;QACN,MAAM;QACN,OAAO;QACP,KAAK;QACL,SAAS;QACT,UAAU;QACV,cAAc;QACd,WAAW;QACX,SAAS;QACT,QAAQ;QACR,GAAG;QACH,GAAG;QACH,OAAO;QACP,OAAO;QACP,OAAO;QACP,MAAM;QACN,MAAM;QAEN,eAAe;QACf,WAAW;QACX,iBAAiB;QACjB,QAAQ;QACR,SAAS;QACT,UAAU;QACV,iBAAiB;QACjB,UAAU;QACV,WAAW;QACX,UAAU;QACV,WAAW;QACX,YAAY;QACZ,YAAY;QACZ,aAAa;QACb,cAAc;QAEd,yBAAyB;QACzB,GAAG;QACH,eAAe;QACf,WAAW;QACX,WAAW;QAEX,kDAAkD;QAClD,UAAU;QACV,YAAY;QACZ,WAAW;QACX,aAAa;QAEb,mBAAmB;QACnB,QAAQ;QACR,UAAU;QACV,KAAK;QACL,QAAQ;QACR,SAAS;QACT,SAAS;QACT,kBAAkB;QAClB,kBAAkB;QAClB,QAAQ;QACR,GAAG;QACH,SAAS;QAET,yBAAyB;QACzB,gEAAgE;QAChE,0DAA0D;QAC1D,+DAA+D;QAC/D,wDAAwD;QACxD,iDAAiD;QACjD,iDAAiD;QACjD,kFAAkF;QAClF,kEAAkE;QAElE,GAAG,OAAO,CAAC,YAAY;KACxB,CAAC;IAEF,OAAO,EAAE,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AACnC,CAAC"}
|