@squiz/render-runtime-lib 1.2.1-alpha.100

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 (56) hide show
  1. package/README.md +11 -0
  2. package/lib/component-runner/component-runner.spec.d.ts +1 -0
  3. package/lib/component-runner/index.d.ts +22 -0
  4. package/lib/component-runner/worker/WorkerPool.d.ts +50 -0
  5. package/lib/component-runner/worker/getRuntimeModules.d.ts +1 -0
  6. package/lib/component-runner/worker/worker-root.d.ts +1 -0
  7. package/lib/index.d.ts +25 -0
  8. package/lib/index.js +112791 -0
  9. package/lib/index.js.map +7 -0
  10. package/lib/migrations/20220704054051_initial.sql +19 -0
  11. package/lib/migrations/20220718172237_adding_component_sets.sql +23 -0
  12. package/lib/migrations/20220728113941_add_env_vars_field.sql +1 -0
  13. package/lib/migrations/20220817113300_removing_null_props_from_jsonb.sql +41 -0
  14. package/lib/render-runtime-lib.spec.d.ts +1 -0
  15. package/lib/test/helpers/fixtures.d.ts +20 -0
  16. package/lib/test/helpers/stack.d.ts +6 -0
  17. package/lib/test/index.d.ts +2 -0
  18. package/lib/utils/convertFunctionStaticFilesToFqdn.d.ts +1 -0
  19. package/lib/utils/convertFunctionStaticFilesToFqdn.spec.d.ts +1 -0
  20. package/lib/utils/getFunctionDefinitionFromManifest.d.ts +2 -0
  21. package/lib/utils/getManifestPath.d.ts +1 -0
  22. package/lib/utils/getManifestPath.spec.d.ts +1 -0
  23. package/lib/utils/getPreviewFilePath.d.ts +1 -0
  24. package/lib/utils/getPreviewFilePath.spec.d.ts +1 -0
  25. package/lib/utils/isInProductionMode.d.ts +1 -0
  26. package/lib/utils/isInProductionMode.spec.d.ts +1 -0
  27. package/lib/utils/resolvePreviewOutput.d.ts +2 -0
  28. package/lib/utils/resolvePreviewOutput.spec.d.ts +1 -0
  29. package/lib/webserver/app.d.ts +4 -0
  30. package/lib/webserver/controllers/core.d.ts +3 -0
  31. package/lib/webserver/controllers/core.spec.d.ts +1 -0
  32. package/lib/webserver/controllers/definition.d.ts +3 -0
  33. package/lib/webserver/controllers/definition.spec.d.ts +1 -0
  34. package/lib/webserver/controllers/index.d.ts +4 -0
  35. package/lib/webserver/controllers/render.d.ts +3 -0
  36. package/lib/webserver/controllers/render.spec.d.ts +1 -0
  37. package/lib/webserver/controllers/static.d.ts +3 -0
  38. package/lib/webserver/controllers/static.spec.d.ts +1 -0
  39. package/lib/webserver/controllers/test/definition-route-tests.d.ts +1 -0
  40. package/lib/webserver/controllers/test/render-route-tests.d.ts +1 -0
  41. package/lib/webserver/controllers/test/static-route-tests.d.ts +1 -0
  42. package/lib/webserver/index.d.ts +22 -0
  43. package/lib/worker/bridge.js +1010 -0
  44. package/lib/worker/compiler.js +87 -0
  45. package/lib/worker/events.js +977 -0
  46. package/lib/worker/nodevm.js +503 -0
  47. package/lib/worker/resolver-compat.js +342 -0
  48. package/lib/worker/resolver.js +882 -0
  49. package/lib/worker/script.js +388 -0
  50. package/lib/worker/setup-node-sandbox.js +469 -0
  51. package/lib/worker/setup-sandbox.js +456 -0
  52. package/lib/worker/transformer.js +180 -0
  53. package/lib/worker/vm.js +539 -0
  54. package/lib/worker/worker-root.js +41743 -0
  55. package/lib/worker/worker-root.js.map +7 -0
  56. package/package.json +60 -0
@@ -0,0 +1,539 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * This callback will be called to transform a script to JavaScript.
5
+ *
6
+ * @callback compileCallback
7
+ * @param {string} code - Script code to transform to JavaScript.
8
+ * @param {string} filename - Filename of this script.
9
+ * @return {string} JavaScript code that represents the script code.
10
+ */
11
+
12
+ /**
13
+ * This callback will be called to resolve a module if it couldn't be found.
14
+ *
15
+ * @callback resolveCallback
16
+ * @param {string} moduleName - Name of the modulusedRequiree to resolve.
17
+ * @param {string} dirname - Name of the current directory.
18
+ * @return {(string|undefined)} The file or directory to use to load the requested module.
19
+ */
20
+
21
+ const fs = require('fs');
22
+ const pa = require('path');
23
+ const {
24
+ Script,
25
+ createContext
26
+ } = require('vm');
27
+ const {
28
+ EventEmitter
29
+ } = require('events');
30
+ const {
31
+ INSPECT_MAX_BYTES
32
+ } = require('buffer');
33
+ const {
34
+ createBridge,
35
+ VMError
36
+ } = require('./bridge');
37
+ const {
38
+ transformer,
39
+ INTERNAL_STATE_NAME
40
+ } = require('./transformer');
41
+ const {
42
+ lookupCompiler
43
+ } = require('./compiler');
44
+ const {
45
+ VMScript
46
+ } = require('./script');
47
+
48
+ const objectDefineProperties = Object.defineProperties;
49
+
50
+ /**
51
+ * Host objects
52
+ *
53
+ * @private
54
+ */
55
+ const HOST = Object.freeze({
56
+ Buffer,
57
+ Function,
58
+ Object,
59
+ transformAndCheck,
60
+ INSPECT_MAX_BYTES,
61
+ INTERNAL_STATE_NAME
62
+ });
63
+
64
+ /**
65
+ * Compile a script.
66
+ *
67
+ * @private
68
+ * @param {string} filename - Filename of the script.
69
+ * @param {string} script - Script.
70
+ * @return {vm.Script} The compiled script.
71
+ */
72
+ function compileScript(filename, script) {
73
+ return new Script(script, {
74
+ __proto__: null,
75
+ filename,
76
+ displayErrors: false
77
+ });
78
+ }
79
+
80
+ /**
81
+ * Default run options for vm.Script.runInContext
82
+ *
83
+ * @private
84
+ */
85
+ const DEFAULT_RUN_OPTIONS = Object.freeze({__proto__: null, displayErrors: false});
86
+
87
+ function checkAsync(allow) {
88
+ if (!allow) throw new VMError('Async not available');
89
+ }
90
+
91
+ function transformAndCheck(args, code, isAsync, isGenerator, allowAsync) {
92
+ const ret = transformer(args, code, isAsync, isGenerator, undefined);
93
+ checkAsync(allowAsync || !ret.hasAsync);
94
+ return ret.code;
95
+ }
96
+
97
+ /**
98
+ *
99
+ * This callback will be called and has a specific time to finish.<br>
100
+ * No parameters will be supplied.<br>
101
+ * If parameters are required, use a closure.
102
+ *
103
+ * @private
104
+ * @callback runWithTimeout
105
+ * @return {*}
106
+ *
107
+ */
108
+
109
+ let cacheTimeoutContext = null;
110
+ let cacheTimeoutScript = null;
111
+
112
+ /**
113
+ * Run a function with a specific timeout.
114
+ *
115
+ * @private
116
+ * @param {runWithTimeout} fn - Function to run with the specific timeout.
117
+ * @param {number} timeout - The amount of time to give the function to finish.
118
+ * @return {*} The value returned by the function.
119
+ * @throws {Error} If the function took to long.
120
+ */
121
+ function doWithTimeout(fn, timeout) {
122
+ if (!cacheTimeoutContext) {
123
+ cacheTimeoutContext = createContext();
124
+ cacheTimeoutScript = new Script('fn()', {
125
+ __proto__: null,
126
+ filename: 'timeout_bridge.js',
127
+ displayErrors: false
128
+ });
129
+ }
130
+ cacheTimeoutContext.fn = fn;
131
+ try {
132
+ return cacheTimeoutScript.runInContext(cacheTimeoutContext, {
133
+ __proto__: null,
134
+ displayErrors: false,
135
+ timeout
136
+ });
137
+ } finally {
138
+ cacheTimeoutContext.fn = null;
139
+ }
140
+ }
141
+
142
+ const bridgeScript = compileScript(`${__dirname}/bridge.js`,
143
+ `(function(global) {"use strict"; const exports = {};${fs.readFileSync(`${__dirname}/bridge.js`, 'utf8')}\nreturn exports;})`);
144
+ const setupSandboxScript = compileScript(`${__dirname}/setup-sandbox.js`,
145
+ `(function(global, host, bridge, data, context) { ${fs.readFileSync(`${__dirname}/setup-sandbox.js`, 'utf8')}\n})`);
146
+ const getGlobalScript = compileScript('get_global.js', 'this');
147
+
148
+ let getGeneratorFunctionScript = null;
149
+ let getAsyncFunctionScript = null;
150
+ let getAsyncGeneratorFunctionScript = null;
151
+ try {
152
+ getGeneratorFunctionScript = compileScript('get_generator_function.js', '(function*(){}).constructor');
153
+ } catch (ex) {}
154
+ try {
155
+ getAsyncFunctionScript = compileScript('get_async_function.js', '(async function(){}).constructor');
156
+ } catch (ex) {}
157
+ try {
158
+ getAsyncGeneratorFunctionScript = compileScript('get_async_generator_function.js', '(async function*(){}).constructor');
159
+ } catch (ex) {}
160
+
161
+ /**
162
+ * Class VM.
163
+ *
164
+ * @public
165
+ */
166
+ class VM extends EventEmitter {
167
+
168
+ /**
169
+ * The timeout for {@link VM#run} calls.
170
+ *
171
+ * @public
172
+ * @since v3.9.0
173
+ * @member {number} timeout
174
+ * @memberOf VM#
175
+ */
176
+
177
+ /**
178
+ * Get the global sandbox object.
179
+ *
180
+ * @public
181
+ * @readonly
182
+ * @since v3.9.0
183
+ * @member {Object} sandbox
184
+ * @memberOf VM#
185
+ */
186
+
187
+ /**
188
+ * The compiler to use to get the JavaScript code.
189
+ *
190
+ * @public
191
+ * @readonly
192
+ * @since v3.9.0
193
+ * @member {(string|compileCallback)} compiler
194
+ * @memberOf VM#
195
+ */
196
+
197
+ /**
198
+ * The resolved compiler to use to get the JavaScript code.
199
+ *
200
+ * @private
201
+ * @readonly
202
+ * @member {compileCallback} _compiler
203
+ * @memberOf VM#
204
+ */
205
+
206
+ /**
207
+ * Create a new VM instance.
208
+ *
209
+ * @public
210
+ * @param {Object} [options] - VM options.
211
+ * @param {number} [options.timeout] - The amount of time until a call to {@link VM#run} will timeout.
212
+ * @param {Object} [options.sandbox] - Objects that will be copied into the global object of the sandbox.
213
+ * @param {(string|compileCallback)} [options.compiler="javascript"] - The compiler to use.
214
+ * @param {boolean} [options.eval=true] - Allow the dynamic evaluation of code via eval(code) or Function(code)().<br>
215
+ * Only available for node v10+.
216
+ * @param {boolean} [options.wasm=true] - Allow to run wasm code.<br>
217
+ * Only available for node v10+.
218
+ * @param {boolean} [options.allowAsync=true] - Allows for async functions.
219
+ * @throws {VMError} If the compiler is unknown.
220
+ */
221
+ constructor(options = {}) {
222
+ super();
223
+
224
+ // Read all options
225
+ const {
226
+ timeout,
227
+ sandbox,
228
+ compiler = 'javascript',
229
+ allowAsync: optAllowAsync = true
230
+ } = options;
231
+ const allowEval = options.eval !== false;
232
+ const allowWasm = options.wasm !== false;
233
+ const allowAsync = optAllowAsync && !options.fixAsync;
234
+
235
+ // Early error if sandbox is not an object.
236
+ if (sandbox && 'object' !== typeof sandbox) {
237
+ throw new VMError('Sandbox must be object.');
238
+ }
239
+
240
+ // Early error if compiler can't be found.
241
+ const resolvedCompiler = lookupCompiler(compiler);
242
+
243
+ // Create a new context for this vm.
244
+ const _context = createContext(undefined, {
245
+ __proto__: null,
246
+ codeGeneration: {
247
+ __proto__: null,
248
+ strings: allowEval,
249
+ wasm: allowWasm
250
+ }
251
+ });
252
+
253
+ const sandboxGlobal = getGlobalScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
254
+
255
+ // Initialize the sandbox bridge
256
+ const {
257
+ createBridge: sandboxCreateBridge
258
+ } = bridgeScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal);
259
+
260
+ // Initialize the bridge
261
+ const bridge = createBridge(sandboxCreateBridge, () => {});
262
+
263
+ const data = {
264
+ __proto__: null,
265
+ allowAsync
266
+ };
267
+
268
+ if (getGeneratorFunctionScript) {
269
+ data.GeneratorFunction = getGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
270
+ }
271
+ if (getAsyncFunctionScript) {
272
+ data.AsyncFunction = getAsyncFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
273
+ }
274
+ if (getAsyncGeneratorFunctionScript) {
275
+ data.AsyncGeneratorFunction = getAsyncGeneratorFunctionScript.runInContext(_context, DEFAULT_RUN_OPTIONS);
276
+ }
277
+
278
+ // Create the bridge between the host and the sandbox.
279
+ const internal = setupSandboxScript.runInContext(_context, DEFAULT_RUN_OPTIONS)(sandboxGlobal, HOST, bridge.other, data, _context);
280
+
281
+ const runScript = (script) => {
282
+ // This closure is intentional to hide _context and bridge since the allow to access the sandbox directly which is unsafe.
283
+ let ret;
284
+ try {
285
+ ret = script.runInContext(_context, DEFAULT_RUN_OPTIONS);
286
+ } catch (e) {
287
+ throw bridge.from(e);
288
+ }
289
+ return bridge.from(ret);
290
+ };
291
+
292
+ const makeReadonly = (value, mock) => {
293
+ try {
294
+ internal.readonly(value, mock);
295
+ } catch (e) {
296
+ throw bridge.from(e);
297
+ }
298
+ return value;
299
+ };
300
+
301
+ const makeProtected = (value) => {
302
+ const sandboxBridge = bridge.other;
303
+ try {
304
+ sandboxBridge.fromWithFactory(sandboxBridge.protectedFactory, value);
305
+ } catch (e) {
306
+ throw bridge.from(e);
307
+ }
308
+ return value;
309
+ };
310
+
311
+ const addProtoMapping = (hostProto, sandboxProto) => {
312
+ const sandboxBridge = bridge.other;
313
+ let otherProto;
314
+ try {
315
+ otherProto = sandboxBridge.from(sandboxProto);
316
+ sandboxBridge.addProtoMapping(otherProto, hostProto);
317
+ } catch (e) {
318
+ throw bridge.from(e);
319
+ }
320
+ bridge.addProtoMapping(hostProto, otherProto);
321
+ };
322
+
323
+ const addProtoMappingFactory = (hostProto, sandboxProtoFactory) => {
324
+ const sandboxBridge = bridge.other;
325
+ const factory = () => {
326
+ const proto = sandboxProtoFactory(this);
327
+ bridge.addProtoMapping(hostProto, proto);
328
+ return proto;
329
+ };
330
+ try {
331
+ const otherProtoFactory = sandboxBridge.from(factory);
332
+ sandboxBridge.addProtoMappingFactory(otherProtoFactory, hostProto);
333
+ } catch (e) {
334
+ throw bridge.from(e);
335
+ }
336
+ };
337
+
338
+ // Define the properties of this object.
339
+ // Use Object.defineProperties here to be able to
340
+ // hide and set properties read-only.
341
+ objectDefineProperties(this, {
342
+ __proto__: null,
343
+ timeout: {
344
+ __proto__: null,
345
+ value: timeout,
346
+ writable: true,
347
+ enumerable: true
348
+ },
349
+ compiler: {
350
+ __proto__: null,
351
+ value: compiler,
352
+ enumerable: true
353
+ },
354
+ sandbox: {
355
+ __proto__: null,
356
+ value: bridge.from(sandboxGlobal),
357
+ enumerable: true
358
+ },
359
+ _runScript: {__proto__: null, value: runScript},
360
+ _makeReadonly: {__proto__: null, value: makeReadonly},
361
+ _makeProtected: {__proto__: null, value: makeProtected},
362
+ _addProtoMapping: {__proto__: null, value: addProtoMapping},
363
+ _addProtoMappingFactory: {__proto__: null, value: addProtoMappingFactory},
364
+ _compiler: {__proto__: null, value: resolvedCompiler},
365
+ _allowAsync: {__proto__: null, value: allowAsync}
366
+ });
367
+
368
+ // prepare global sandbox
369
+ if (sandbox) {
370
+ this.setGlobals(sandbox);
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Adds all the values to the globals.
376
+ *
377
+ * @public
378
+ * @since v3.9.0
379
+ * @param {Object} values - All values that will be added to the globals.
380
+ * @return {this} This for chaining.
381
+ * @throws {*} If the setter of a global throws an exception it is propagated. And the remaining globals will not be written.
382
+ */
383
+ setGlobals(values) {
384
+ for (const name in values) {
385
+ if (Object.prototype.hasOwnProperty.call(values, name)) {
386
+ this.sandbox[name] = values[name];
387
+ }
388
+ }
389
+ return this;
390
+ }
391
+
392
+ /**
393
+ * Set a global value.
394
+ *
395
+ * @public
396
+ * @since v3.9.0
397
+ * @param {string} name - The name of the global.
398
+ * @param {*} value - The value of the global.
399
+ * @return {this} This for chaining.
400
+ * @throws {*} If the setter of the global throws an exception it is propagated.
401
+ */
402
+ setGlobal(name, value) {
403
+ this.sandbox[name] = value;
404
+ return this;
405
+ }
406
+
407
+ /**
408
+ * Get a global value.
409
+ *
410
+ * @public
411
+ * @since v3.9.0
412
+ * @param {string} name - The name of the global.
413
+ * @return {*} The value of the global.
414
+ * @throws {*} If the getter of the global throws an exception it is propagated.
415
+ */
416
+ getGlobal(name) {
417
+ return this.sandbox[name];
418
+ }
419
+
420
+ /**
421
+ * Freezes the object inside VM making it read-only. Not available for primitive values.
422
+ *
423
+ * @public
424
+ * @param {*} value - Object to freeze.
425
+ * @param {string} [globalName] - Whether to add the object to global.
426
+ * @return {*} Object to freeze.
427
+ * @throws {*} If the setter of the global throws an exception it is propagated.
428
+ */
429
+ freeze(value, globalName) {
430
+ this.readonly(value);
431
+ if (globalName) this.sandbox[globalName] = value;
432
+ return value;
433
+ }
434
+
435
+ /**
436
+ * Freezes the object inside VM making it read-only. Not available for primitive values.
437
+ *
438
+ * @public
439
+ * @param {*} value - Object to freeze.
440
+ * @param {*} [mock] - When the object does not have a property the mock is used before prototype lookup.
441
+ * @return {*} Object to freeze.
442
+ */
443
+ readonly(value, mock) {
444
+ return this._makeReadonly(value, mock);
445
+ }
446
+
447
+ /**
448
+ * Protects the object inside VM making impossible to set functions as it's properties. Not available for primitive values.
449
+ *
450
+ * @public
451
+ * @param {*} value - Object to protect.
452
+ * @param {string} [globalName] - Whether to add the object to global.
453
+ * @return {*} Object to protect.
454
+ * @throws {*} If the setter of the global throws an exception it is propagated.
455
+ */
456
+ protect(value, globalName) {
457
+ this._makeProtected(value);
458
+ if (globalName) this.sandbox[globalName] = value;
459
+ return value;
460
+ }
461
+
462
+ /**
463
+ * Run the code in VM.
464
+ *
465
+ * @public
466
+ * @param {(string|VMScript)} code - Code to run.
467
+ * @param {(string|Object)} [options] - Options map or filename.
468
+ * @param {string} [options.filename="vm.js"] - Filename that shows up in any stack traces produced from this script.<br>
469
+ * This is only used if code is a String.
470
+ * @return {*} Result of executed code.
471
+ * @throws {SyntaxError} If there is a syntax error in the script.
472
+ * @throws {Error} An error is thrown when the script took to long and there is a timeout.
473
+ * @throws {*} If the script execution terminated with an exception it is propagated.
474
+ */
475
+ run(code, options) {
476
+ let script;
477
+ let filename;
478
+
479
+ if (typeof options === 'object') {
480
+ filename = options.filename;
481
+ } else {
482
+ filename = options;
483
+ }
484
+
485
+ if (code instanceof VMScript) {
486
+ script = code._compileVM();
487
+ checkAsync(this._allowAsync || !code._hasAsync);
488
+ } else {
489
+ const useFileName = filename || 'vm.js';
490
+ let scriptCode = this._compiler(code, useFileName);
491
+ const ret = transformer(null, scriptCode, false, false, useFileName);
492
+ scriptCode = ret.code;
493
+ checkAsync(this._allowAsync || !ret.hasAsync);
494
+ // Compile the script here so that we don't need to create a instance of VMScript.
495
+ script = new Script(scriptCode, {
496
+ __proto__: null,
497
+ filename: useFileName,
498
+ displayErrors: false
499
+ });
500
+ }
501
+
502
+ if (!this.timeout) {
503
+ return this._runScript(script);
504
+ }
505
+
506
+ return doWithTimeout(() => {
507
+ return this._runScript(script);
508
+ }, this.timeout);
509
+ }
510
+
511
+ /**
512
+ * Run the code in VM.
513
+ *
514
+ * @public
515
+ * @since v3.9.0
516
+ * @param {string} filename - Filename of file to load and execute in a NodeVM.
517
+ * @return {*} Result of executed code.
518
+ * @throws {Error} If filename is not a valid filename.
519
+ * @throws {SyntaxError} If there is a syntax error in the script.
520
+ * @throws {Error} An error is thrown when the script took to long and there is a timeout.
521
+ * @throws {*} If the script execution terminated with an exception it is propagated.
522
+ */
523
+ runFile(filename) {
524
+ const resolvedFilename = pa.resolve(filename);
525
+
526
+ if (!fs.existsSync(resolvedFilename)) {
527
+ throw new VMError(`Script '${filename}' not found.`);
528
+ }
529
+
530
+ if (fs.statSync(resolvedFilename).isDirectory()) {
531
+ throw new VMError('Script must be file, got directory.');
532
+ }
533
+
534
+ return this.run(fs.readFileSync(resolvedFilename, 'utf8'), resolvedFilename);
535
+ }
536
+
537
+ }
538
+
539
+ exports.VM = VM;