UNPKG

jscrambler

Version:

Jscrambler Code Integrity API client.

1,289 lines (1,285 loc) 47.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; require("core-js/modules/es.promise.finally.js"); require("core-js/modules/web.dom-collections.iterator.js"); var _path = _interopRequireDefault(require("path")); var _axios = _interopRequireDefault(require("axios")); var _lodash = _interopRequireDefault(require("lodash.defaults")); var _fs = _interopRequireDefault(require("fs")); var _config2 = _interopRequireDefault(require("./config")); var _generateSignedParams = _interopRequireDefault(require("./generate-signed-params")); var _client = _interopRequireDefault(require("./client")); var mutations = _interopRequireWildcard(require("./mutations")); var queries = _interopRequireWildcard(require("./queries")); var _constants = require("./constants"); var _zip = require("./zip"); var introspection = _interopRequireWildcard(require("./introspection")); var _utils = require("./utils"); var _getProtectionDefaultFragments = _interopRequireWildcard(require("./get-protection-default-fragments")); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } /* eslint-disable no-console */ const { intoObjectType } = introspection; const debug = !!process.env.DEBUG; const APP_URL = 'https://app.jscrambler.com'; const POLLING_MIN_INTERVAL = 1000; const POLLING_MAX_INTERVAL = 10000; const INCREASE_POLL_INTERVAL_EVERY = 30000; const MAX_PRINTED_ERRORS = 3; /** * Calculate polling interval for protection and instrumentation. * Upper limit of {POLLING_MAX_INTERVAL}. * @param start * @returns {number|number} */ function getPollingInterval(start) { const pollingInterval = POLLING_MIN_INTERVAL * Math.ceil((Date.now() - start) / INCREASE_POLL_INTERVAL_EVERY); return pollingInterval >= POLLING_MAX_INTERVAL ? POLLING_MAX_INTERVAL : pollingInterval; } function errorHandler(res) { if (res.errors && res.errors.length) { res.errors.forEach(error => { throw new Error("Error: ".concat(error.message)); }); } if (res.data && res.data.errors) { res.data.errors.forEach(e => console.error(e.message)); throw new Error('GraphQL Query Error'); } if (res.message) { throw new Error("Error: ".concat(res.message)); } return res; } function printSourcesErrors(errors) { console.error('Source errors:'); for (let i = 0; i < Math.min(MAX_PRINTED_ERRORS, errors.length); i++) { let sourceErrorsMessage = errors[i].line ? ':' + errors[i].line + ':' + errors[i].column + ': ' : ': '; console.error('- ' + errors[i].filename + sourceErrorsMessage + errors[i].message); } const errorsLeft = errors.length - MAX_PRINTED_ERRORS; if (errorsLeft > 0) { if (errorsLeft === 1) { console.error('There is 1 more error.'); } else { console.error('There are ' + errorsLeft + ' more errors.'); } } console.error(); } function normalizeFileSizeFilter(parameters, fileSizeOption) { const validateFileSizeThreshold = (0, _utils.validateThresholdFn)(fileSizeOption); parameters.filter(parameter => typeof parameter[fileSizeOption] === 'string').forEach(parameter => { // change from "1kb" to 1024 bytes // eslint-disable-next-line no-param-reassign parameter[fileSizeOption] = validateFileSizeThreshold(parameter[fileSizeOption]); }); } function normalizeParameters(parameters) { let result; if (!Array.isArray(parameters)) { result = []; Object.keys(parameters).forEach(name => { result.push({ name, options: parameters[name] }); }); } else { result = parameters; } normalizeFileSizeFilter(result, 'minFileSize'); normalizeFileSizeFilter(result, 'maxFileSize'); return result; } function buildFinalConfig(configPathOrObject) { const _config = typeof configPathOrObject === 'string' ? require(configPathOrObject) : configPathOrObject; return (0, _lodash.default)(_config, _config2.default); } var _default = exports.default = { Client: _client.default, config: _config2.default, queries, generateSignedParams: _generateSignedParams.default, /** * Remove and Add application sources * @param {object} client * @param {string} applicationId * @param {{ * sources: Array.<{filename: string, content: string}>, * filesSrc: Array.<string>, * cwd: string, * saveSrc: boolean, * appProfiling: ?object, * runBeforeProtection?: Array<{type: string, target: string, source: string }> * }} opts * @returns {Promise<{extension: string, filename: string, content: *}>} */ async updateApplicationSources(client, applicationId, _ref) { let { sources, filesSrc, cwd, appProfiling, saveSrc = true, runBeforeProtection = [] } = _ref; if (sources || filesSrc && filesSrc.length) { // prevent removing sources if profiling state is READY if (appProfiling && appProfiling.data && appProfiling.data.state === 'READY') { throw new Error('You have a finished Profiling for this application so you are NOT ALLOWED to update sources. To override this behavior use *--remove-profiling-data* or *--skip-sources*.'); } const removeSourceRes = await this.removeSourceFromApplication(client, '', applicationId); errorHandler(removeSourceRes); } let zipped; let source; if (filesSrc && filesSrc.length) { let _filesSrc = []; for (let i = 0, l = filesSrc.length; i < l; i += 1) { if (typeof filesSrc[i] === 'string') { _filesSrc = _filesSrc.concat((0, _utils.getMatchedFiles)(filesSrc[i])); } else { _filesSrc.push(filesSrc[i]); } } if (debug) { console.log('Creating zip from source files'); } if (runBeforeProtection.length > 0) { runBeforeProtection.map(element => { if (!_filesSrc.includes(element.target)) { console.error('Error on beforeProtection: Target files need to be in the files to protect list (or filesSrc).'); process.exit(1); } }); } zipped = await (0, _zip.zip)(_filesSrc, cwd, runBeforeProtection); } else if (sources) { if (debug) { console.log('Creating zip from sources'); } zipped = await (0, _zip.zipSources)(sources); } if (zipped) { const content = await zipped.generateAsync({ type: 'base64', compression: 'DEFLATE', compressionOptions: { // 1 - 9 (max compression) level: 5 } }); if (debug) { console.log('Adding sources to application'); } source = { content, filename: 'application.zip', extension: 'zip' }; if (saveSrc) { errorHandler(await this.addApplicationSource(client, applicationId, source)); } } return source; }, // This method is a shortcut method that accepts an object with everything needed // for the entire process of requesting an application protection and downloading // that same protection when the same ends. // // `configPathOrObject` can be a path to a JSON or directly an object containing // the following structure: // // ```json // { // "keys": { // "accessKey": "", // "secretKey": "" // }, // "applicationId": "", // "filesDest": "" // } // ``` // // Also the following optional parameters are accepted: // // ```json // { // "filesSrc": [""], // "params": {}, // "customLabels": { "env": "production", "version": "9.2.2" }, // "cwd": "", // "host": "api.jscrambler.com", // "port": "443", // "basePath": "" // } // ``` // // `filesSrc` supports glob patterns, and if it's provided it will replace the // entire application sources. // // `params` if provided will replace all the application transformation parameters. // // `customLabels` if provided attaches string key-value metadata to the protection request // (API must support this field). // // `cwd` allows you to set the current working directory to resolve problems with // relative paths with your `filesSrc` is outside the current working directory. // // Finally, `host` and `port` can be overridden if you to engage with a different // endpoint than the default one, useful if you're running an enterprise version of // Jscrambler or if you're provided access to beta features of our product. // async protectAndDownload(configPathOrObject, destCallback) { const start = Date.now(); const finalConfig = buildFinalConfig(configPathOrObject); const { applicationId, host, port, basePath, protocol, cafile, keys, sources, stream = true, cwd, params, applicationTypes, languageSpecifications, sourceMaps, randomizationSeed, areSubscribersOrdered, useRecommendedOrder, bail = true, jscramblerVersion, debugMode, proxy, utc, clientId, tolerateMinification, codeHardeningThreshold, useProfilingData, browsers, useAppClassification, profilingDataMode, removeProfilingData, skipSources, inputSymbolTable, entryPoint, excludeList, numberOfProtections, ensureCodeAnnotation, forceAppEnvironment, deleteProtectionOnSuccess, mode, saveSrc, globalNamesPrefix, useGlobalNamesOnModules, generateAlias, customLabels } = finalConfig; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, proxy, utc, clientId }); let filesSrc = finalConfig.filesSrc; let filesDest = finalConfig.filesDest; let runBeforeProtection = finalConfig.beforeProtection; if (finalConfig.numberOfProtections && finalConfig.numberOfProtections > 1) { console.log("Protections will be stored in ".concat(filesDest).concat(filesDest.slice(-1) === '/' ? '' : '/', "[protection-id]")); } if (sources) { filesSrc = undefined; } if (destCallback) { filesDest = undefined; } if (!applicationId) { throw new Error('Required *applicationId* not provided'); } if (!filesDest && !destCallback) { throw new Error('Required *filesDest* not provided'); } let source; if (!skipSources) { const appProfiling = await this.getApplicationProfiling(client, applicationId).catch(e => { if (typeof profilingDataMode === 'string' && profilingDataMode !== 'off') { switch (e.statusCode) { case _constants.HTTP_STATUS_CODES.FORBIDDEN: throw new Error("No ".concat(profilingDataMode, " profiling feature in your plan. Please set profilingDataMode to \"off\" or contact the Jscrambler Support.")); case _constants.HTTP_STATUS_CODES.NOT_FOUND: if (profilingDataMode === 'automatic') { throw new Error('You can not use the automatic mode without previous profiling having been done.'); } break; case _constants.HTTP_STATUS_CODES.SERVICE_UNAVAILABLE: if (profilingDataMode === 'automatic') { throw e; } } } }); if (appProfiling && removeProfilingData) { await this.deleteProfiling(client, appProfiling.data.id); appProfiling.data.state = 'DELETED'; } source = await this.updateApplicationSources(client, applicationId, { sources, filesSrc, cwd, appProfiling, runBeforeProtection, saveSrc }); } else { console.log('Update source files SKIPPED'); } const updateData = { debugMode: !!debugMode, tolerateMinification, codeHardeningThreshold }; if (params && Object.keys(params).length) { updateData.parameters = normalizeParameters(params); updateData.areSubscribersOrdered = Array.isArray(params); } const dataToValidate = { applicationTypes, areSubscribersOrdered, browsers, languageSpecifications, profilingDataMode, sourceMaps, useAppClassification, ensureCodeAnnotation, useProfilingData, useRecommendedOrder, mode, generateAlias }; for (const prop in dataToValidate) { const value = dataToValidate[prop]; if (typeof value !== 'undefined') { updateData[prop] = value; } } if ((updateData.parameters || updateData.applicationTypes || updateData.languageSpecifications || updateData.browsers || typeof updateData.areSubscribersOrdered !== 'undefined') && saveSrc) { if (debug) { console.log('Updating parameters of protection'); } const applicationUpdate = await intoObjectType(client, updateData, 'ApplicationUpdate'); const updateApplicationRes = await this.updateApplication(client, applicationUpdate, undefined, applicationId); if (debug) { console.log('Finished updating parameters of protection'); console.error(updateApplicationRes); } errorHandler(updateApplicationRes); } if (debug) { console.log('Creating Application Protection'); } const protectionOptions = _objectSpread(_objectSpread({}, updateData), {}, { bail, entryPoint, excludeList, inputSymbolTable, randomizationSeed, source, tolerateMinification, numberOfProtections, forceAppEnvironment, mode, globalNamesPrefix, useGlobalNamesOnModules, generateAlias }); const normalizedCustomLabels = (0, _utils.validateCustomLabels)(customLabels); if (Object.keys(normalizedCustomLabels).length) { protectionOptions.customLabels = normalizedCustomLabels; } if (finalConfig.inputSymbolTable) { const inputSymbolTableContents = await _fs.default.promises.readFile(finalConfig.inputSymbolTable, 'utf-8'); protectionOptions.inputSymbolTable = inputSymbolTableContents; } if (debug) { console.log('Parameters', JSON.stringify(updateData.parameters, null, 2)); } const createApplicationProtectionRes = await this.createApplicationProtections(client, applicationId, protectionOptions); errorHandler(createApplicationProtectionRes); const protectionIds = createApplicationProtectionRes.data.protections.map(_ref2 => { let { _id } = _ref2; return _id; }); const onExitCancelProtection = async () => { for (let i = 0; i < protectionIds.length; i++) { const protectionId = protectionIds[i]; try { await this.cancelProtection(client, protectionId, applicationId); console.log('** Protection %s WAS CANCELLED **', protectionId); } catch (e) { if (debug) { console.error(e); } } } process.exit(1); }; process.once('SIGINT', onExitCancelProtection).once('SIGTERM', onExitCancelProtection); const downloadOptions = { filesDest, destCallback, stream, multiple: protectionOptions.numberOfProtections && protectionOptions.numberOfProtections > 1, deleteProtectionOnSuccess }; const processedProtections = await this.pollProtections(client, applicationId, protectionIds, await (0, _getProtectionDefaultFragments.default)(client), downloadOptions); process.removeListener('SIGINT', onExitCancelProtection).removeListener('SIGTERM', onExitCancelProtection); const handleProtection = async protection => { if (protection.growthWarning) { console.warn("Warning: Your protected application has surpassed a reasonable file growth.\nFor more information on what might have caused this, please see the Protection Report.\nLink: ".concat(APP_URL, ".")); } if (protection.hasForcedDateLock) { console.warn("Warning: Since your plan is a Trial, your protected files will stop working on ".concat(protection.parameters.find(p => p.name === 'dateLock' && p.options.endDate).options.endDate)); } if (debug) { console.log("Finished protecting".concat(downloadOptions.multiple ? ": ".concat(protection._id) : '')); } if (protection.deprecations) { protection.deprecations.forEach(deprecation => { if (deprecation.type === 'Transformation') { console.warn("Warning: ".concat(deprecation.type, " ").concat(deprecation.entity, " is no longer maintained. Please consider removing it from your configuration.")); } else if (deprecation.type && deprecation.entity) { console.warn("Warning: ".concat(deprecation.type, " ").concat(deprecation.entity, " is deprecated.")); } }); } const sourcesErrors = []; protection.sources.forEach(s => { if (s.isSource && s.errorMessages && s.errorMessages.length > 0) { sourcesErrors.push(...s.errorMessages.map(e => _objectSpread({ filename: s.filename }, e))); } }); if (protection.state === 'errored') { console.error('Global protection errors:'); console.error("- ".concat(protection.errorMessage)); console.error(''); if (sourcesErrors.length > 0) { printSourcesErrors(sourcesErrors); } throw new Error("Protection failed. For more information visit: ".concat(APP_URL, ".")); } else if (sourcesErrors.length > 0) { if (protection.bail) { printSourcesErrors(sourcesErrors); throw new Error('Your protection has failed.'); } else { sourcesErrors.forEach(e => console.warn("Non-fatal error: \"".concat(e.message, "\" in ").concat(e.filename))); } } if (!protectionOptions.numberOfProtections || protectionOptions.numberOfProtections === 1) { await this.handleApplicationProtectionDownload(client, protection._id, applicationId, downloadOptions); } return protection._id; }; if (processedProtections.length === 1) { return handleProtection(processedProtections[0]); } for (let i = 0; i < processedProtections.length; i++) { const protection = processedProtections[i]; try { await handleProtection(protection); } catch (e) { console.error(e); } } console.log("Runtime: ".concat(processedProtections.length, " protections in ").concat(Math.round((Date.now() - start) / 1000), "s")); return protectionIds; }, /** * Handle the download, unzipping, and possible deletion of protections * @param {object} client * @param {string} protectionId * @param {string} applicationId * @param {object} downloadOptions * @returns {Promise<object>} */ async handleApplicationProtectionDownload(client, protectionId, applicationId, downloadOptions) { const { filesDest, destCallback, stream, multiple, deleteProtectionOnSuccess } = downloadOptions; if (debug) { console.log("Downloading protection ".concat(multiple ? "".concat(protectionId, " ") : '', "result")); } const download = await this.downloadApplicationProtection(client, protectionId); errorHandler(download); if (debug) { console.log("Unzipping files".concat(multiple ? " from protection ".concat(protectionId) : '')); } await (0, _zip.unzip)(download, (filesDest ? "".concat(filesDest).concat(multiple ? "/".concat(protectionId, "/") : '') : filesDest) || destCallback, stream); if (debug) { console.log("Finished unzipping files for protection".concat(multiple ? " ".concat(protectionId) : '')); } if (!multiple) { console.log(protectionId); } // change this to have the variable that checks if the protection is to be removed if (deleteProtectionOnSuccess) { await this.removeProtection(client, protectionId, applicationId).then(() => { if (debug) { console.log('Protection has been successful and will now be deleted'); } }).catch(error => console.error(error)); } }, /** * Instrument and download application sources for profiling purposes * @param {object} configPathOrObject * @param {function} [destCallback] * @returns {Promise<string>} */ async instrumentAndDownload(configPathOrObject, destCallback) { const finalConfig = buildFinalConfig(configPathOrObject); const { applicationId, host, port, basePath, protocol, cafile, keys, sources, stream = true, cwd, jscramblerVersion, proxy, utc, skipSources, clientId } = finalConfig; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, proxy, utc, clientId }); let { filesSrc, filesDest } = finalConfig; if (sources) { filesSrc = undefined; } if (destCallback) { filesDest = undefined; } if (!applicationId) { throw new Error('Required *applicationId* not provided'); } if (!filesDest && !destCallback) { throw new Error('Required *filesDest* not provided'); } if (!skipSources) { await this.updateApplicationSources(client, applicationId, { sources, filesSrc, cwd }); } else { console.log('Update source files SKIPPED'); } let instrumentation = await this.startInstrumentation(client, applicationId); errorHandler(instrumentation); const onExitCancelInstrumentation = () => { this.deleteProfiling(client, instrumentation.data.id).then(() => console.log('\n** Instrumentation %s WAS CANCELLED **', instrumentation.data.id)).catch(() => debug && console.error(e)).finally(() => process.exit(1)); }; process.once('SIGINT', onExitCancelInstrumentation).once('SIGTERM', onExitCancelInstrumentation); instrumentation = await this.pollInstrumentation(client, instrumentation.data.id); process.removeListener('SIGINT', onExitCancelInstrumentation).removeListener('SIGTERM', onExitCancelInstrumentation); if (debug) { console.log("Finished instrumention with id ".concat(instrumentation.data.id, ". Downloading...")); } const download = await this.downloadApplicationInstrumented(client, instrumentation.data.id); errorHandler(download); if (debug) { console.log('Unzipping files'); } await (0, _zip.unzip)(download, filesDest || destCallback, stream); if (debug) { console.log('Finished unzipping files'); } console.warn("\n WARNING: DO NOT SEND THIS CODE TO PRODUCTION AS IT IS NOT PROTECTED\n "); console.log("Application ".concat(applicationId, " was instrumented. Bootstrap your instrumented application and run *--start-profiling* command.")); return instrumentation.data.id; }, /** * Change the profiling run stat. * @param configPathOrObject * @param state * @param label * @param {string} nextStepMessage * @returns {Promise<string>} The previous state */ async setProfilingState(configPathOrObject, state, label, nextStepMessage) { const finalConfig = buildFinalConfig(configPathOrObject); const { keys, host, port, basePath, protocol, cafile, applicationId, proxy, utc, jscramblerVersion, clientId } = finalConfig; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, proxy, utc, jscramblerVersion, clientId }); const instrumentation = await client.get('/profiling-run', { applicationId }).catch(e => { if (e.statusCode !== 404) throw e; }); if (!instrumentation) { throw new Error('There is no active profiling run. Instrument your application first.'); } const previousState = instrumentation.data.state; if (previousState === state) { console.log("Profiling was already ".concat(label, " for application ").concat(applicationId, ". ").concat(nextStepMessage)); return; } await client.patch("/profiling-run/".concat(instrumentation.data.id), { state }); console.log("Profiling was ".concat(label, " for application ").concat(applicationId, ". ").concat(nextStepMessage)); }, async downloadSourceMaps(configs, destCallback) { const { keys, host, port, basePath, protocol, cafile, stream = true, filesDest, filesSrc, protectionId, jscramblerVersion, utc, proxy } = configs; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, utc, proxy }); if (!filesDest && !destCallback) { throw new Error('Required *filesDest* not provided'); } if (!protectionId) { throw new Error('Required *protectionId* not provided'); } if (filesSrc) { console.warn('[Warning] Ignoring sources supplied. Downloading source maps of given protection'); } let download; try { download = await this.downloadSourceMapsRequest(client, protectionId); } catch (e) { errorHandler(e); } await (0, _zip.unzip)(download, filesDest || destCallback, stream); }, async downloadSymbolTable(configs, destCallback) { const { keys, host, port, basePath, protocol, cafile, stream = true, filesDest, filesSrc, protectionId, jscramblerVersion, utc, proxy } = configs; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, utc, proxy }); if (!filesDest && !destCallback) { throw new Error('Required *filesDest* not provided'); } if (!protectionId) { throw new Error('Required *protectionId* not provided'); } if (filesSrc) { console.warn('[Warning] Ignoring sources supplied. Downloading symbol table of given protection'); } let download; try { download = await this.downloadSymbolTableRequest(client, protectionId); } catch (e) { console.warn('[Warning] The symbol table can only be output if you have Identifiers Renaming on UNSAFE mode or an includeList with global or public identifiers.'); errorHandler(e); } if (typeof destCallback === 'function') { destCallback(download, filesDest); } else { await _fs.default.promises.mkdir(filesDest, { recursive: true }); await _fs.default.promises.writeFile(_path.default.join(filesDest, "".concat(protectionId, "_symbolTable.json")), download); } }, /** * Polls a instrumentation every POLLING_INTERVALms until the state be equal to * FINISHED_INSTRUMENTATION, FAILED_INSTRUMENTATION or DELETED * @param {object} client * @param {string} instrumentationId * @returns {Promise<object>} * @throws {Error} due to errors in instrumentation process or user cancel the operation */ async pollInstrumentation(client, instrumentationId) { const start = Date.now(); const poll = async () => { const instrumentation = await this.getInstrumentation(client, instrumentationId); switch (instrumentation.data.state) { case 'DELETED': throw new Error('Protection canceled by user'); case 'FAILED_INSTRUMENTATION': instrumentation.errors = instrumentation.errors.concat(instrumentation.data.instrumentationErrors.map(e => ({ message: "".concat(e.message, " at ").concat(e.fileName, ":").concat(e.lineNumber) }))); return errorHandler(instrumentation); case 'FINISHED_INSTRUMENTATION': return instrumentation; default: await new Promise(resolve => setTimeout(resolve, getPollingInterval(start))); return poll(); } }; return poll(); }, async withRetries(action) { let retriesLeft = _config2.default.maxRetries; for (;;) { try { return await action(); } catch (e) { if (retriesLeft <= 0) { throw e; } if (e.statusCode !== _constants.HTTP_STATUS_CODES.SERVICE_UNAVAILABLE && e.statusCode !== _constants.HTTP_STATUS_CODES.GATEWAY_TIMEOUT) { throw e; } // Retry if (debug) { console.log('Retrying request'); } retriesLeft--; } } }, async pollProtection(client, applicationId, protectionId, fragments) { const start = Date.now(); const poll = async () => { const applicationProtection = await this.withRetries(() => this.getApplicationProtection(client, applicationId, protectionId, fragments)); if (applicationProtection.errors) { console.log('Error polling protection', applicationProtection.errors); throw new Error("Protection failed. For more information visit: ".concat(APP_URL, ".")); } else { const { state } = applicationProtection.data.applicationProtection; if (state !== 'finished' && state !== 'errored' && state !== 'canceled') { await new Promise(resolve => setTimeout(resolve, getPollingInterval(start))); return poll(); } else if (state === 'canceled') { throw new Error('Protection canceled by user'); } else { return applicationProtection.data.applicationProtection; } } }; return poll(); }, async pollProtections(client, applicationId, protectionIds, fragments, downloadOptions) { if (protectionIds.length === 1) { return [await this.pollProtection(client, applicationId, protectionIds[0], fragments)]; } const start = Date.now(); let seen = {}; const poll = async () => { const applicationProtections = await this.withRetries(() => this.getApplicationProtections(client, applicationId, { protectionIds }, fragments.applicationProtection, ["$protectionIds: [String]"])); if (applicationProtections.errors) { console.log('Error polling protection', applicationProtections.errors); throw new Error("Protection failed. For more information visit: ".concat(APP_URL, ".")); } else { const ended = applicationProtections.data.applicationProtections.filter(_ref3 => { let { state } = _ref3; return state === 'finished' || state === 'errored' || state === 'canceled'; }); // print progress ended.filter(_ref4 => { let { _id, state } = _ref4; return !seen[_id] && state !== 'canceled'; }).forEach(async _ref5 => { let { _id, startedAt, finishedAt, state } = _ref5; seen[_id] = true; console.log("[".concat(Object.keys(seen).length, "/").concat(protectionIds.length, "] Protection=").concat(_id, ", state=").concat(state, ", build-time=").concat(Math.round((new Date(finishedAt) - new Date(startedAt)) / 1000), "s")); await this.handleApplicationProtectionDownload(client, _id, applicationId, downloadOptions); console.log("Downloaded: ".concat(_id)); }); if (ended.length < protectionIds.length) { await new Promise(resolve => setTimeout(resolve, getPollingInterval(start))); return poll(); } return applicationProtections.data.applicationProtections; } }; return poll(); }, // async createApplication(client, data, fragments) { return client.post('/application', mutations.createApplication(data, fragments)); }, // async duplicateApplication(client, data, fragments) { return client.post('/application', mutations.duplicateApplication(data, fragments)); }, // async removeApplication(client, id) { return client.post('/application', mutations.removeApplication(id)); }, // async removeProtection(client, id, appId, fragments) { return client.post('/application', mutations.removeProtection(id, appId, fragments)); }, // async cancelProtection(client, id, appId, fragments) { const mutation = await mutations.cancelProtection(id, appId, fragments); return client.post('/application', mutation); }, // async updateApplication(client, applicationData, fragments, applicationId) { const mutation = await mutations.updateApplication(applicationData, fragments, applicationId); return client.post('/application', mutation); }, // async unlockApplication(client, application, fragments) { const mutation = await mutations.unlockApplication(application, fragments); return client.post('/application', mutation); }, // async getApplication(client, applicationId, fragments, params) { const query = await queries.getApplication(applicationId, fragments, params); return client.get('/application', query); }, // async getApplicationSource(client, sourceId, fragments, limits) { const query = await queries.getApplicationSource(sourceId, fragments, limits); return client.get('/application', query); }, // async getApplicationProtections(client, applicationId, params, fragments, queryArgs) { const query = queries.getApplicationProtections(applicationId, params, fragments, queryArgs); return client.get('/application', query); }, // async getApplicationProtectionsCount(client, applicationId, fragments) { const query = await queries.getApplicationProtectionsCount(applicationId, fragments); return client.get('/application', query); }, // async createTemplate(client, template, fragments) { const mutation = await mutations.createTemplate(template, fragments); return client.post('/application', mutation); }, // async removeTemplate(client, id) { const mutation = await mutations.removeTemplate(id); return client.post('/application', mutation); }, // async getTemplates(client, fragments) { const query = await queries.getTemplates(fragments); return client.get('/application', query); }, // async getApplications(client, fragments, params) { const query = await queries.getApplications(fragments, params); return client.get('/application', query); }, // async addApplicationSource(client, applicationId, applicationSource, fragments) { const mutation = await mutations.addApplicationSource(applicationId, applicationSource, fragments); return this.withRetries(() => client.post('/application', mutation)); }, // async addApplicationSourceFromURL(client, applicationId, url, fragments) { const file = await getFileFromUrl(client, url); const mutation = await mutations.addApplicationSource(applicationId, file, fragments); return client.post('/application', mutation); }, // async updateApplicationSource(client, applicationSource, fragments) { const mutation = await mutations.updateApplicationSource(applicationSource, fragments); return client.post('/application', mutation); }, // async removeSourceFromApplication(client, sourceId, applicationId, fragments) { const mutation = await mutations.removeSourceFromApplication(sourceId, applicationId, fragments); return this.withRetries(() => client.post('/application', mutation)); }, // async applyTemplate(client, templateId, appId, fragments) { const mutation = await mutations.applyTemplate(templateId, appId, fragments); return client.post('/application', mutation); }, // async updateTemplate(client, template, fragments) { const mutation = await mutations.updateTemplate(template, fragments); return client.post('/application', mutation); }, async getApplicationProfiling(client, applicationId) { return client.get('/profiling-run', { applicationId }); }, async deleteProfiling(client, profilingId) { return client.patch("/profiling-run/".concat(profilingId), { state: 'DELETED' }); }, /** * Starts a new instrumentation process. * Previous instrumentation must be deleted, before starting a new one. * @param client * @param applicationId * @returns {Promise<*>} */ async startInstrumentation(client, applicationId) { const instrumentation = await this.getApplicationProfiling(client, applicationId).catch(e => { if (e.statusCode !== 404) throw e; }); if (instrumentation) { await this.deleteProfiling(client, instrumentation.data.id); } return client.post('/profiling-run', { applicationId }); }, // async createApplicationProtection(client, applicationId, protectionOptions, fragments) { const { args } = await introspection.mutation(client, 'createApplicationProtection'); const mutation = await mutations.createApplicationProtection(applicationId, fragments, protectionOptions, args); return client.post('/application', mutation); }, /** * Create one or more application protections at once * @param {JscramblerClient} client * @param {string} applicationId * @param {object} protectionOptions * @param {number} [protectionOptions.numberOfProtections] * @param {object} fragments * @returns {Promise<{data: {protections: Array.<{_id}>}, errors: Array}>} */ async createApplicationProtections(client, applicationId, protectionOptions, fragments) { let result; if (!protectionOptions.numberOfProtections || protectionOptions.numberOfProtections < 2) { result = await this.createApplicationProtection(client, applicationId, _objectSpread(_objectSpread({}, protectionOptions), {}, { numberOfProtections: undefined }), fragments); if (result.data && result.data.createApplicationProtection) { result.data.protections = [result.data.createApplicationProtection]; delete result.data.createApplicationProtection; } } else { const mutationType = await introspection.mutation(client, 'createApplicationProtections'); if (!mutationType) { console.error("\"Create multiple protections at once\" it's only available on Jscrambler version 7.2 and above."); process.exit(1); } const mutation = await mutations.createApplicationProtections(applicationId, fragments, protectionOptions, mutationType.args); result = await client.post('/application', mutation); if (result.data && result.data.createApplicationProtections) { result.data.protections = result.data.createApplicationProtections.protections; delete result.data.createApplicationProtections; } } return result; }, /** * @param {object} client * @param {string} instrumentationId * @returns {Promise<object>} */ async getInstrumentation(client, instrumentationId) { return client.get("/profiling-run/".concat(instrumentationId)); }, // async getApplicationProtection(client, applicationId, protectionId, fragments) { const query = await queries.getProtection(applicationId, protectionId, fragments); return client.get('/application', query); }, // async downloadSourceMapsRequest(client, protectionId) { return client.get("/application/sourceMaps/".concat(protectionId), null, false); }, async downloadSymbolTableRequest(client, protectionId) { return client.get("/application/symbolTable/".concat(protectionId), null, false); }, // async downloadApplicationProtection(client, protectionId) { return client.get("/application/download/".concat(protectionId), null, false); }, /** * @param {object} client * @param {string} instrumentationId * @returns {*} */ downloadApplicationInstrumented(client, instrumentationId) { return client.get("/profiling-run/".concat(instrumentationId, "/instrumented-bundle"), null, false); }, /** * Introspect method to check if a certain field is supported. * @param {Object} config jscrambler client config * @param {String} queryOrMutation a string in ['query, 'mutation'] * @param {String} methodName query or mutation name * @param {String} field args field to introspect * @returns {Boolean} true if the field is supported, false otherwise */ async introspectFieldOnMethod(config, queryOrMutation, methodName, field) { const instrospectionType = queryOrMutation.toLowerCase() === 'mutation' ? introspection.mutation : introspection.query; const client = new this.Client(_objectSpread({}, config)); const result = await instrospectionType(client, methodName); if (!result || !result.args) { debug && console.log("Method *".concat(methodName, "* not found.")); return false; } const dataArg = result.args.find(arg => arg.name === 'data'); const isFieldSupported = dataArg && dataArg.type.inputFields.some(e => e.name === field); return isFieldSupported; }, async getProtectionMetadata(conf, protectionId, outputDir) { const INVALID_PARAMETERS = ['original', 'initialCleanup', 'wrapUp']; const finalConfig = buildFinalConfig(conf); const { applicationId, host, port, basePath, protocol, cafile, keys, jscramblerVersion, proxy, utc, clientId } = finalConfig; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, proxy, utc, clientId }); const appSource = await (0, _getProtectionDefaultFragments.getIntrospection)(client, 'ApplicationSource'); if (!appSource.fields.some(_ref6 => { let { name } = _ref6; return name === 'transformedContentHash'; })) { console.error("\"Protection report\" it's only available on Jscrambler version 8.4 and above."); process.exit(1); } const response = await this.getApplicationProtection(client, applicationId, protectionId, { application: '_id', applicationProtection: '_id, applicationId, parameters, version, areSubscribersOrdered, useRecommendedOrder, tolerateMinification, profilingDataMode, useAppClassification, browsers, sourceMaps, sources { filename, transformedContentHash, metrics { transformation } }' }); errorHandler(response); const sourcesInfo = response.data.applicationProtection.sources.map(source => { const parameters = source.metrics.filter(metric => !INVALID_PARAMETERS.includes(metric.transformation)); return { filename: source.filename, sha256Checksum: source.transformedContentHash, parameters: parameters.map(param => param.transformation) }; }); const metadataJson = JSON.stringify({ applicationId: response.data.applicationProtection.applicationId, // eslint-disable-next-line no-underscore-dangle protectionId: response.data.applicationProtection._id, jscramblerVersion: response.data.applicationProtection.version, areSubscribersOrdered: response.data.applicationProtection.areSubscribersOrdered, useRecommendedOrder: response.data.applicationProtection.useRecommendedOrder, tolerateMinification: response.data.applicationProtection.tolerateMinification, profilingDataMode: response.data.applicationProtection.profilingDataMode, useAppClassification: response.data.applicationProtection.useAppClassification, browsers: response.data.applicationProtection.browsers, sourceMaps: response.data.applicationProtection.sourceMaps, parameters: response.data.applicationProtection.parameters, sources: sourcesInfo }, null, 2); if (outputDir) { await _fs.default.promises.writeFile(outputDir, metadataJson); console.log("Protection Report ".concat(protectionId, " saved in ").concat(outputDir)); } else { console.log(metadataJson); } }, async getBalance(conf) { const finalConfig = buildFinalConfig(conf); const { host, port, basePath, protocol, cafile, keys, jscramblerVersion, proxy, utc, clientId } = finalConfig; const { accessKey, secretKey } = keys; const client = new this.Client({ accessKey, secretKey, host, port, basePath, protocol, cafile, jscramblerVersion, proxy, utc, clientId }); const balance = await client.get('/balance'); console.log(balance); return balance; } }; function getFileFromUrl(client, url) { return _axios.default.get(url).then(res => ({ content: res.data, filename: _path.default.basename(url), extension: _path.default.extname(url).substr(1) })); }