diff --git a/packages/string-templates/src/helpers/javascript.js b/packages/string-templates/src/helpers/javascript.js index 4a7f602690..7827736812 100644 --- a/packages/string-templates/src/helpers/javascript.js +++ b/packages/string-templates/src/helpers/javascript.js @@ -1,4 +1,4 @@ -const { atob } = require("../utilities") +const { atob, isBackendService, isJSAllowed } = require("../utilities") const cloneDeep = require("lodash.clonedeep") const { LITERAL_MARKER } = require("../helpers/constants") const { getJsHelperList } = require("./list") @@ -7,6 +7,9 @@ const { getJsHelperList } = require("./list") // This setter is used in the entrypoint (either index.js or index.mjs). let runJS module.exports.setJSRunner = runner => (runJS = runner) +module.exports.removeJSRunner = () => { + runJS = undefined +} let onErrorLog module.exports.setOnErrorLog = delegate => (onErrorLog = delegate) @@ -39,7 +42,7 @@ const getContextValue = (path, context) => { // Evaluates JS code against a certain context module.exports.processJS = (handlebars, context) => { - if (process && process.env.NO_JS) { + if (!isJSAllowed() || (isBackendService() && !runJS)) { throw new Error("JS disabled in environment.") } try { diff --git a/packages/string-templates/src/index.js b/packages/string-templates/src/index.js index bcd63d2e6f..2dc360bd96 100644 --- a/packages/string-templates/src/index.js +++ b/packages/string-templates/src/index.js @@ -2,7 +2,7 @@ const vm = require("vm") const handlebars = require("handlebars") const { registerAll, registerMinimum } = require("./helpers/index") const processors = require("./processors") -const { atob, btoa } = require("./utilities") +const { atob, btoa, isBackendService, isJSAllowed } = require("./utilities") const manifest = require("../manifest.json") const { FIND_HBS_REGEX, @@ -404,18 +404,25 @@ module.exports.JsErrorTimeout = errors.JsErrorTimeout module.exports.helpersToRemoveForJs = helpersToRemoveForJs -if (process && !process.env.NO_JS) { - /** - * Use polyfilled vm to run JS scripts in a browser Env - */ - javascript.setJSRunner((js, context) => { - context = { - ...context, - alert: undefined, - setInterval: undefined, - setTimeout: undefined, - } - vm.createContext(context) - return vm.runInNewContext(js, context, { timeout: 1000 }) - }) +function defaultJSSetup() { + if (!isBackendService()) { + /** + * Use polyfilled vm to run JS scripts in a browser Env + */ + javascript.setJSRunner((js, context) => { + context = { + ...context, + alert: undefined, + setInterval: undefined, + setTimeout: undefined, + } + vm.createContext(context) + return vm.runInNewContext(js, context, { timeout: 1000 }) + }) + } else { + javascript.removeJSRunner() + } } +defaultJSSetup() + +module.exports.defaultJSSetup = defaultJSSetup diff --git a/packages/string-templates/src/utilities.js b/packages/string-templates/src/utilities.js index 775c150e1b..00b2d7d855 100644 --- a/packages/string-templates/src/utilities.js +++ b/packages/string-templates/src/utilities.js @@ -4,6 +4,14 @@ module.exports.FIND_HBS_REGEX = /{{([^{].*?)}}/g module.exports.FIND_ANY_HBS_REGEX = /{?{{([^{].*?)}}}?/g module.exports.FIND_TRIPLE_HBS_REGEX = /{{{([^{].*?)}}}/g +module.exports.isBackendService = () => { + return typeof window === "undefined" +} + +module.exports.isJSAllowed = () => { + return process && !process.env.NO_JS +} + // originally this could be done with a single regex using look behinds // but safari does not support this feature // original regex: /(? { + const utilities = jest.requireActual("../src/utilities") + return { + ...utilities, + isBackendService: jest.fn().mockReturnValue(true), + } +}) +const { defaultJSSetup, processStringSync, encodeJSBinding } = require("../src") +const { isBackendService } = require("../src/utilities") +const mockedBackendService = jest.mocked(isBackendService) + +const binding = encodeJSBinding("return 1") +describe("confirm VM is available when expected and when not", () => { + it("shouldn't have JS available in a backend service by default", () => { + defaultJSSetup() + const result = processStringSync(binding, {}) + // shouldn't process at all + expect(result).toBe(binding) + }) + + it("should have JS available in frontend environments", () => { + mockedBackendService.mockReturnValue(false) + defaultJSSetup() + const result = processStringSync(binding, {}) + expect(result).toBe(1) + }) +})