@secure-exec/browser 0.2.0-rc.1 → 0.3.0-rc.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.
- package/README.md +4 -5
- package/dist/driver.d.ts +10 -4
- package/dist/driver.js +18 -3
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -2
- package/dist/os-filesystem.d.ts +4 -4
- package/dist/os-filesystem.js +37 -19
- package/dist/runtime-driver.d.ts +11 -3
- package/dist/runtime-driver.js +271 -22
- package/dist/runtime.d.ts +222 -0
- package/dist/runtime.js +377 -0
- package/dist/sync-bridge.d.ts +46 -0
- package/dist/sync-bridge.js +49 -0
- package/dist/worker-adapter.js +1 -0
- package/dist/worker-protocol.d.ts +33 -10
- package/dist/worker.js +897 -116
- package/package.json +79 -66
- package/LICENSE +0 -191
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
# Secure Exec
|
|
1
|
+
# Secure Exec Browser
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Browser driver primitives for secure-exec.
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
- [GitHub](https://github.com/rivet-dev/secure-exec)
|
|
5
|
+
- Package: `@secure-exec/browser`
|
|
6
|
+
- Exports: `createBrowserDriver`, `createBrowserRuntimeDriverFactory`, `createOpfsFileSystem`, `BrowserWorkerAdapter`
|
package/dist/driver.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import type { NetworkAdapter, SystemDriver } from "@secure-exec/core";
|
|
1
|
+
import type { NetworkAdapter, Permissions, SystemDriver, VirtualFileSystem } from "./runtime.js";
|
|
2
|
+
import { createCommandExecutorStub, createFsStub, createNetworkStub } from "./runtime.js";
|
|
4
3
|
export interface BrowserRuntimeSystemOptions {
|
|
5
4
|
filesystem: "opfs" | "memory";
|
|
6
5
|
networkEnabled: boolean;
|
|
@@ -31,6 +30,9 @@ export declare class OpfsFileSystem implements VirtualFileSystem {
|
|
|
31
30
|
stat(path: string): Promise<{
|
|
32
31
|
mode: number;
|
|
33
32
|
size: number;
|
|
33
|
+
blocks: number;
|
|
34
|
+
dev: number;
|
|
35
|
+
rdev: number;
|
|
34
36
|
isDirectory: boolean;
|
|
35
37
|
isSymbolicLink: boolean;
|
|
36
38
|
atimeMs: number;
|
|
@@ -50,6 +52,9 @@ export declare class OpfsFileSystem implements VirtualFileSystem {
|
|
|
50
52
|
lstat(path: string): Promise<{
|
|
51
53
|
mode: number;
|
|
52
54
|
size: number;
|
|
55
|
+
blocks: number;
|
|
56
|
+
dev: number;
|
|
57
|
+
rdev: number;
|
|
53
58
|
isDirectory: boolean;
|
|
54
59
|
isSymbolicLink: boolean;
|
|
55
60
|
atimeMs: number;
|
|
@@ -68,6 +73,7 @@ export declare class OpfsFileSystem implements VirtualFileSystem {
|
|
|
68
73
|
truncate(path: string, length: number): Promise<void>;
|
|
69
74
|
realpath(path: string): Promise<string>;
|
|
70
75
|
pread(path: string, offset: number, length: number): Promise<Uint8Array>;
|
|
76
|
+
pwrite(path: string, offset: number, data: Uint8Array): Promise<void>;
|
|
71
77
|
}
|
|
72
78
|
export interface BrowserDriverOptions {
|
|
73
79
|
filesystem?: "opfs" | "memory";
|
|
@@ -82,4 +88,4 @@ export declare function createBrowserNetworkAdapter(): NetworkAdapter;
|
|
|
82
88
|
export declare function getBrowserSystemDriverOptions(systemDriver: SystemDriver): BrowserRuntimeSystemOptions;
|
|
83
89
|
/** Assemble a browser-side SystemDriver with permission-wrapped adapters. */
|
|
84
90
|
export declare function createBrowserDriver(options?: BrowserDriverOptions): Promise<SystemDriver>;
|
|
85
|
-
export { createCommandExecutorStub, createFsStub, createNetworkStub
|
|
91
|
+
export { createCommandExecutorStub, createFsStub, createNetworkStub };
|
package/dist/driver.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createCommandExecutorStub, createFsStub, createNetworkStub, wrapFileSystem, wrapNetworkAdapter,
|
|
1
|
+
import { createCommandExecutorStub, createEnosysError, createFsStub, createInMemoryFileSystem, createNetworkStub, wrapFileSystem, wrapNetworkAdapter, } from "./runtime.js";
|
|
2
2
|
const S_IFREG = 0o100000;
|
|
3
3
|
const S_IFDIR = 0o040000;
|
|
4
4
|
const BROWSER_SYSTEM_DRIVER_OPTIONS = Symbol.for("secure-exec.browserSystemDriverOptions");
|
|
@@ -133,6 +133,9 @@ export class OpfsFileSystem {
|
|
|
133
133
|
return {
|
|
134
134
|
mode: S_IFREG | 0o644,
|
|
135
135
|
size: file.size,
|
|
136
|
+
blocks: file.size === 0 ? 0 : Math.ceil(file.size / 512),
|
|
137
|
+
dev: 1,
|
|
138
|
+
rdev: 0,
|
|
136
139
|
isDirectory: false,
|
|
137
140
|
isSymbolicLink: false,
|
|
138
141
|
atimeMs: file.lastModified,
|
|
@@ -153,6 +156,9 @@ export class OpfsFileSystem {
|
|
|
153
156
|
return {
|
|
154
157
|
mode: S_IFDIR | 0o755,
|
|
155
158
|
size: 4096,
|
|
159
|
+
blocks: 8,
|
|
160
|
+
dev: 1,
|
|
161
|
+
rdev: 0,
|
|
156
162
|
isDirectory: true,
|
|
157
163
|
isSymbolicLink: false,
|
|
158
164
|
atimeMs: now,
|
|
@@ -227,10 +233,19 @@ export class OpfsFileSystem {
|
|
|
227
233
|
const data = await this.readFile(path);
|
|
228
234
|
return data.slice(offset, offset + length);
|
|
229
235
|
}
|
|
236
|
+
async pwrite(path, offset, data) {
|
|
237
|
+
const content = await this.readFile(path);
|
|
238
|
+
const endPos = offset + data.length;
|
|
239
|
+
const newContent = new Uint8Array(Math.max(content.length, endPos));
|
|
240
|
+
newContent.set(content);
|
|
241
|
+
newContent.set(data, offset);
|
|
242
|
+
await this.writeFile(path, newContent);
|
|
243
|
+
}
|
|
230
244
|
}
|
|
231
245
|
/** Create an OPFS-backed filesystem, falling back to in-memory if OPFS is unavailable. */
|
|
232
246
|
export async function createOpfsFileSystem() {
|
|
233
|
-
if (!("storage" in navigator) ||
|
|
247
|
+
if (!("storage" in navigator) ||
|
|
248
|
+
typeof navigator.storage.getDirectory !== "function") {
|
|
234
249
|
return createInMemoryFileSystem();
|
|
235
250
|
}
|
|
236
251
|
return new OpfsFileSystem();
|
|
@@ -332,4 +347,4 @@ export async function createBrowserDriver(options = {}) {
|
|
|
332
347
|
};
|
|
333
348
|
return systemDriver;
|
|
334
349
|
}
|
|
335
|
-
export { createCommandExecutorStub, createFsStub, createNetworkStub
|
|
350
|
+
export { createCommandExecutorStub, createFsStub, createNetworkStub };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
export { createBrowserDriver, createBrowserNetworkAdapter, createOpfsFileSystem, } from "./driver.js";
|
|
2
1
|
export type { BrowserDriverOptions, BrowserRuntimeSystemOptions, } from "./driver.js";
|
|
3
|
-
export {
|
|
4
|
-
export type { BrowserRuntimeDriverFactoryOptions, } from "./runtime-driver.js";
|
|
5
|
-
export { createInMemoryFileSystem } from "@secure-exec/core";
|
|
2
|
+
export { createBrowserDriver, createBrowserNetworkAdapter, createOpfsFileSystem, } from "./driver.js";
|
|
6
3
|
export { InMemoryFileSystem } from "./os-filesystem.js";
|
|
7
|
-
export {
|
|
4
|
+
export type { ExecOptions, ExecResult, NodeRuntimeDriver, StdioChannel, StdioEvent, TimingMitigation, } from "./runtime.js";
|
|
5
|
+
export { allowAll, allowAllChildProcess, allowAllEnv, allowAllFs, allowAllNetwork, createInMemoryFileSystem, } from "./runtime.js";
|
|
6
|
+
export type { BrowserRuntimeDriverFactoryOptions } from "./runtime-driver.js";
|
|
7
|
+
export { createBrowserRuntimeDriverFactory } from "./runtime-driver.js";
|
|
8
8
|
export type { WorkerHandle } from "./worker-adapter.js";
|
|
9
|
+
export { BrowserWorkerAdapter } from "./worker-adapter.js";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createBrowserDriver, createBrowserNetworkAdapter, createOpfsFileSystem, } from "./driver.js";
|
|
2
|
-
export { createBrowserRuntimeDriverFactory, } from "./runtime-driver.js";
|
|
3
|
-
export { createInMemoryFileSystem } from "@secure-exec/core";
|
|
4
2
|
export { InMemoryFileSystem } from "./os-filesystem.js";
|
|
3
|
+
export { allowAll, allowAllChildProcess, allowAllEnv, allowAllFs, allowAllNetwork, createInMemoryFileSystem, } from "./runtime.js";
|
|
4
|
+
export { createBrowserRuntimeDriverFactory } from "./runtime-driver.js";
|
|
5
5
|
export { BrowserWorkerAdapter } from "./worker-adapter.js";
|
package/dist/os-filesystem.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-memory filesystem for browser environments.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* needed by the kernel VFS interface.
|
|
4
|
+
* In-memory filesystem with POSIX extensions (symlinks, hard links, chmod,
|
|
5
|
+
* chown, utimes, truncate) needed by the kernel VFS interface.
|
|
7
6
|
*/
|
|
8
|
-
import type { VirtualFileSystem, VirtualStat
|
|
7
|
+
import type { VirtualDirEntry, VirtualFileSystem, VirtualStat } from "./runtime.js";
|
|
9
8
|
export declare class InMemoryFileSystem implements VirtualFileSystem {
|
|
10
9
|
private entries;
|
|
11
10
|
constructor();
|
|
@@ -33,6 +32,7 @@ export declare class InMemoryFileSystem implements VirtualFileSystem {
|
|
|
33
32
|
utimes(path: string, atime: number, mtime: number): Promise<void>;
|
|
34
33
|
truncate(path: string, length: number): Promise<void>;
|
|
35
34
|
pread(path: string, offset: number, length: number): Promise<Uint8Array>;
|
|
35
|
+
pwrite(path: string, offset: number, data: Uint8Array): Promise<void>;
|
|
36
36
|
/**
|
|
37
37
|
* Resolve symlinks to get the final path. Returns the normalized path
|
|
38
38
|
* after following all symlinks.
|
package/dist/os-filesystem.js
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* In-memory filesystem for browser environments.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* needed by the kernel VFS interface.
|
|
4
|
+
* In-memory filesystem with POSIX extensions (symlinks, hard links, chmod,
|
|
5
|
+
* chown, utimes, truncate) needed by the kernel VFS interface.
|
|
7
6
|
*/
|
|
8
7
|
const S_IFREG = 0o100000;
|
|
9
8
|
const S_IFDIR = 0o040000;
|
|
@@ -30,13 +29,13 @@ function normalizePath(path) {
|
|
|
30
29
|
resolved.push(part);
|
|
31
30
|
}
|
|
32
31
|
}
|
|
33
|
-
return
|
|
32
|
+
return `/${resolved.join("/")}` || "/";
|
|
34
33
|
}
|
|
35
34
|
function dirname(path) {
|
|
36
35
|
const parts = normalizePath(path).split("/").filter(Boolean);
|
|
37
36
|
if (parts.length <= 1)
|
|
38
37
|
return "/";
|
|
39
|
-
return
|
|
38
|
+
return `/${parts.slice(0, -1).join("/")}`;
|
|
40
39
|
}
|
|
41
40
|
let nextIno = 1;
|
|
42
41
|
export class InMemoryFileSystem {
|
|
@@ -67,7 +66,7 @@ export class InMemoryFileSystem {
|
|
|
67
66
|
if (!dir || dir.type !== "dir") {
|
|
68
67
|
throw this.enoent("scandir", path);
|
|
69
68
|
}
|
|
70
|
-
const prefix = resolved === "/" ? "/" : resolved
|
|
69
|
+
const prefix = resolved === "/" ? "/" : `${resolved}/`;
|
|
71
70
|
const names = new Map();
|
|
72
71
|
for (const [entryPath, entry] of this.entries) {
|
|
73
72
|
if (entryPath.startsWith(prefix)) {
|
|
@@ -87,9 +86,7 @@ export class InMemoryFileSystem {
|
|
|
87
86
|
const normalized = normalizePath(path);
|
|
88
87
|
// Ensure parent exists
|
|
89
88
|
await this.mkdir(dirname(normalized), { recursive: true });
|
|
90
|
-
const data = typeof content === "string"
|
|
91
|
-
? new TextEncoder().encode(content)
|
|
92
|
-
: content;
|
|
89
|
+
const data = typeof content === "string" ? new TextEncoder().encode(content) : content;
|
|
93
90
|
const existing = this.entries.get(normalized);
|
|
94
91
|
if (existing && existing.type === "file") {
|
|
95
92
|
existing.data = data;
|
|
@@ -129,7 +126,7 @@ export class InMemoryFileSystem {
|
|
|
129
126
|
const parts = normalized.split("/").filter(Boolean);
|
|
130
127
|
let current = "";
|
|
131
128
|
for (const part of parts) {
|
|
132
|
-
current +=
|
|
129
|
+
current += `/${part}`;
|
|
133
130
|
if (!this.entries.has(current)) {
|
|
134
131
|
this.entries.set(current, this.newDir());
|
|
135
132
|
}
|
|
@@ -172,7 +169,7 @@ export class InMemoryFileSystem {
|
|
|
172
169
|
throw this.enoent("rmdir", path);
|
|
173
170
|
}
|
|
174
171
|
// Check if empty
|
|
175
|
-
const prefix = resolved
|
|
172
|
+
const prefix = `${resolved}/`;
|
|
176
173
|
for (const key of this.entries.keys()) {
|
|
177
174
|
if (key.startsWith(prefix)) {
|
|
178
175
|
throw new Error(`ENOTEMPTY: directory not empty, rmdir '${path}'`);
|
|
@@ -199,7 +196,7 @@ export class InMemoryFileSystem {
|
|
|
199
196
|
return;
|
|
200
197
|
}
|
|
201
198
|
// Move directory and all children
|
|
202
|
-
const prefix = oldResolved
|
|
199
|
+
const prefix = `${oldResolved}/`;
|
|
203
200
|
const toMove = [];
|
|
204
201
|
for (const [key, val] of this.entries) {
|
|
205
202
|
if (key === oldResolved || key.startsWith(prefix)) {
|
|
@@ -210,9 +207,7 @@ export class InMemoryFileSystem {
|
|
|
210
207
|
this.entries.delete(key);
|
|
211
208
|
}
|
|
212
209
|
for (const [key, val] of toMove) {
|
|
213
|
-
const newKey = key === oldResolved
|
|
214
|
-
? newNorm
|
|
215
|
-
: newNorm + key.slice(oldResolved.length);
|
|
210
|
+
const newKey = key === oldResolved ? newNorm : newNorm + key.slice(oldResolved.length);
|
|
216
211
|
this.entries.set(newKey, val);
|
|
217
212
|
}
|
|
218
213
|
}
|
|
@@ -270,8 +265,13 @@ export class InMemoryFileSystem {
|
|
|
270
265
|
const entry = this.resolveEntry(path);
|
|
271
266
|
if (!entry)
|
|
272
267
|
throw this.enoent("chmod", path);
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
const callerTypeBits = mode & 0o170000;
|
|
269
|
+
if (callerTypeBits !== 0) {
|
|
270
|
+
entry.mode = mode;
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
entry.mode = (entry.mode & 0o170000) | (mode & 0o7777);
|
|
274
|
+
}
|
|
275
275
|
entry.ctimeMs = Date.now();
|
|
276
276
|
}
|
|
277
277
|
async chown(path, uid, gid) {
|
|
@@ -316,6 +316,20 @@ export class InMemoryFileSystem {
|
|
|
316
316
|
return new Uint8Array(0);
|
|
317
317
|
return entry.data.slice(offset, Math.min(offset + length, entry.data.length));
|
|
318
318
|
}
|
|
319
|
+
async pwrite(path, offset, data) {
|
|
320
|
+
const entry = this.resolveEntry(path);
|
|
321
|
+
if (!entry || entry.type !== "file") {
|
|
322
|
+
throw this.enoent("open", path);
|
|
323
|
+
}
|
|
324
|
+
const endPos = offset + data.length;
|
|
325
|
+
const newContent = new Uint8Array(Math.max(entry.data.length, endPos));
|
|
326
|
+
newContent.set(entry.data);
|
|
327
|
+
newContent.set(data, offset);
|
|
328
|
+
entry.data = newContent;
|
|
329
|
+
const now = Date.now();
|
|
330
|
+
entry.mtimeMs = now;
|
|
331
|
+
entry.ctimeMs = now;
|
|
332
|
+
}
|
|
319
333
|
// --- Helpers ---
|
|
320
334
|
/**
|
|
321
335
|
* Resolve symlinks to get the final path. Returns the normalized path
|
|
@@ -332,7 +346,7 @@ export class InMemoryFileSystem {
|
|
|
332
346
|
if (entry.type === "symlink") {
|
|
333
347
|
const target = entry.target.startsWith("/")
|
|
334
348
|
? entry.target
|
|
335
|
-
: dirname(normalized)
|
|
349
|
+
: `${dirname(normalized)}/${entry.target}`;
|
|
336
350
|
return this.resolvePath(target, depth + 1);
|
|
337
351
|
}
|
|
338
352
|
return normalized;
|
|
@@ -358,9 +372,13 @@ export class InMemoryFileSystem {
|
|
|
358
372
|
};
|
|
359
373
|
}
|
|
360
374
|
toStat(entry) {
|
|
375
|
+
const size = entry.type === "file" ? entry.data.length : 4096;
|
|
361
376
|
return {
|
|
362
377
|
mode: entry.mode,
|
|
363
|
-
size
|
|
378
|
+
size,
|
|
379
|
+
blocks: size === 0 ? 0 : Math.ceil(size / 512),
|
|
380
|
+
dev: 1,
|
|
381
|
+
rdev: 0,
|
|
364
382
|
isDirectory: entry.type === "dir",
|
|
365
383
|
isSymbolicLink: entry.type === "symlink",
|
|
366
384
|
atimeMs: entry.atimeMs,
|
package/dist/runtime-driver.d.ts
CHANGED
|
@@ -1,25 +1,33 @@
|
|
|
1
|
-
import type { NetworkAdapter, NodeRuntimeDriver, NodeRuntimeDriverFactory, RuntimeDriverOptions } from "
|
|
2
|
-
import type { ExecOptions, ExecResult, RunResult } from "@secure-exec/core";
|
|
1
|
+
import type { ExecOptions, ExecResult, NetworkAdapter, NodeRuntimeDriver, NodeRuntimeDriverFactory, RunResult, RuntimeDriverOptions } from "./runtime.js";
|
|
3
2
|
export interface BrowserRuntimeDriverFactoryOptions {
|
|
4
3
|
workerUrl?: URL | string;
|
|
5
4
|
}
|
|
6
5
|
export declare class BrowserRuntimeDriver implements NodeRuntimeDriver {
|
|
7
|
-
private readonly options;
|
|
8
6
|
private readonly worker;
|
|
9
7
|
private readonly pending;
|
|
8
|
+
private readonly controlToken;
|
|
10
9
|
private readonly defaultOnStdio?;
|
|
10
|
+
private readonly defaultTimingMitigation;
|
|
11
11
|
private readonly networkAdapter;
|
|
12
|
+
private readonly syncBridge;
|
|
13
|
+
private readonly syncFilesystem;
|
|
12
14
|
private readonly ready;
|
|
15
|
+
private readonly encoder;
|
|
13
16
|
private nextId;
|
|
14
17
|
private disposed;
|
|
15
18
|
constructor(options: RuntimeDriverOptions, factoryOptions?: BrowserRuntimeDriverFactoryOptions);
|
|
16
19
|
get network(): Pick<NetworkAdapter, "fetch" | "dnsLookup" | "httpRequest">;
|
|
17
20
|
private handleWorkerError;
|
|
18
21
|
private handleWorkerMessage;
|
|
22
|
+
private handleSyncRequest;
|
|
19
23
|
private rejectAllPending;
|
|
24
|
+
private clearWorkerHandlers;
|
|
25
|
+
private resetSyncBridgeState;
|
|
26
|
+
private cleanup;
|
|
20
27
|
private callWorker;
|
|
21
28
|
run<T = unknown>(code: string, filePath?: string): Promise<RunResult<T>>;
|
|
22
29
|
exec(code: string, options?: ExecOptions): Promise<ExecResult>;
|
|
30
|
+
dispatchExtensionRequest(namespace: string, payload: Uint8Array): Promise<Uint8Array>;
|
|
23
31
|
dispose(): void;
|
|
24
32
|
terminate(): Promise<void>;
|
|
25
33
|
}
|