jscrambler
Version:
Jscrambler Code Integrity API client.
470 lines (459 loc) • 23.1 kB
JavaScript
;
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);
}
})();
}