@ebowwa/sandbox 0.1.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 (108) hide show
  1. package/dist/compilers/index.d.ts +24 -0
  2. package/dist/compilers/index.d.ts.map +1 -0
  3. package/dist/compilers/index.js +42 -0
  4. package/dist/compilers/index.js.map +1 -0
  5. package/dist/compilers/javascript.d.ts +117 -0
  6. package/dist/compilers/javascript.d.ts.map +1 -0
  7. package/dist/compilers/javascript.js +462 -0
  8. package/dist/compilers/javascript.js.map +1 -0
  9. package/dist/compilers/python.d.ts +140 -0
  10. package/dist/compilers/python.d.ts.map +1 -0
  11. package/dist/compilers/python.js +650 -0
  12. package/dist/compilers/python.js.map +1 -0
  13. package/dist/compilers/typescript.d.ts +99 -0
  14. package/dist/compilers/typescript.d.ts.map +1 -0
  15. package/dist/compilers/typescript.js +323 -0
  16. package/dist/compilers/typescript.js.map +1 -0
  17. package/dist/core/cell.d.ts +160 -0
  18. package/dist/core/cell.d.ts.map +1 -0
  19. package/dist/core/cell.js +319 -0
  20. package/dist/core/cell.js.map +1 -0
  21. package/dist/core/compiler.d.ts +126 -0
  22. package/dist/core/compiler.d.ts.map +1 -0
  23. package/dist/core/compiler.js +123 -0
  24. package/dist/core/compiler.js.map +1 -0
  25. package/dist/core/index.d.ts +19 -0
  26. package/dist/core/index.d.ts.map +1 -0
  27. package/dist/core/index.js +14 -0
  28. package/dist/core/index.js.map +1 -0
  29. package/dist/core/limits.d.ts +173 -0
  30. package/dist/core/limits.d.ts.map +1 -0
  31. package/dist/core/limits.js +440 -0
  32. package/dist/core/limits.js.map +1 -0
  33. package/dist/core/permissions.d.ts +103 -0
  34. package/dist/core/permissions.d.ts.map +1 -0
  35. package/dist/core/permissions.js +341 -0
  36. package/dist/core/permissions.js.map +1 -0
  37. package/dist/core/runtime.d.ts +127 -0
  38. package/dist/core/runtime.d.ts.map +1 -0
  39. package/dist/core/runtime.js +325 -0
  40. package/dist/core/runtime.js.map +1 -0
  41. package/dist/core/types.d.ts +380 -0
  42. package/dist/core/types.d.ts.map +1 -0
  43. package/dist/core/types.js +67 -0
  44. package/dist/core/types.js.map +1 -0
  45. package/dist/index.d.ts +145 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +279 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/multi/index.d.ts +9 -0
  50. package/dist/multi/index.d.ts.map +1 -0
  51. package/dist/multi/index.js +7 -0
  52. package/dist/multi/index.js.map +1 -0
  53. package/dist/multi/polyglot.d.ts +179 -0
  54. package/dist/multi/polyglot.d.ts.map +1 -0
  55. package/dist/multi/polyglot.js +319 -0
  56. package/dist/multi/polyglot.js.map +1 -0
  57. package/dist/runtimes/docker.d.ts +97 -0
  58. package/dist/runtimes/docker.d.ts.map +1 -0
  59. package/dist/runtimes/docker.js +368 -0
  60. package/dist/runtimes/docker.js.map +1 -0
  61. package/dist/runtimes/index.d.ts +11 -0
  62. package/dist/runtimes/index.d.ts.map +1 -0
  63. package/dist/runtimes/index.js +9 -0
  64. package/dist/runtimes/index.js.map +1 -0
  65. package/dist/runtimes/process.d.ts +47 -0
  66. package/dist/runtimes/process.d.ts.map +1 -0
  67. package/dist/runtimes/process.js +230 -0
  68. package/dist/runtimes/process.js.map +1 -0
  69. package/dist/session/index.d.ts +12 -0
  70. package/dist/session/index.d.ts.map +1 -0
  71. package/dist/session/index.js +9 -0
  72. package/dist/session/index.js.map +1 -0
  73. package/dist/session/kernel.d.ts +199 -0
  74. package/dist/session/kernel.d.ts.map +1 -0
  75. package/dist/session/kernel.js +400 -0
  76. package/dist/session/kernel.js.map +1 -0
  77. package/dist/session/notebook.d.ts +168 -0
  78. package/dist/session/notebook.d.ts.map +1 -0
  79. package/dist/session/notebook.js +499 -0
  80. package/dist/session/notebook.js.map +1 -0
  81. package/dist/session/repl.d.ts +159 -0
  82. package/dist/session/repl.d.ts.map +1 -0
  83. package/dist/session/repl.js +409 -0
  84. package/dist/session/repl.js.map +1 -0
  85. package/package.json +142 -0
  86. package/src/compilers/index.ts +80 -0
  87. package/src/compilers/javascript.ts +571 -0
  88. package/src/compilers/python.ts +785 -0
  89. package/src/compilers/typescript.ts +442 -0
  90. package/src/core/cell.ts +439 -0
  91. package/src/core/compiler.ts +250 -0
  92. package/src/core/index.ts +123 -0
  93. package/src/core/limits.ts +508 -0
  94. package/src/core/permissions.ts +409 -0
  95. package/src/core/runtime.ts +499 -0
  96. package/src/core/types.ts +528 -0
  97. package/src/global.d.ts +59 -0
  98. package/src/index.ts +515 -0
  99. package/src/multi/index.ts +22 -0
  100. package/src/multi/polyglot.ts +461 -0
  101. package/src/runtimes/docker.ts +501 -0
  102. package/src/runtimes/index.ts +21 -0
  103. package/src/runtimes/process.ts +316 -0
  104. package/src/session/index.ts +41 -0
  105. package/src/session/kernel.ts +553 -0
  106. package/src/session/notebook.ts +635 -0
  107. package/src/session/repl.ts +521 -0
  108. package/src/wasm2wasm.d.ts +35 -0
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Permissions Primitive
3
+ *
4
+ * Composable permission enforcement for sandbox execution.
5
+ * Each permission can be independently granted or denied.
6
+ */
7
+
8
+ import type {
9
+ Permissions,
10
+ PermissionLevel,
11
+ FileSystemPermission,
12
+ NetworkPermission,
13
+ EnvironmentPermission,
14
+ ProcessPermission,
15
+ } from "./types.js";
16
+
17
+ /**
18
+ * Permission Checker
19
+ *
20
+ * Validates permissions against allowed operations.
21
+ */
22
+ export class PermissionChecker {
23
+ constructor(private permissions: Permissions) {}
24
+
25
+ /**
26
+ * Check if filesystem read is allowed
27
+ */
28
+ canReadFile(path?: string): boolean {
29
+ const fs = this.permissions.fs;
30
+ if (!fs?.read) return false;
31
+
32
+ if (path) {
33
+ return this.isPathAllowed(path, fs);
34
+ }
35
+ return true;
36
+ }
37
+
38
+ /**
39
+ * Check if filesystem write is allowed
40
+ */
41
+ canWriteFile(path?: string): boolean {
42
+ const fs = this.permissions.fs;
43
+ if (!fs?.write) return false;
44
+
45
+ if (path) {
46
+ return this.isPathAllowed(path, fs);
47
+ }
48
+ return true;
49
+ }
50
+
51
+ /**
52
+ * Check if filesystem delete is allowed
53
+ */
54
+ canDeleteFile(path?: string): boolean {
55
+ const fs = this.permissions.fs;
56
+ if (!fs?.delete) return false;
57
+
58
+ if (path) {
59
+ return this.isPathAllowed(path, fs);
60
+ }
61
+ return true;
62
+ }
63
+
64
+ /**
65
+ * Check if network outbound is allowed
66
+ */
67
+ canConnect(host?: string): boolean {
68
+ const net = this.permissions.network;
69
+ if (!net?.outbound) return false;
70
+
71
+ if (host) {
72
+ return this.isHostAllowed(host, net);
73
+ }
74
+ return true;
75
+ }
76
+
77
+ /**
78
+ * Check if network inbound is allowed
79
+ */
80
+ canListen(port?: number): boolean {
81
+ const net = this.permissions.network;
82
+ if (!net?.inbound) return false;
83
+
84
+ // Could add port-based restrictions here
85
+ return true;
86
+ }
87
+
88
+ /**
89
+ * Check if environment read is allowed
90
+ */
91
+ canReadEnv(key?: string): boolean {
92
+ const env = this.permissions.env;
93
+ if (!env?.read) return false;
94
+
95
+ if (key && env.allowedKeys) {
96
+ return env.allowedKeys.includes(key);
97
+ }
98
+ return true;
99
+ }
100
+
101
+ /**
102
+ * Check if environment write is allowed
103
+ */
104
+ canWriteEnv(key?: string): boolean {
105
+ const env = this.permissions.env;
106
+ if (!env?.write) return false;
107
+
108
+ if (key && env.allowedKeys) {
109
+ return env.allowedKeys.includes(key);
110
+ }
111
+ return true;
112
+ }
113
+
114
+ /**
115
+ * Check if process spawn is allowed
116
+ */
117
+ canSpawnProcess(command?: string): boolean {
118
+ const proc = this.permissions.process;
119
+ if (!proc?.spawn) return false;
120
+
121
+ if (command && proc.allowedCommands) {
122
+ return proc.allowedCommands.some(cmd =>
123
+ command.startsWith(cmd) || command === cmd
124
+ );
125
+ }
126
+ return true;
127
+ }
128
+
129
+ /**
130
+ * Get memory limit
131
+ */
132
+ getMemoryLimit(): number | undefined {
133
+ return this.permissions.memory?.maxBytes;
134
+ }
135
+
136
+ /**
137
+ * Get CPU limits
138
+ */
139
+ getCpuLimits(): { maxPercent?: number; maxTimeMs?: number } {
140
+ const cpu = this.permissions.cpu;
141
+ return {
142
+ maxPercent: cpu?.maxPercent,
143
+ maxTimeMs: cpu?.maxTimeMs,
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Check if path is allowed
149
+ */
150
+ private isPathAllowed(path: string, fs: FileSystemPermission): boolean {
151
+ // Check denied paths first
152
+ if (fs.deniedPaths) {
153
+ for (const denied of fs.deniedPaths) {
154
+ if (path.startsWith(denied)) {
155
+ return false;
156
+ }
157
+ }
158
+ }
159
+
160
+ // Check allowed paths
161
+ if (fs.allowedPaths) {
162
+ for (const allowed of fs.allowedPaths) {
163
+ if (path.startsWith(allowed)) {
164
+ return true;
165
+ }
166
+ }
167
+ return false;
168
+ }
169
+
170
+ return true;
171
+ }
172
+
173
+ /**
174
+ * Check if host is allowed
175
+ */
176
+ private isHostAllowed(host: string, net: NetworkPermission): boolean {
177
+ // Check denied hosts first
178
+ if (net.deniedHosts) {
179
+ for (const denied of net.deniedHosts) {
180
+ if (host === denied || host.endsWith(`.${denied}`)) {
181
+ return false;
182
+ }
183
+ }
184
+ }
185
+
186
+ // Check allowed hosts
187
+ if (net.allowedHosts) {
188
+ for (const allowed of net.allowedHosts) {
189
+ if (host === allowed || host.endsWith(`.${allowed}`)) {
190
+ return true;
191
+ }
192
+ }
193
+ return false;
194
+ }
195
+
196
+ return true;
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Permission Builder
202
+ *
203
+ * Fluent API for building permission sets.
204
+ */
205
+ export class PermissionBuilder {
206
+ private permissions: Permissions = {};
207
+
208
+ filesystem(options: Partial<FileSystemPermission>): this {
209
+ this.permissions.fs = {
210
+ ...this.permissions.fs,
211
+ ...options,
212
+ };
213
+ return this;
214
+ }
215
+
216
+ network(options: Partial<NetworkPermission>): this {
217
+ this.permissions.network = {
218
+ ...this.permissions.network,
219
+ ...options,
220
+ };
221
+ return this;
222
+ }
223
+
224
+ environment(options: Partial<EnvironmentPermission>): this {
225
+ this.permissions.env = {
226
+ ...this.permissions.env,
227
+ ...options,
228
+ };
229
+ return this;
230
+ }
231
+
232
+ process(options: Partial<ProcessPermission>): this {
233
+ this.permissions.process = {
234
+ ...this.permissions.process,
235
+ ...options,
236
+ };
237
+ return this;
238
+ }
239
+
240
+ memory(maxBytes: number): this {
241
+ this.permissions.memory = { maxBytes };
242
+ return this;
243
+ }
244
+
245
+ cpu(options: { maxPercent?: number; maxTimeMs?: number }): this {
246
+ this.permissions.cpu = options;
247
+ return this;
248
+ }
249
+
250
+ build(): Permissions {
251
+ return this.permissions;
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Create permission set from level
257
+ */
258
+ export function createPermissions(level: PermissionLevel): Permissions {
259
+ switch (level) {
260
+ case "isolated":
261
+ return {};
262
+
263
+ case "readonly":
264
+ return {
265
+ fs: { read: true },
266
+ };
267
+
268
+ case "network":
269
+ return {
270
+ fs: { read: true },
271
+ network: { outbound: true },
272
+ };
273
+
274
+ case "filesystem":
275
+ return {
276
+ fs: { read: true, write: true, delete: true },
277
+ network: { outbound: true, inbound: true },
278
+ };
279
+
280
+ case "admin":
281
+ return {
282
+ fs: { read: true, write: true, delete: true },
283
+ network: { outbound: true, inbound: true },
284
+ env: { read: true, write: true },
285
+ process: { spawn: true },
286
+ };
287
+
288
+ case "sudo":
289
+ return {
290
+ fs: { read: true, write: true, delete: true },
291
+ network: { outbound: true, inbound: true },
292
+ env: { read: true, write: true },
293
+ process: { spawn: true },
294
+ };
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Merge permissions (intersection - most restrictive)
300
+ */
301
+ export function mergePermissions(...perms: Permissions[]): Permissions {
302
+ const result: Permissions = {};
303
+
304
+ // Filesystem
305
+ const fsPerms = perms.filter(p => p.fs);
306
+ if (fsPerms.length === perms.length) {
307
+ result.fs = {
308
+ read: fsPerms.every(p => p.fs?.read),
309
+ write: fsPerms.every(p => p.fs?.write),
310
+ delete: fsPerms.every(p => p.fs?.delete),
311
+ allowedPaths: intersectArrays(
312
+ ...fsPerms.map(p => p.fs?.allowedPaths).filter(Boolean) as string[][]
313
+ ),
314
+ deniedPaths: unionArrays(
315
+ ...fsPerms.map(p => p.fs?.deniedPaths).filter(Boolean) as string[][]
316
+ ),
317
+ };
318
+ }
319
+
320
+ // Network
321
+ const netPerms = perms.filter(p => p.network);
322
+ if (netPerms.length === perms.length) {
323
+ result.network = {
324
+ outbound: netPerms.every(p => p.network?.outbound),
325
+ inbound: netPerms.every(p => p.network?.inbound),
326
+ allowedHosts: intersectArrays(
327
+ ...netPerms.map(p => p.network?.allowedHosts).filter(Boolean) as string[][]
328
+ ),
329
+ deniedHosts: unionArrays(
330
+ ...netPerms.map(p => p.network?.deniedHosts).filter(Boolean) as string[][]
331
+ ),
332
+ };
333
+ }
334
+
335
+ // Memory (take minimum)
336
+ const memoryLimits = perms
337
+ .map(p => p.memory?.maxBytes)
338
+ .filter(Boolean) as number[];
339
+ if (memoryLimits.length > 0) {
340
+ result.memory = { maxBytes: Math.min(...memoryLimits) };
341
+ }
342
+
343
+ // CPU (take minimums)
344
+ const cpuPerms = perms.filter(p => p.cpu);
345
+ if (cpuPerms.length > 0) {
346
+ result.cpu = {
347
+ maxPercent: Math.min(
348
+ ...cpuPerms.map(p => p.cpu?.maxPercent ?? 100)
349
+ ),
350
+ maxTimeMs: Math.min(
351
+ ...cpuPerms.map(p => p.cpu?.maxTimeMs ?? Infinity)
352
+ ),
353
+ };
354
+ }
355
+
356
+ return result;
357
+ }
358
+
359
+ /**
360
+ * Check if permission level implies another
361
+ */
362
+ export function impliesPermission(
363
+ level: PermissionLevel,
364
+ required: PermissionLevel
365
+ ): boolean {
366
+ const levels: PermissionLevel[] = [
367
+ "isolated",
368
+ "readonly",
369
+ "network",
370
+ "filesystem",
371
+ "admin",
372
+ "sudo",
373
+ ];
374
+ return levels.indexOf(level) >= levels.indexOf(required);
375
+ }
376
+
377
+ /**
378
+ * Upgrade permissions to higher level
379
+ */
380
+ export function upgradePermissions(
381
+ current: Permissions,
382
+ newLevel: PermissionLevel
383
+ ): Permissions {
384
+ const newPerms = createPermissions(newLevel);
385
+ return mergePermissions(current, newPerms);
386
+ }
387
+
388
+ // Helper functions
389
+ function intersectArrays<T>(...arrays: T[][]): T[] {
390
+ if (arrays.length === 0) return [];
391
+ if (arrays.length === 1) return arrays[0];
392
+
393
+ return arrays[0].filter(item =>
394
+ arrays.every(arr => arr.includes(item))
395
+ );
396
+ }
397
+
398
+ function unionArrays<T>(...arrays: T[][]): T[] {
399
+ const set = new Set<T>();
400
+ for (const arr of arrays) {
401
+ for (const item of arr) {
402
+ set.add(item);
403
+ }
404
+ }
405
+ return Array.from(set);
406
+ }
407
+
408
+ // Re-export from types
409
+ export { permissionsFromLevel } from "./types.js";