@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,168 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { resolveAuthProvider, resolveOperation, extractAuthConfigs, loadProviderClient, createBridge, } from "../src/bridge.js";
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
// resolveAuthProvider
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
describe("resolveAuthProvider", () => {
|
|
7
|
+
it("returns undefined when credentials are empty", () => {
|
|
8
|
+
const auth = resolveAuthProvider({});
|
|
9
|
+
expect(auth).toBeUndefined();
|
|
10
|
+
});
|
|
11
|
+
it("resolves BearerToken from api_key config with Bearer pattern", async () => {
|
|
12
|
+
const configs = [
|
|
13
|
+
{
|
|
14
|
+
auth_type: "api_key",
|
|
15
|
+
api_key: "Bearer ${GITHUB_TOKEN}",
|
|
16
|
+
var_name: "Authorization",
|
|
17
|
+
location: "header",
|
|
18
|
+
},
|
|
19
|
+
];
|
|
20
|
+
const auth = resolveAuthProvider({ GITHUB_TOKEN: "ghs_test123" }, configs);
|
|
21
|
+
expect(auth).toBeDefined();
|
|
22
|
+
const headers = {};
|
|
23
|
+
await auth.authenticate(headers);
|
|
24
|
+
expect(headers["Authorization"]).toBe("Bearer ghs_test123");
|
|
25
|
+
});
|
|
26
|
+
it("resolves ApiKey from raw ${VAR_NAME} api_key pattern", async () => {
|
|
27
|
+
const configs = [
|
|
28
|
+
{
|
|
29
|
+
auth_type: "api_key",
|
|
30
|
+
api_key: "${DD_API_KEY}",
|
|
31
|
+
var_name: "DD-API-Key",
|
|
32
|
+
location: "header",
|
|
33
|
+
},
|
|
34
|
+
];
|
|
35
|
+
const auth = resolveAuthProvider({ DD_API_KEY: "dd_secret" }, configs);
|
|
36
|
+
expect(auth).toBeDefined();
|
|
37
|
+
const headers = {};
|
|
38
|
+
await auth.authenticate(headers);
|
|
39
|
+
expect(headers["DD-API-Key"]).toBe("dd_secret");
|
|
40
|
+
});
|
|
41
|
+
it("resolves BearerToken from oauth2 ACCESS_TOKEN credential", async () => {
|
|
42
|
+
const configs = [
|
|
43
|
+
{ auth_type: "oauth2", flow: "authorization_code" },
|
|
44
|
+
];
|
|
45
|
+
const auth = resolveAuthProvider({ SPOTIFY_ACCESS_TOKEN: "eyJaccess" }, configs);
|
|
46
|
+
expect(auth).toBeDefined();
|
|
47
|
+
const headers = {};
|
|
48
|
+
await auth.authenticate(headers);
|
|
49
|
+
expect(headers["Authorization"]).toBe("Bearer eyJaccess");
|
|
50
|
+
});
|
|
51
|
+
it("falls back to BearerToken with first credential when auth config is empty", async () => {
|
|
52
|
+
const auth = resolveAuthProvider({ SOME_TOKEN: "fallback-token" });
|
|
53
|
+
expect(auth).toBeDefined();
|
|
54
|
+
const headers = {};
|
|
55
|
+
await auth.authenticate(headers);
|
|
56
|
+
expect(headers["Authorization"]).toBe("Bearer fallback-token");
|
|
57
|
+
});
|
|
58
|
+
it("credential value is never written to process.env", () => {
|
|
59
|
+
const originalEnv = { ...process.env };
|
|
60
|
+
resolveAuthProvider({ SECRET_KEY: "my-secret" });
|
|
61
|
+
// Verify process.env was not modified
|
|
62
|
+
expect(process.env).toEqual(originalEnv);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// resolveOperation
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
describe("resolveOperation", () => {
|
|
69
|
+
it("resolves a simple top-level function", () => {
|
|
70
|
+
const fn = vi.fn();
|
|
71
|
+
const client = { getUser: fn };
|
|
72
|
+
const op = resolveOperation(client, "getUser");
|
|
73
|
+
expect(op).toBe(fn);
|
|
74
|
+
});
|
|
75
|
+
it("resolves a nested dot-notation path", () => {
|
|
76
|
+
const fn = vi.fn();
|
|
77
|
+
const client = { users: { getByUsername: fn } };
|
|
78
|
+
const op = resolveOperation(client, "users.getByUsername");
|
|
79
|
+
expect(op).toBe(fn);
|
|
80
|
+
});
|
|
81
|
+
it("resolves a deeply nested path", () => {
|
|
82
|
+
const fn = vi.fn();
|
|
83
|
+
const client = { a: { b: { c: fn } } };
|
|
84
|
+
const op = resolveOperation(client, "a.b.c");
|
|
85
|
+
expect(op).toBe(fn);
|
|
86
|
+
});
|
|
87
|
+
it("throws TypeError when intermediate segment is missing", () => {
|
|
88
|
+
const client = { users: {} };
|
|
89
|
+
expect(() => resolveOperation(client, "users.missing.fn")).toThrow(TypeError);
|
|
90
|
+
});
|
|
91
|
+
it("throws TypeError when operation is not a function", () => {
|
|
92
|
+
const client = { count: 42 };
|
|
93
|
+
expect(() => resolveOperation(client, "count")).toThrow(TypeError);
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
// extractAuthConfigs
|
|
98
|
+
// ---------------------------------------------------------------------------
|
|
99
|
+
describe("extractAuthConfigs", () => {
|
|
100
|
+
it("returns empty array when module has no packageJson export", () => {
|
|
101
|
+
const mod = {};
|
|
102
|
+
expect(extractAuthConfigs(mod)).toEqual([]);
|
|
103
|
+
});
|
|
104
|
+
it("returns empty array when packageJson has no utdk field", () => {
|
|
105
|
+
const mod = { packageJson: { name: "@utdk/test" } };
|
|
106
|
+
expect(extractAuthConfigs(mod)).toEqual([]);
|
|
107
|
+
});
|
|
108
|
+
it("extracts auth array from packageJson.utdk.auth", () => {
|
|
109
|
+
const authConfig = [
|
|
110
|
+
{ auth_type: "api_key", api_key: "Bearer ${TOKEN}", var_name: "Authorization" },
|
|
111
|
+
];
|
|
112
|
+
const mod = {
|
|
113
|
+
packageJson: {
|
|
114
|
+
name: "@utdk/test",
|
|
115
|
+
utdk: { auth: authConfig },
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
expect(extractAuthConfigs(mod)).toEqual(authConfig);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// loadProviderClient
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
describe("loadProviderClient", () => {
|
|
125
|
+
it("calls the create*Client factory with auth option", async () => {
|
|
126
|
+
const mockClient = { users: { getByUsername: vi.fn() } };
|
|
127
|
+
const createMockClient = vi.fn().mockResolvedValue(mockClient);
|
|
128
|
+
const mod = { createMockClient };
|
|
129
|
+
const auth = { authenticate: vi.fn() };
|
|
130
|
+
const result = await loadProviderClient(mod, auth);
|
|
131
|
+
expect(createMockClient).toHaveBeenCalledWith({ auth });
|
|
132
|
+
expect(result).toBe(mockClient);
|
|
133
|
+
});
|
|
134
|
+
it("calls the factory with empty options when no auth is provided", async () => {
|
|
135
|
+
const mockClient = {};
|
|
136
|
+
const createNoAuthClient = vi.fn().mockResolvedValue(mockClient);
|
|
137
|
+
const mod = { createNoAuthClient };
|
|
138
|
+
await loadProviderClient(mod, undefined);
|
|
139
|
+
expect(createNoAuthClient).toHaveBeenCalledWith({});
|
|
140
|
+
});
|
|
141
|
+
it("throws when no create*Client export is found", async () => {
|
|
142
|
+
const mod = { someOtherExport: "value" };
|
|
143
|
+
await expect(loadProviderClient(mod, undefined)).rejects.toThrow(/does not export a create\*Client function/);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// createBridge
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
describe("createBridge", () => {
|
|
150
|
+
it("returns an object with a call method", () => {
|
|
151
|
+
const op = vi.fn().mockResolvedValue({ id: 1 });
|
|
152
|
+
const bridge = createBridge({ operation: op });
|
|
153
|
+
expect(typeof bridge.call).toBe("function");
|
|
154
|
+
});
|
|
155
|
+
it("delegates call to the underlying operation", async () => {
|
|
156
|
+
const op = vi.fn().mockResolvedValue({ login: "octocat" });
|
|
157
|
+
const bridge = createBridge({ operation: op });
|
|
158
|
+
const result = await bridge.call({ username: "octocat" });
|
|
159
|
+
expect(op).toHaveBeenCalledWith({ username: "octocat" });
|
|
160
|
+
expect(result).toEqual({ login: "octocat" });
|
|
161
|
+
});
|
|
162
|
+
it("propagates errors from the operation", async () => {
|
|
163
|
+
const op = vi.fn().mockRejectedValue(new Error("API error"));
|
|
164
|
+
const bridge = createBridge({ operation: op });
|
|
165
|
+
await expect(bridge.call({})).rejects.toThrow("API error");
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=bridge.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge.test.js","sourceRoot":"","sources":["../../__tests__/bridge.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the Isolate runtime.
|
|
3
|
+
*
|
|
4
|
+
* These tests exercise the full execute() path using mock provider modules.
|
|
5
|
+
* Real network calls are avoided — all provider operations are vi.fn() mocks.
|
|
6
|
+
*
|
|
7
|
+
* Provider simulation:
|
|
8
|
+
* - `@utdk/github` → Bearer token auth, users.getByUsername operation
|
|
9
|
+
* - `@utdk/spotify` → OAuth2 pre-resolved token, tracks.search operation
|
|
10
|
+
*/
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for the Isolate runtime.
|
|
3
|
+
*
|
|
4
|
+
* These tests exercise the full execute() path using mock provider modules.
|
|
5
|
+
* Real network calls are avoided — all provider operations are vi.fn() mocks.
|
|
6
|
+
*
|
|
7
|
+
* Provider simulation:
|
|
8
|
+
* - `@utdk/github` → Bearer token auth, users.getByUsername operation
|
|
9
|
+
* - `@utdk/spotify` → OAuth2 pre-resolved token, tracks.search operation
|
|
10
|
+
*/
|
|
11
|
+
import vm from "node:vm";
|
|
12
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
13
|
+
import { createBridge, resolveAuthProvider } from "../src/bridge.js";
|
|
14
|
+
import { IsolateTimeoutError } from "../src/index.js";
|
|
15
|
+
import { createSandboxContext } from "../src/sandbox.js";
|
|
16
|
+
// Reusable sandbox script (same one the Isolate uses internally)
|
|
17
|
+
const SANDBOX_SCRIPT = new vm.Script("__bridge__.call(__args__)");
|
|
18
|
+
afterEach(() => {
|
|
19
|
+
vi.restoreAllMocks();
|
|
20
|
+
});
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
// Sandbox execution via bridge
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
describe("sandbox script invokes bridge.call", () => {
|
|
25
|
+
it("passes args to the operation and returns the result", async () => {
|
|
26
|
+
const operationFn = vi.fn().mockResolvedValue({ id: "track-123", name: "Bohemian Rhapsody" });
|
|
27
|
+
const bridge = createBridge({ operation: operationFn });
|
|
28
|
+
const args = { q: "Bohemian Rhapsody", limit: 1 };
|
|
29
|
+
const context = createSandboxContext({
|
|
30
|
+
extraGlobals: { __bridge__: bridge, __args__: args },
|
|
31
|
+
});
|
|
32
|
+
const result = await SANDBOX_SCRIPT.runInContext(context);
|
|
33
|
+
expect(operationFn).toHaveBeenCalledWith(args);
|
|
34
|
+
expect(result).toEqual({ id: "track-123", name: "Bohemian Rhapsody" });
|
|
35
|
+
});
|
|
36
|
+
it("propagates errors thrown by the bridge", async () => {
|
|
37
|
+
const operationFn = vi.fn().mockRejectedValue(new Error("API error 429"));
|
|
38
|
+
const bridge = createBridge({ operation: operationFn });
|
|
39
|
+
const context = createSandboxContext({
|
|
40
|
+
extraGlobals: { __bridge__: bridge, __args__: {} },
|
|
41
|
+
});
|
|
42
|
+
await expect(SANDBOX_SCRIPT.runInContext(context)).rejects.toThrow("API error 429");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Sandbox isolation
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
describe("sandbox isolation", () => {
|
|
49
|
+
it("process is not accessible inside the sandbox", () => {
|
|
50
|
+
const ctx = createSandboxContext();
|
|
51
|
+
const result = vm.runInContext("typeof process", ctx);
|
|
52
|
+
expect(result).toBe("undefined");
|
|
53
|
+
});
|
|
54
|
+
it("process.env is not accessible inside the sandbox", () => {
|
|
55
|
+
const ctx = createSandboxContext();
|
|
56
|
+
const result = vm.runInContext("try { typeof process.env } catch (e) { 'threw' }", ctx);
|
|
57
|
+
expect(["threw", "undefined"]).toContain(result);
|
|
58
|
+
});
|
|
59
|
+
it("require is not accessible inside the sandbox", () => {
|
|
60
|
+
const ctx = createSandboxContext();
|
|
61
|
+
expect(vm.runInContext("typeof require", ctx)).toBe("undefined");
|
|
62
|
+
});
|
|
63
|
+
it("global is not accessible inside the sandbox", () => {
|
|
64
|
+
const ctx = createSandboxContext();
|
|
65
|
+
expect(vm.runInContext("typeof global", ctx)).toBe("undefined");
|
|
66
|
+
});
|
|
67
|
+
it("credentials are NOT injected as standalone sandbox globals", () => {
|
|
68
|
+
// Credentials must only flow through the bridge, never as top-level globals.
|
|
69
|
+
const ctx = createSandboxContext({
|
|
70
|
+
extraGlobals: {
|
|
71
|
+
__bridge__: { call: vi.fn().mockResolvedValue({}) },
|
|
72
|
+
__args__: {},
|
|
73
|
+
// GITHUB_TOKEN intentionally NOT added
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
expect(vm.runInContext("typeof GITHUB_TOKEN", ctx)).toBe("undefined");
|
|
77
|
+
expect(vm.runInContext("typeof SPOTIFY_ACCESS_TOKEN", ctx)).toBe("undefined");
|
|
78
|
+
});
|
|
79
|
+
it("state from one execution context does not leak to the next", () => {
|
|
80
|
+
const ctx1 = createSandboxContext();
|
|
81
|
+
const ctx2 = createSandboxContext();
|
|
82
|
+
vm.runInContext("var leakyVar = 'should-not-escape'", ctx1);
|
|
83
|
+
expect(vm.runInContext("typeof leakyVar", ctx2)).toBe("undefined");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
// Timeout enforcement
|
|
88
|
+
// ---------------------------------------------------------------------------
|
|
89
|
+
describe("timeout enforcement", () => {
|
|
90
|
+
it("rejects with TimeoutError when execution stalls", async () => {
|
|
91
|
+
const neverResolves = new Promise(() => { });
|
|
92
|
+
const bridge = { call: vi.fn().mockReturnValue(neverResolves) };
|
|
93
|
+
const ctx = createSandboxContext({
|
|
94
|
+
extraGlobals: { __bridge__: bridge, __args__: {} },
|
|
95
|
+
});
|
|
96
|
+
const resultPromise = SANDBOX_SCRIPT.runInContext(ctx);
|
|
97
|
+
const timeoutMs = 50;
|
|
98
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
99
|
+
setTimeout(() => reject(new IsolateTimeoutError(timeoutMs)), timeoutMs);
|
|
100
|
+
});
|
|
101
|
+
await expect(Promise.race([resultPromise, timeoutPromise])).rejects.toMatchObject({
|
|
102
|
+
name: "TimeoutError",
|
|
103
|
+
});
|
|
104
|
+
}, 1_000);
|
|
105
|
+
it("TimeoutError message includes the configured timeout value", () => {
|
|
106
|
+
const err = new IsolateTimeoutError(5_000);
|
|
107
|
+
expect(err.message).toContain("5000ms");
|
|
108
|
+
expect(err.name).toBe("TimeoutError");
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Credential injection — github simulation
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
describe("credential injection — github provider simulation", () => {
|
|
115
|
+
it("bearer token is injected via auth provider, NOT process.env", async () => {
|
|
116
|
+
const authConfigs = [
|
|
117
|
+
{
|
|
118
|
+
auth_type: "api_key",
|
|
119
|
+
api_key: "Bearer ${GITHUB_TOKEN}",
|
|
120
|
+
var_name: "Authorization",
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
const credentials = { GITHUB_TOKEN: "ghs_injected_by_gateway" };
|
|
124
|
+
const auth = resolveAuthProvider(credentials, authConfigs);
|
|
125
|
+
const headers = {};
|
|
126
|
+
await auth.authenticate(headers);
|
|
127
|
+
expect(headers["Authorization"]).toBe("Bearer ghs_injected_by_gateway");
|
|
128
|
+
// process.env must NOT have been touched
|
|
129
|
+
expect(process.env["GITHUB_TOKEN"]).toBeUndefined();
|
|
130
|
+
});
|
|
131
|
+
it("executes github-like operation via sandbox bridge", async () => {
|
|
132
|
+
const mockUser = { login: "octocat", id: 1, type: "User" };
|
|
133
|
+
const getUserFn = vi.fn().mockResolvedValue(mockUser);
|
|
134
|
+
// Simulate how the client would be structured after createGithubClient()
|
|
135
|
+
const bridge = createBridge({
|
|
136
|
+
operation: (args) => getUserFn(args),
|
|
137
|
+
});
|
|
138
|
+
const context = createSandboxContext({
|
|
139
|
+
extraGlobals: { __bridge__: bridge, __args__: { username: "octocat" } },
|
|
140
|
+
});
|
|
141
|
+
const result = await SANDBOX_SCRIPT.runInContext(context);
|
|
142
|
+
expect(getUserFn).toHaveBeenCalledWith({ username: "octocat" });
|
|
143
|
+
expect(result).toEqual(mockUser);
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
// Credential injection — spotify simulation
|
|
148
|
+
// ---------------------------------------------------------------------------
|
|
149
|
+
describe("credential injection — spotify provider simulation", () => {
|
|
150
|
+
it("pre-resolved OAuth2 token is injected as BearerToken, NOT via process.env", async () => {
|
|
151
|
+
const authConfigs = [
|
|
152
|
+
{ auth_type: "oauth2", flow: "authorization_code" },
|
|
153
|
+
];
|
|
154
|
+
// Gateway pre-resolves the OAuth2 exchange and provides the access token directly
|
|
155
|
+
const credentials = { SPOTIFY_ACCESS_TOKEN: "eyJspotify_access_token" };
|
|
156
|
+
const auth = resolveAuthProvider(credentials, authConfigs);
|
|
157
|
+
const headers = {};
|
|
158
|
+
await auth.authenticate(headers);
|
|
159
|
+
expect(headers["Authorization"]).toBe("Bearer eyJspotify_access_token");
|
|
160
|
+
expect(process.env["SPOTIFY_ACCESS_TOKEN"]).toBeUndefined();
|
|
161
|
+
expect(process.env["SPOTIFY_CLIENT_SECRET"]).toBeUndefined();
|
|
162
|
+
});
|
|
163
|
+
it("executes spotify-like search operation via sandbox bridge", async () => {
|
|
164
|
+
const mockTracks = {
|
|
165
|
+
tracks: { items: [{ name: "Bohemian Rhapsody", id: "abc" }] },
|
|
166
|
+
};
|
|
167
|
+
const searchFn = vi.fn().mockResolvedValue(mockTracks);
|
|
168
|
+
const bridge = createBridge({ operation: searchFn });
|
|
169
|
+
const context = createSandboxContext({
|
|
170
|
+
extraGlobals: {
|
|
171
|
+
__bridge__: bridge,
|
|
172
|
+
__args__: { q: "Bohemian Rhapsody", type: "track", limit: 1 },
|
|
173
|
+
},
|
|
174
|
+
});
|
|
175
|
+
const result = await SANDBOX_SCRIPT.runInContext(context);
|
|
176
|
+
expect(searchFn).toHaveBeenCalledWith({
|
|
177
|
+
q: "Bohemian Rhapsody",
|
|
178
|
+
type: "track",
|
|
179
|
+
limit: 1,
|
|
180
|
+
});
|
|
181
|
+
expect(result).toEqual(mockTracks);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
// Performance overhead measurement
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
describe("performance overhead", () => {
|
|
188
|
+
it("sandbox overhead is within acceptable bounds (<200ms for trivial calls)", async () => {
|
|
189
|
+
const operationFn = vi.fn().mockResolvedValue({ ok: true });
|
|
190
|
+
const bridge = createBridge({ operation: operationFn });
|
|
191
|
+
const args = { test: true };
|
|
192
|
+
const ITERATIONS = 5;
|
|
193
|
+
const sandboxDurations = [];
|
|
194
|
+
const directDurations = [];
|
|
195
|
+
for (let i = 0; i < ITERATIONS; i++) {
|
|
196
|
+
// Sandbox path
|
|
197
|
+
const ctx = createSandboxContext({
|
|
198
|
+
extraGlobals: { __bridge__: bridge, __args__: args },
|
|
199
|
+
});
|
|
200
|
+
const sandboxStart = performance.now();
|
|
201
|
+
await SANDBOX_SCRIPT.runInContext(ctx);
|
|
202
|
+
sandboxDurations.push(performance.now() - sandboxStart);
|
|
203
|
+
// Direct call baseline
|
|
204
|
+
const directStart = performance.now();
|
|
205
|
+
await operationFn(args);
|
|
206
|
+
directDurations.push(performance.now() - directStart);
|
|
207
|
+
}
|
|
208
|
+
const avgSandbox = sandboxDurations.reduce((a, b) => a + b, 0) / ITERATIONS;
|
|
209
|
+
const avgDirect = directDurations.reduce((a, b) => a + b, 0) / ITERATIONS;
|
|
210
|
+
const overhead = avgSandbox - avgDirect;
|
|
211
|
+
console.log(`[perf] avg sandbox: ${avgSandbox.toFixed(2)}ms, ` +
|
|
212
|
+
`avg direct: ${avgDirect.toFixed(2)}ms, ` +
|
|
213
|
+
`overhead: ${overhead.toFixed(2)}ms`);
|
|
214
|
+
// Context creation + vm.Script execution overhead should be well under 200ms
|
|
215
|
+
expect(avgSandbox).toBeLessThan(200);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
//# sourceMappingURL=isolate.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"isolate.test.js","sourceRoot":"","sources":["../../__tests__/isolate.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAGzD,iEAAiE;AACjE,MAAM,cAAc,GAAG,IAAI,EAAE,CAAC,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAElE,SAAS,CAAC,GAAG,EAAE;IACb,EAAE,CAAC,eAAe,EAAE,CAAC;AACvB,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,+BAA+B;AAC/B,8EAA8E;AAE9E,QAAQ,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAClD,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC;QAC9F,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,mBAAmB,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAElD,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACnC,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;SACrD,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAsB,CAAC;QAEhF,MAAM,CAAC,WAAW,CAAC,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC;QAC/C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;QAC1E,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QAExD,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACnC,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;SACnD,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,cAAc,CAAC,YAAY,CAAC,OAAO,CAAqB,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1G,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;IACjC,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,GAAG,EAAE;QAC1D,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAC5B,kDAAkD,EAClD,GAAG,CACJ,CAAC;QACF,MAAM,CAAC,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,gBAAgB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;QACnC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,6EAA6E;QAC7E,MAAM,GAAG,GAAG,oBAAoB,CAAC;YAC/B,YAAY,EAAE;gBACZ,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,EAAE;gBACnD,QAAQ,EAAE,EAAE;gBACZ,uCAAuC;aACxC;SACF,CAAC,CAAC;QACH,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACtE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAChF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;QACpC,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;QAEpC,EAAE,CAAC,YAAY,CAAC,oCAAoC,EAAE,IAAI,CAAC,CAAC;QAE5D,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,aAAa,GAAG,IAAI,OAAO,CAAQ,GAAG,EAAE,GAAiB,CAAC,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,aAAa,CAAC,EAAE,CAAC;QAEhE,MAAM,GAAG,GAAG,oBAAoB,CAAC;YAC/B,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,EAAE;SACnD,CAAC,CAAC;QAEH,MAAM,aAAa,GAAG,cAAc,CAAC,YAAY,CAAC,GAAG,CAAqB,CAAC;QAC3E,MAAM,SAAS,GAAG,EAAE,CAAC;QAErB,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YACtD,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,mBAAmB,CAAC,SAAS,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC1E,CAAC,CAAC,CAAC;QAEH,MAAM,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC;YAChF,IAAI,EAAE,cAAc;SACrB,CAAC,CAAC;IACL,CAAC,EAAE,KAAK,CAAC,CAAC;IAEV,EAAE,CAAC,4DAA4D,EAAE,GAAG,EAAE;QACpE,MAAM,GAAG,GAAG,IAAI,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,2CAA2C;AAC3C,8EAA8E;AAE9E,QAAQ,CAAC,mDAAmD,EAAE,GAAG,EAAE;IACjE,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;QAC3E,MAAM,WAAW,GAAqB;YACpC;gBACE,SAAS,EAAE,SAAS;gBACpB,OAAO,EAAE,wBAAwB;gBACjC,QAAQ,EAAE,eAAe;aAC1B;SACF,CAAC;QACF,MAAM,WAAW,GAAG,EAAE,YAAY,EAAE,yBAAyB,EAAE,CAAC;QAChE,MAAM,IAAI,GAAG,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAE3D,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,MAAM,IAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAElC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACxE,yCAAyC;QACzC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,QAAQ,GAAG,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAC3D,MAAM,SAAS,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAEtD,yEAAyE;QACzE,MAAM,MAAM,GAAG,YAAY,CAAC;YAC1B,SAAS,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC;SACrC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACnC,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,EAAE;SACxE,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAsB,CAAC;QAEhF,MAAM,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;QAChE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,4CAA4C;AAC5C,8EAA8E;AAE9E,QAAQ,CAAC,oDAAoD,EAAE,GAAG,EAAE;IAClE,EAAE,CAAC,2EAA2E,EAAE,KAAK,IAAI,EAAE;QACzF,MAAM,WAAW,GAAqB;YACpC,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,oBAAoB,EAAE;SACpD,CAAC;QACF,kFAAkF;QAClF,MAAM,WAAW,GAAG,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,mBAAmB,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QAE3D,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,MAAM,IAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QAElC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC;QACxE,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;QAC5D,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,UAAU,GAAG;YACjB,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE;SAC9D,CAAC;QACF,MAAM,QAAQ,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QACvD,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,CAAC;QAErD,MAAM,OAAO,GAAG,oBAAoB,CAAC;YACnC,YAAY,EAAE;gBACZ,UAAU,EAAE,MAAM;gBAClB,QAAQ,EAAE,EAAE,CAAC,EAAE,mBAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE;aAC9D;SACF,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAO,cAAc,CAAC,YAAY,CAAC,OAAO,CAAsB,CAAC;QAEhF,MAAM,CAAC,QAAQ,CAAC,CAAC,oBAAoB,CAAC;YACpC,CAAC,EAAE,mBAAmB;YACtB,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,CAAC;SACT,CAAC,CAAC;QACH,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,mCAAmC;AACnC,8EAA8E;AAE9E,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,WAAW,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,YAAY,CAAC,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAE5B,MAAM,UAAU,GAAG,CAAC,CAAC;QACrB,MAAM,gBAAgB,GAAa,EAAE,CAAC;QACtC,MAAM,eAAe,GAAa,EAAE,CAAC;QAErC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,eAAe;YACf,MAAM,GAAG,GAAG,oBAAoB,CAAC;gBAC/B,YAAY,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;aACrD,CAAC,CAAC;YACH,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YACvC,MAAO,cAAc,CAAC,YAAY,CAAC,GAAG,CAAsB,CAAC;YAC7D,gBAAgB,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC;YAExD,uBAAuB;YACvB,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;YACtC,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;YACxB,eAAe,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC;QACxD,CAAC;QAED,MAAM,UAAU,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC;QAC5E,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,UAAU,CAAC;QAC1E,MAAM,QAAQ,GAAG,UAAU,GAAG,SAAS,CAAC;QAExC,OAAO,CAAC,GAAG,CACT,uBAAuB,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;YAClD,eAAe,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM;YACzC,aAAa,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CACrC,CAAC;QAEF,6EAA6E;QAC7E,MAAM,CAAC,UAAU,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import vm from "node:vm";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { createSandboxContext } from "../src/sandbox.js";
|
|
4
|
+
describe("createSandboxContext", () => {
|
|
5
|
+
it("creates a valid vm context", () => {
|
|
6
|
+
const ctx = createSandboxContext();
|
|
7
|
+
expect(vm.isContext(ctx)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
describe("isolation — blocked host APIs", () => {
|
|
10
|
+
it("does not expose process", () => {
|
|
11
|
+
const ctx = createSandboxContext();
|
|
12
|
+
const result = vm.runInContext("typeof process", ctx);
|
|
13
|
+
expect(result).toBe("undefined");
|
|
14
|
+
});
|
|
15
|
+
it("does not expose process.env (guard against prototype chain leakage)", () => {
|
|
16
|
+
const ctx = createSandboxContext();
|
|
17
|
+
// If `process` is undefined, accessing .env should throw or be undefined
|
|
18
|
+
const result = vm.runInContext("try { typeof process.env } catch (e) { 'threw' }", ctx);
|
|
19
|
+
// Either 'threw' (TypeError: Cannot read properties of undefined) or 'undefined'
|
|
20
|
+
expect(["threw", "undefined"]).toContain(result);
|
|
21
|
+
});
|
|
22
|
+
it("does not expose require", () => {
|
|
23
|
+
const ctx = createSandboxContext();
|
|
24
|
+
expect(vm.runInContext("typeof require", ctx)).toBe("undefined");
|
|
25
|
+
});
|
|
26
|
+
it("does not expose global", () => {
|
|
27
|
+
const ctx = createSandboxContext();
|
|
28
|
+
// `global` is Node.js-specific; not in context
|
|
29
|
+
expect(vm.runInContext("typeof global", ctx)).toBe("undefined");
|
|
30
|
+
});
|
|
31
|
+
it("does not expose Buffer", () => {
|
|
32
|
+
const ctx = createSandboxContext();
|
|
33
|
+
expect(vm.runInContext("typeof Buffer", ctx)).toBe("undefined");
|
|
34
|
+
});
|
|
35
|
+
it("does not expose __dirname", () => {
|
|
36
|
+
const ctx = createSandboxContext();
|
|
37
|
+
expect(vm.runInContext("typeof __dirname", ctx)).toBe("undefined");
|
|
38
|
+
});
|
|
39
|
+
it("does not expose __filename", () => {
|
|
40
|
+
const ctx = createSandboxContext();
|
|
41
|
+
expect(vm.runInContext("typeof __filename", ctx)).toBe("undefined");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
describe("safe globals — accessible inside sandbox", () => {
|
|
45
|
+
it("exposes JSON", () => {
|
|
46
|
+
const ctx = createSandboxContext();
|
|
47
|
+
expect(vm.runInContext("JSON.stringify({ a: 1 })", ctx)).toBe('{"a":1}');
|
|
48
|
+
});
|
|
49
|
+
it("exposes Math", () => {
|
|
50
|
+
const ctx = createSandboxContext();
|
|
51
|
+
expect(vm.runInContext("Math.max(2, 3)", ctx)).toBe(3);
|
|
52
|
+
});
|
|
53
|
+
it("exposes Date", () => {
|
|
54
|
+
const ctx = createSandboxContext();
|
|
55
|
+
expect(vm.runInContext("typeof new Date()", ctx)).toBe("object");
|
|
56
|
+
});
|
|
57
|
+
it("exposes URL", () => {
|
|
58
|
+
const ctx = createSandboxContext();
|
|
59
|
+
const href = vm.runInContext('new URL("https://example.com").href', ctx);
|
|
60
|
+
expect(href).toBe("https://example.com/");
|
|
61
|
+
});
|
|
62
|
+
it("exposes Promise", () => {
|
|
63
|
+
const ctx = createSandboxContext();
|
|
64
|
+
expect(vm.runInContext("typeof Promise.resolve", ctx)).toBe("function");
|
|
65
|
+
});
|
|
66
|
+
it("exposes setTimeout", () => {
|
|
67
|
+
const ctx = createSandboxContext();
|
|
68
|
+
expect(vm.runInContext("typeof setTimeout", ctx)).toBe("function");
|
|
69
|
+
});
|
|
70
|
+
it("exposes TextEncoder / TextDecoder", () => {
|
|
71
|
+
const ctx = createSandboxContext();
|
|
72
|
+
expect(vm.runInContext("typeof TextEncoder", ctx)).toBe("function");
|
|
73
|
+
expect(vm.runInContext("typeof TextDecoder", ctx)).toBe("function");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
describe("extraGlobals", () => {
|
|
77
|
+
it("injects arbitrary globals into the context", () => {
|
|
78
|
+
const ctx = createSandboxContext({
|
|
79
|
+
extraGlobals: { __answer__: 42 },
|
|
80
|
+
});
|
|
81
|
+
expect(vm.runInContext("__answer__", ctx)).toBe(42);
|
|
82
|
+
});
|
|
83
|
+
it("injected bridge object is callable from the sandbox", () => {
|
|
84
|
+
const bridge = { call: (args) => ({ echo: args }) };
|
|
85
|
+
const ctx = createSandboxContext({
|
|
86
|
+
extraGlobals: {
|
|
87
|
+
__bridge__: bridge,
|
|
88
|
+
__args__: { x: 1 },
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
const result = vm.runInContext("__bridge__.call(__args__)", ctx);
|
|
92
|
+
expect(result.echo).toEqual({ x: 1 });
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
describe("context isolation between calls", () => {
|
|
96
|
+
it("state written in one context does not leak to another", () => {
|
|
97
|
+
const ctx1 = createSandboxContext();
|
|
98
|
+
const ctx2 = createSandboxContext();
|
|
99
|
+
vm.runInContext("var secret = 'ctx1-secret'", ctx1);
|
|
100
|
+
expect(vm.runInContext("typeof secret", ctx2)).toBe("undefined");
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
//# sourceMappingURL=sandbox.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sandbox.test.js","sourceRoot":"","sources":["../../__tests__/sandbox.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
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 { type AuthProvider } from "@utdk/common";
|
|
19
|
+
import type { UtdkAuthConfig } from "./types.js";
|
|
20
|
+
/**
|
|
21
|
+
* Resolves an `AuthProvider` from a flat credentials map and the provider's
|
|
22
|
+
* `utdk.auth` config array (from its `package.json`).
|
|
23
|
+
*
|
|
24
|
+
* Supported patterns:
|
|
25
|
+
* api_key – Bearer header: `api_key: "Bearer ${TOKEN_NAME}"`
|
|
26
|
+
* api_key – Raw header: `api_key: "${TOKEN_NAME}"`
|
|
27
|
+
* oauth2 – Pre-resolved: looks for `PROVIDER_ACCESS_TOKEN` key
|
|
28
|
+
*
|
|
29
|
+
* Falls back to `BearerToken(firstValue)` when the auth config is absent or
|
|
30
|
+
* the pattern is not recognised.
|
|
31
|
+
*/
|
|
32
|
+
export declare function resolveAuthProvider(credentials: Record<string, string>, authConfigs?: UtdkAuthConfig[]): AuthProvider | undefined;
|
|
33
|
+
/**
|
|
34
|
+
* Extracts the `utdk.auth` array from a provider module's package.json.
|
|
35
|
+
* Returns an empty array if the module does not export a `packageJson`
|
|
36
|
+
* property or if the config is absent.
|
|
37
|
+
*/
|
|
38
|
+
export declare function extractAuthConfigs(providerModule: Record<string, unknown>): UtdkAuthConfig[];
|
|
39
|
+
/**
|
|
40
|
+
* Resolves a dot-notation operation path on a client object.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* resolveOperation(client, 'users.getByUsername')
|
|
44
|
+
* // → client.users.getByUsername (as a bound function)
|
|
45
|
+
*/
|
|
46
|
+
export declare function resolveOperation(client: unknown, operationPath: string): (...args: unknown[]) => Promise<unknown>;
|
|
47
|
+
export interface BridgeOptions {
|
|
48
|
+
/** The resolved operation function (from host context) */
|
|
49
|
+
operation: (...args: unknown[]) => Promise<unknown>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Creates a bridge object that is safe to expose inside a vm context.
|
|
53
|
+
*
|
|
54
|
+
* The bridge has a single method, `call(args)`, which invokes the pre-resolved
|
|
55
|
+
* provider operation with the given arguments. Credentials are already baked
|
|
56
|
+
* into the operation via the auth provider — the sandbox script never sees
|
|
57
|
+
* them.
|
|
58
|
+
*/
|
|
59
|
+
export declare function createBridge(options: BridgeOptions): {
|
|
60
|
+
call: (args: Record<string, unknown>) => Promise<unknown>;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Locates and invokes the `create*Client` factory in a provider module.
|
|
64
|
+
*
|
|
65
|
+
* Provider modules export a `create<Name>Client(options?)` function that
|
|
66
|
+
* returns a `Promise<Client>`. This helper finds it and calls it with the
|
|
67
|
+
* supplied auth provider.
|
|
68
|
+
*
|
|
69
|
+
* @throws if no factory function matching `create*Client` is found.
|
|
70
|
+
*/
|
|
71
|
+
export declare function loadProviderClient(providerModule: Record<string, unknown>, auth: AuthProvider | undefined): Promise<unknown>;
|