@wbce-d9/api 10.6.0 → 11.0.0

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/dist/env.js CHANGED
@@ -191,8 +191,9 @@ const allowedEnvironmentVars = [
191
191
  'RELATIONAL_BATCH_SIZE',
192
192
  'EXPORT_BATCH_SIZE',
193
193
  // flows
194
- 'FLOWS_EXEC_ALLOWED_MODULES',
195
194
  'FLOWS_ENV_ALLOW_LIST',
195
+ 'FLOWS_SCRIPT_MAX_MEMORY',
196
+ 'FLOWS_SCRIPT_EXEC_TIMEOUT',
196
197
  ].map((name) => new RegExp(`^${name}$`));
197
198
  const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
198
199
  const defaults = {
@@ -270,8 +271,9 @@ const defaults = {
270
271
  EXPORT_BATCH_SIZE: 5000,
271
272
  FILE_METADATA_ALLOW_LIST: 'ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO',
272
273
  GRAPHQL_INTROSPECTION: true,
273
- FLOWS_EXEC_ALLOWED_MODULES: false,
274
274
  FLOWS_ENV_ALLOW_LIST: false,
275
+ FLOWS_SCRIPT_MAX_MEMORY: 32,
276
+ FLOWS_SCRIPT_EXEC_TIMEOUT: 10000,
275
277
  };
276
278
  // Allows us to force certain environment variable into a type, instead of relying
277
279
  // on the auto-parsed type in processValues. ref #3705
@@ -1,5 +1,5 @@
1
- type Options = {
1
+ type ExecutionOptions = {
2
2
  code: string;
3
3
  };
4
- declare const _default: import("@wbce-d9/types").OperationApiConfig<Options>;
4
+ declare const _default: import("@wbce-d9/types").OperationApiConfig<ExecutionOptions>;
5
5
  export default _default;
@@ -1,38 +1,44 @@
1
- import { defineOperationApi, toArray } from '@wbce-d9/utils';
2
- import { isBuiltin } from 'node:module';
3
- import { NodeVM, VMScript } from 'vm2';
1
+ import { defineOperationApi } from '@wbce-d9/utils';
2
+ import { createRequire } from 'node:module';
3
+ const require = createRequire(import.meta.url);
4
+ const ivm = require('isolated-vm');
4
5
  export default defineOperationApi({
5
- id: 'exec',
6
- handler: async ({ code }, { data, env }) => {
7
- const allowedModules = env['FLOWS_EXEC_ALLOWED_MODULES'] ? toArray(env['FLOWS_EXEC_ALLOWED_MODULES']) : [];
8
- const allowedModulesBuiltIn = [];
9
- const allowedModulesExternal = [];
10
- const allowedEnv = data['$env'] ?? {};
11
- const opts = {
12
- eval: false,
13
- wasm: false,
14
- env: allowedEnv,
15
- };
16
- for (const module of allowedModules) {
17
- if (isBuiltin(module)) {
18
- allowedModulesBuiltIn.push(module);
19
- }
20
- else {
21
- allowedModulesExternal.push(module);
22
- }
23
- }
24
- if (allowedModules.length > 0) {
25
- opts.require = {
26
- builtin: allowedModulesBuiltIn,
27
- external: {
28
- modules: allowedModulesExternal,
29
- transitive: false,
30
- },
31
- };
32
- }
33
- const vm = new NodeVM(opts);
34
- const script = new VMScript(code).compile();
35
- const fn = await vm.run(script);
36
- return await fn(data);
6
+ id: 'execute_script',
7
+ handler: async ({ code }, { data, env, logger }) => {
8
+ // Fetching execution limits and environment variables
9
+ const memoryLimit = env['FLOWS_SCRIPT_MAX_MEMORY'];
10
+ const executionTimeout = env['FLOWS_SCRIPT_EXEC_TIMEOUT'];
11
+ const environmentVariables = data['$env'] ?? {};
12
+ // Creating an isolate with the specified memory limit
13
+ const isolate = new ivm.Isolate({ memoryLimit });
14
+ const context = isolate.createContextSync();
15
+ const jail = context.global;
16
+ jail.setSync('global', jail.derefInto());
17
+ jail.setSync('process', { env: environmentVariables }, { copy: true });
18
+ jail.setSync('module', { exports: null }, { copy: true });
19
+ // Adding a secure logger for the isolate
20
+ const createLoggerCallback = (logFn) => new ivm.Callback((...args) => logFn(args.length === 1 ? args[0] : args), { sync: true });
21
+ jail.setSync('console', {
22
+ log: createLoggerCallback(() => logger.info),
23
+ info: createLoggerCallback(() => logger.info),
24
+ warn: createLoggerCallback(() => logger.warn),
25
+ error: createLoggerCallback(() => logger.error),
26
+ debug: createLoggerCallback(() => logger.debug),
27
+ trace: createLoggerCallback(() => logger.trace),
28
+ }, { copy: true });
29
+ // Executing the code within the isolate
30
+ await context.eval(code, { timeout: executionTimeout });
31
+ const dataCopy = new ivm.ExternalCopy({ data });
32
+ const resultReference = await context.evalClosure(`return module.exports($0.data)`, [dataCopy.copyInto()], {
33
+ result: { reference: true, promise: true },
34
+ timeout: executionTimeout,
35
+ });
36
+ const finalResult = await resultReference.copy();
37
+ // Cleanup memory
38
+ resultReference.release();
39
+ dataCopy.release();
40
+ context.release();
41
+ isolate.dispose();
42
+ return finalResult;
37
43
  },
38
44
  });
@@ -1526,16 +1526,6 @@ export class GraphQLService {
1526
1526
  }),
1527
1527
  }
1528
1528
  : GraphQLBoolean,
1529
- flows: {
1530
- type: new GraphQLObjectType({
1531
- name: 'server_info_flows',
1532
- fields: {
1533
- execAllowedModules: {
1534
- type: new GraphQLList(GraphQLString),
1535
- },
1536
- },
1537
- }),
1538
- },
1539
1529
  });
1540
1530
  }
1541
1531
  if (this.accountability?.admin === true) {
@@ -61,9 +61,6 @@ export class ServerService {
61
61
  else {
62
62
  info['rateLimitGlobal'] = false;
63
63
  }
64
- info['flows'] = {
65
- execAllowedModules: env['FLOWS_EXEC_ALLOWED_MODULES'] ? toArray(env['FLOWS_EXEC_ALLOWED_MODULES']) : [],
66
- };
67
64
  }
68
65
  if (this.accountability?.admin === true) {
69
66
  const { osType, osVersion } = getOSInfo();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wbce-d9/api",
3
- "version": "10.6.0",
3
+ "version": "11.0.0",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -101,6 +101,7 @@
101
101
  "icc": "3.0.0",
102
102
  "inquirer": "9.1.5",
103
103
  "ioredis": "5.3.2",
104
+ "isolated-vm": "^5.0.3",
104
105
  "joi": "17.9.1",
105
106
  "js-yaml": "4.1.0",
106
107
  "js2xmlparser": "5.0.0",
@@ -141,21 +142,20 @@
141
142
  "tsx": "3.12.6",
142
143
  "uuid": "9.0.0",
143
144
  "uuid-validate": "0.0.3",
144
- "vm2": "^3.9.19",
145
145
  "wellknown": "0.5.0",
146
- "@wbce-d9/app": "9.32.0",
146
+ "@wbce-d9/app": "9.33.0",
147
147
  "@wbce-d9/constants": "9.28.0",
148
148
  "@wbce-d9/extensions-sdk": "9.27.1",
149
149
  "@wbce-d9/exceptions": "9.26.4",
150
- "@wbce-d9/schema": "9.26.4",
151
- "@wbce-d9/storage-driver-azure": "9.26.8",
152
150
  "@wbce-d9/specs": "9.26.4",
151
+ "@wbce-d9/storage-driver-azure": "9.26.8",
153
152
  "@wbce-d9/storage": "9.26.4",
154
153
  "@wbce-d9/storage-driver-cloudinary": "9.27.0",
155
- "@wbce-d9/storage-driver-local": "9.26.8",
154
+ "@wbce-d9/schema": "9.26.4",
156
155
  "@wbce-d9/storage-driver-s3": "9.26.10",
157
- "@wbce-d9/storage-driver-gcs": "9.26.8",
158
156
  "@wbce-d9/utils": "9.29.1",
157
+ "@wbce-d9/storage-driver-local": "9.26.8",
158
+ "@wbce-d9/storage-driver-gcs": "9.26.8",
159
159
  "@wbce-d9/update-check": "9.28.0"
160
160
  },
161
161
  "devDependencies": {