UNPKG

jscrambler

Version:

Jscrambler Code Integrity API client.

470 lines (459 loc) 23.1 kB
#!/usr/bin/env node "use strict"; require("core-js/modules/es.regexp.exec.js"); require("core-js/modules/web.dom-collections.iterator.js"); var _commander = _interopRequireDefault(require("commander")); var _lodash = _interopRequireDefault(require("lodash.defaults")); var _path = _interopRequireDefault(require("path")); var _config2 = _interopRequireDefault(require("../config")); var _ = _interopRequireDefault(require("../")); var _utils = require("../utils"); 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); } const debug = !!process.env.DEBUG; const validateBool = option => val => { if (!/^(true|false)$/i.test(val)) { console.error("*".concat(option, "* requires a <bool> value.")); process.exit(1); } return val.toLowerCase(); }; const validateCodeHardeningThreshold = (0, _utils.validateThresholdFn)('code-hardening-threshold'); const validateProfilingDataMode = mode => { const availableModes = ['automatic', 'annotations', 'off']; const normalizedMode = mode.toLowerCase(); if (!availableModes.includes(normalizedMode)) { console.error("*profiling-data-mode* requires one of the following modes: {".concat(availableModes.toString(), "}. Example: --profiling-data-mode ").concat(availableModes[0])); process.exit(1); } return normalizedMode; }; const availableEnvironments = ['node', 'browser', 'isomorphic', 'automatic']; const validateForceAppEnvironment = env => { const normalizeEnvironment = env.toLowerCase(); if (!availableEnvironments.includes(normalizeEnvironment)) { console.error("*force-app-environment* requires one of the following values: {".concat(availableEnvironments.toString(), "}. Example: --force-app-environment ").concat(availableEnvironments[0])); process.exit(1); } return normalizeEnvironment; }; const validateMode = mode => { const availableModes = ['automatic', 'manual']; const normalizedMode = mode.toLowerCase(); if (!availableModes.includes(normalizedMode)) { console.error("*mode* requires one of the following modes: {".concat(availableModes.toString(), "}. Example: --mode ").concat(availableModes[0])); process.exit(1); } return normalizedMode; }; const validateBeforeProtection = function () { let beforeProtectionArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; if (beforeProtectionArray.length === 0) { return; } const mandatoryKeys = ['type', 'target', 'source']; const usedTargets = new Set(); const usedSources = new Set(); beforeProtectionArray.filter(element => { // Check if every array element has a type, a target and a source const validateMandatoryKeys = mandatoryKeys.every(key => key in element); if (!validateMandatoryKeys) { console.error('Invalid structure on beforeProtection: each element must have the following structure { type: "type", target: "/path/to/target", source: "/path/to/script"}'); process.exit(1); } const { target, source, type } = element; // Check if only valid types are being used if (type !== _utils.APPEND_JS_TYPE && type !== _utils.PREPEND_JS_TYPE) { console.error("Invalid type on beforeProtection: only \"".concat(_utils.APPEND_JS_TYPE, "\" or \"").concat(_utils.PREPEND_JS_TYPE, "\" are allowed.")); process.exit(1); } // Check if the provided files are js, mjs or cjs files if (!(0, _utils.isJavascriptFile)(target) || !(0, _utils.isJavascriptFile)(source)) { console.error('Invalid extension for beforeProtection target or source files: only *js, mjs and cjs* files can be used to append or prepend.'); process.exit(1); } // Check if the target has already been used as a source if (usedTargets.has(source)) { console.error("Error on beforeProtection: file \"".concat(source, "\" has already been used as target and can't be used as source.")); process.exit(1); } if (usedSources.has(target)) { console.error("Error on beforeProtection: file \"".concat(target, "\" has already been used as source and can't be used as target.")); process.exit(1); } // Check if the target and source are the same if (target === source) { console.error("Error on beforeProtection: File \"".concat(target, "\" can't be used as both a target and a source.")); process.exit(1); } // Add the target and the source to the corresponding sets usedTargets.add(target); usedSources.add(source); }); return beforeProtectionArray; }; const parseCustomLabelCommandLineOption = function (val) { let accumulator = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const idx = val.indexOf('='); if (idx < 1) { console.error("*--custom-label* expects key=value (for example --custom-label \"env=production\"), got \"".concat(val, "\"")); process.exit(1); } const key = val.slice(0, idx); const value = val.slice(idx + 1); return _objectSpread(_objectSpread({}, accumulator), {}, { [key]: value }); }; _commander.default.version(require('../../package.json').version).usage('[options] <file ...>').option('-a, --access-key <accessKey>', 'Access key').option('-c, --config <config>', 'Jscrambler configuration options').option('-H, --host <host>', 'Hostname').option('-i, --application-id <id>', 'Application ID').option('-o, --output-dir <dir>', 'Output directory').option('-p, --port <port>', 'Port').option('--base-path <path>', 'Base Path').option('--protocol <protocol>', 'Protocol (http or https)').option('--cafile <path>', 'Internal certificate authority').option('-C, --cwd <dir>', 'Current Working Directory').option('-s, --secret-key <secretKey>', 'Secret key').option('-m, --source-maps <id>', 'Download source maps').option('-R, --randomization-seed <seed>', 'Set randomization seed').option('--instrument', 'Instrument file(s) before start profiling. ATTENTION: previous profiling information will be deleted').option('--start-profiling', 'Starts profiling (assumes an already instrumented application)').option('--stop-profiling', 'Stops profiling').option('--code-hardening-threshold <threshold>', 'Set code hardening file size threshold. Format: {value}{unit="b,kb,mb"}. Example: 200kb', validateCodeHardeningThreshold).option('--recommended-order <bool>', 'Use recommended order', validateBool('recommended-order')).option('-W, --werror <bool>', 'Set werror flag value (default: true)', validateBool('werror')).option('--utc <bool>', 'Set UTC as the request time zone. Otherwise it uses the local time zone (default: true)', validateBool('utc')).option('--tolerate-minification <bool>', "Don't detect minification as malicious tampering (default: true)", validateBool('tolerate-minification')).option('--use-profiling-data <bool>', "(version 6.2 only) Protection should use the existing profiling data (default: true)", validateBool('use-profiling-data')).option('--profiling-data-mode <mode>', "(version 6.3 and above) Select profiling mode (default: automatic)", validateProfilingDataMode).option('--remove-profiling-data', "Removes the current application profiling information").option('--use-app-classification <bool>', '(version 6.3 and above) Protection should use Application Classification metadata when protecting (default: true)', validateBool('--use-app-classification')).option('--input-symbol-table <file>', '(version 6.3 and above) Protection should use symbol table when protecting. (default: no file)').option('--output-symbol-table <id>', '(version 6.3 and above) Download output symbol table (json)').option('--jscramblerVersion <version>', 'Use a specific Jscrambler version').option('--debugMode', 'Protect in debug mode').option('--skip-sources', 'Prevent source files from being updated').option('--force-app-environment <environment>', "(version 7.1 and above) Override application's environment detected automatically. Possible values: ".concat(availableEnvironments.toString()), validateForceAppEnvironment).option('--ensure-code-annotation <bool>', "(version 7.3 and above) Fail protection if no annotations are found on the source code (default: false)", validateBool('ensure-code-annotation')).option('-n <number>', "(version 7.2 and above) Create multiple protections at once.").option('--delete-protection-on-success <bool>', 'Deletes the protection files after they have been protected and downloaded (default: false)', validateBool('--delete-protection-on-success')).option('--mode <mode>', "(version 8.4 and above) Define protection mode. Possible values: automatic, manual (default: manual)", validateMode).option('--save-src <bool>', 'Protection should save application sources (default: true)', validateBool('--save-src')).option('--protection-report <string>', '(version 8.4 and above) Protection id for the metadata report').option('--use-global-names-on-modules <bool>', '(version 8.5 and above) Force the usage of more complex names on modules').option('--balance', '(version 8.4 and above) Gets the balance of the user').option('--generate-alias <bool>', '(version 8.5 and above) Generate alias for the transformations', validateBool('--generate-alias')).option('--custom-label <key=value>', 'Attach a custom label to the protection (repeatable). Example: --custom-label "env=production"', parseCustomLabelCommandLineOption, {}).parse(process.argv); let globSrc, filesSrc, config; // If -c, --config file was provided if (_commander.default.config) { // We're using `commander` (CLI) as the source of all truths, falling back to // the `config` provided by the file passed as argument config = require(_path.default.resolve(_commander.default.config, '.')); } else { config = {}; } // Merge explicit config file (specified by CLI flag `--config`) with `.jscramblerrc` (`_config` fills undefined keys) config = (0, _lodash.default)(config, _config2.default); config.accessKey = _commander.default.accessKey || (config.keys ? config.keys.accessKey : undefined); config.secretKey = _commander.default.secretKey || (config.keys ? config.keys.secretKey : undefined); config.host = _commander.default.host || config.host; config.port = _commander.default.port || config.port; config.basePath = _commander.default.basePath || config.basePath; config.port = config.port && parseInt(config.port); config.protocol = _commander.default.protocol || config.protocol; config.cafile = _commander.default.cafile || config.cafile; config.filesDest = _commander.default.outputDir || config.filesDest; config.applicationId = _commander.default.applicationId || config.applicationId; config.randomizationSeed = _commander.default.randomizationSeed || config.randomizationSeed; config.cwd = _commander.default.cwd || config.cwd; config.useRecommendedOrder = _commander.default.recommendedOrder ? _commander.default.recommendedOrder !== 'false' : config.useRecommendedOrder; config.tolerateMinification = _commander.default.tolerateMinification ? _commander.default.tolerateMinification !== 'false' : config.tolerateMinification; config.werror = _commander.default.werror ? _commander.default.werror !== 'false' : config.werror; config.jscramblerVersion = _commander.default.jscramblerVersion || config.jscramblerVersion; config.inputSymbolTable = _commander.default.inputSymbolTable || config.inputSymbolTable; config.removeProfilingData = _commander.default.removeProfilingData; config.skipSources = _commander.default.skipSources; config.debugMode = _commander.default.debugMode || config.debugMode; config.instrument = _commander.default.instrument || config.instrument; config.mode = _commander.default.mode || config.mode; config.globalNamesPrefix = _commander.default.globalNamesPrefix || config.globalNamesPrefix; config.useGlobalNamesOnModules = _commander.default.useGlobalNamesOnModules || config.useGlobalNamesOnModules; // handle codeHardening = 0 if (typeof _commander.default.codeHardeningThreshold === 'undefined') { config.codeHardeningThreshold = config.codeHardeningThreshold ? validateCodeHardeningThreshold(config.codeHardeningThreshold) : undefined; } else { config.codeHardeningThreshold = _commander.default.codeHardeningThreshold; } if (_commander.default.profilingDataMode) { config.profilingDataMode = _commander.default.profilingDataMode; } else { config.profilingDataMode = config.profilingDataMode ? validateProfilingDataMode(config.profilingDataMode) : undefined; } if (_commander.default.utc) { config.utc = _commander.default.utc !== 'false'; } if (_commander.default.useProfilingData) { config.useProfilingData = _commander.default.useProfilingData !== 'false'; } if (_commander.default.useAppClassification) { config.useAppClassification = _commander.default.useAppClassification !== 'false'; } if (_commander.default.ensureCodeAnnotation) { config.ensureCodeAnnotation = _commander.default.ensureCodeAnnotation === 'true'; } if (config.jscramblerVersion && !/^(?:\d+\.\d+(?:-f)?|stable|latest)$/.test(config.jscramblerVersion)) { console.error('The Jscrambler version must be in the form of $major.$minor or the words stable and latest. (e.g. 5.2, stable, latest)'); process.exit(1); } if (_commander.default.forceAppEnvironment) { config.forceAppEnvironment = _commander.default.forceAppEnvironment; } else { config.forceAppEnvironment = config.forceAppEnvironment ? validateForceAppEnvironment(config.forceAppEnvironment) : undefined; } // Validate customLabels from file config try { config.customLabels = (0, _utils.validateCustomLabels)(config.customLabels); } catch (err) { console.error(err.message); process.exit(1); } // Merge file config with CLI `--custom-label` flags (CLI wins on duplicate keys) config.customLabels = _objectSpread(_objectSpread({}, config.customLabels), _commander.default.customLabel || {}); if (Object.keys(config.customLabels).length === 0) { delete config.customLabels; } config.numberOfProtections = (0, _utils.validateNProtections)(_commander.default.N); if (config.codeHardeningThreshold) { config.codeHardeningThreshold = validateCodeHardeningThreshold(config.codeHardeningThreshold); } if (config.profilingDataMode) { config.profilingDataMode = validateProfilingDataMode(config.profilingDataMode); } if (config.beforeProtection) { config.beforeProtection = validateBeforeProtection(config.beforeProtection); } if (_commander.default.deleteProtectionOnSuccess) { config.deleteProtectionOnSuccess = _commander.default.deleteProtectionOnSuccess === 'true'; } if (_commander.default.saveSrc) { config.saveSrc = _commander.default.saveSrc !== 'false'; } else { config.saveSrc = config.saveSrc !== false; } if (_commander.default.generateAlias) { config.generateAlias = _commander.default.generateAlias !== 'false'; } globSrc = config.filesSrc; // If src paths have been provided if (_commander.default.args.length > 0) { globSrc = _commander.default.args; } if (globSrc && globSrc.length) { // this will be translated to real files in the updateApplicationSources method filesSrc = globSrc; let nSources = 0; // Iterate `globSrc` to build a list of source files into `filesSrc` for (let i = 0, l = globSrc.length; i < l; i += 1) { // Calling sync `glob` because async is pointless for the CLI use case // (as of now at least) // If the user is providing a zip alongside more files if (_path.default.extname(globSrc[i]) === '.zip' && globSrc.length > 1) { console.error('Please provide either a zip file containing all your source files or use the minimatch syntax'); process.exit(1); } const tmpGlob = (0, _utils.getMatchedFiles)(globSrc[i]); if (config.werror && tmpGlob.length === 0) { console.error("Pattern \"".concat(globSrc[i], "\" doesn't match any files.")); process.exit(1); } if (debug) { if (tmpGlob.length === 0) { console.log("Pattern \"".concat(globSrc[i], "\" doesn't match any files. Will be ignored.")); } else { console.log("Pattern \"".concat(globSrc[i], "\" matched the following files:")); tmpGlob.forEach(file => { console.log(" ".concat(file)); }); } } nSources += tmpGlob.length; } if (nSources === 0) { console.error('No files matched.'); process.exit(1); } } else if (debug) { console.log('No filesSrc provided. Using the ones in the application (if any).'); } const { applicationId, accessKey, secretKey, filesDest, host, port, basePath, protocol, cafile, applicationTypes, languageSpecifications, areSubscribersOrdered, cwd, randomizationSeed, sourceMaps = false, useRecommendedOrder, werror, tolerateMinification, jscramblerVersion, debugMode, proxy, codeHardeningThreshold, useProfilingData, profilingDataMode, browsers, useAppClassification, removeProfilingData, skipSources, inputSymbolTable, utc, entryPoint, excludeList, numberOfProtections, ensureCodeAnnotation, forceAppEnvironment, beforeProtection, deleteProtectionOnSuccess, mode, saveSrc, globalNamesPrefix, useGlobalNamesOnModules, generateAlias, customLabels } = config; const params = config.params; const incompatibleOptions = ['sourceMaps', 'instrument', 'startProfiling', 'stopProfiling']; const usedIncompatibleOptions = []; for (const incompatibleOption of incompatibleOptions) { if (_commander.default[incompatibleOption]) { usedIncompatibleOptions.push(incompatibleOption); } } if (usedIncompatibleOptions.length > 1) { console.error('Using mutually exclusive options:', usedIncompatibleOptions); process.exit(1); } if ((_commander.default.mode === 'automatic' || config.mode === 'automatic') && config.params) { console.error('Cannot submit a Jscrambler configuration file in automatic mode with parameters.'); process.exit(1); } const clientSettings = { keys: { accessKey, secretKey }, host, port, basePath, protocol, cafile, proxy, utc, jscramblerVersion }; if (_commander.default.balance) { (async () => { try { await _.default.getBalance(clientSettings); process.exit(0); } catch (error) { if (error.statusCode === 404) { console.error('Not found: Ensure you are using version 8.4 or above.'); } else { console.error(debug ? error : error.message || error); } process.exit(1); } })(); } else if (_commander.default.sourceMaps) { // Go, go, go download (async () => { try { await _.default.downloadSourceMaps(_objectSpread(_objectSpread({}, clientSettings), {}, { filesDest, filesSrc, protectionId: _commander.default.sourceMaps })); } catch (error) { console.error(debug ? error : error.message || error); process.exit(1); } })(); } else if (_commander.default.outputSymbolTable) { // Go, go, go download (async () => { try { await _.default.downloadSymbolTable(_objectSpread(_objectSpread({}, clientSettings), {}, { filesDest, filesSrc, protectionId: _commander.default.outputSymbolTable })); } catch (error) { console.error(debug ? error : error.message || error); process.exit(1); } })(); } else if (config.instrument) { _.default.instrumentAndDownload(_objectSpread(_objectSpread({}, clientSettings), {}, { applicationId, filesSrc, filesDest, skipSources, cwd })).catch(error => { console.error(debug ? error : error.message || error); process.exit(1); }); } else if (_commander.default.startProfiling) { _.default.setProfilingState(_objectSpread(_objectSpread({}, clientSettings), {}, { applicationId }), 'RUNNING', 'STARTED', "Exercise your application and when you're finished run *--stop-profiling* command").catch(error => { console.error(debug ? error : error.message || error); process.exit(1); }); } else if (_commander.default.stopProfiling) { _.default.setProfilingState(_objectSpread(_objectSpread({}, clientSettings), {}, { applicationId }), 'READY', 'STOPPED', 'Protect your application with 2 extra arguments: *--profiling-data-mode automatic* and *--skip-sources*').catch(error => { console.error(debug ? error : error.message || error); process.exit(1); }); } else if (_commander.default.protectionReport) { (async () => { try { await _.default.getProtectionMetadata(clientSettings, _commander.default.protectionReport, _commander.default.outputDir); } catch (error) { console.error(debug ? error : error.message || error); process.exit(1); } })(); } else { // Go, go, go (async () => { const protectAndDownloadOptions = _objectSpread(_objectSpread({}, clientSettings), {}, { applicationId, filesSrc, filesDest, params, applicationTypes, languageSpecifications, areSubscribersOrdered, cwd, sourceMaps, randomizationSeed, useRecommendedOrder, tolerateMinification, debugMode, codeHardeningThreshold, useProfilingData, profilingDataMode, browsers, useAppClassification, skipSources, removeProfilingData, inputSymbolTable, entryPoint, excludeList, ensureCodeAnnotation, numberOfProtections, forceAppEnvironment, beforeProtection, deleteProtectionOnSuccess, mode, saveSrc, globalNamesPrefix, useGlobalNamesOnModules, generateAlias, customLabels }); try { if (typeof werror !== 'undefined') { protectAndDownloadOptions.bail = werror; } await _.default.protectAndDownload(protectAndDownloadOptions); } catch (error) { console.error(debug ? error : error.message || error); process.exit(1); } })(); }