jscrambler
Version:
Jscrambler Code Integrity API client.
1,289 lines (1,285 loc) • 47.6 kB
JavaScript
;
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)
}));
}