jscrambler 6.4.27 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # jscrambler
2
2
 
3
+ ## 7.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - [26fdf6e]: Addition of a new flag `--delete-protection-on-success` that allows for the deletion of a protection after it has been run and was successful.
8
+ By default `--delete-protection-on-success` is set to `false` and must be explicitly set to `true`.
9
+
10
+ This flag was added in order to delete successful protections after the files were downloaded: protections might not be needed anymore after being used one single time and will take up space unnecessarily.
11
+
12
+ ### Patch Changes
13
+
14
+ - [6758a7f]: Better package metadata. This may assist tooling like Renovate bot.
15
+
16
+ ## 6.4.28
17
+
18
+ ### Patch Changes
19
+
20
+ - [3d3cfc6]: Possibility to append or prepend scripts to specific files
21
+
3
22
  ## 6.4.27
4
23
 
5
24
  ### Patch Changes
package/README.md CHANGED
@@ -331,6 +331,29 @@ Instrument is used when you want to [Profile](https://docs.jscrambler.com/code-i
331
331
 
332
332
  **WARNING:** DO NOT SEND THIS CODE TO PRODUCTION AS IT IS NOT PROTECTED
333
333
 
334
+ ## Javascript Appending and Prepending
335
+
336
+ This option is available in your configuration file and allows for Javascript files to be appended or prepended to specific files before protecting your code.
337
+ It allows for multiple files to be affixed or prefixed with another file, without changing the original content of the file and more than one script can act on the same file - you can both append and prepend the same JS file with the desired scripts on the same protection.
338
+ The concatenation of files can result in max file size errors - even though the original file may be under the max limit, the result of the concatenation may exceed this threshold.
339
+
340
+ ```json
341
+ {
342
+ "beforeProtection": [
343
+ {
344
+ "type": "append-js",
345
+ "target": "/path/to/target/file.js",
346
+ "source": "/path/to/script/file.js"
347
+ },
348
+ {
349
+ "type": "prepend-js",
350
+ "target": "/path/to/target/file.js",
351
+ "source": "/path/to/script/file.js"
352
+ }
353
+ ]
354
+ }
355
+ ```
356
+
334
357
  ## Symbol Table
335
358
 
336
359
  Jscrambler can import symbol tables to ensure certain global variables and object properties have specific names.
@@ -53,7 +53,62 @@ const validateForceAppEnvironment = env => {
53
53
  }
54
54
  return normalizeEnvironment;
55
55
  };
56
- _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.").parse(process.argv);
56
+ const validateBeforeProtection = function () {
57
+ let beforeProtectionArray = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
58
+ if (beforeProtectionArray.length === 0) {
59
+ return;
60
+ }
61
+ const mandatoryKeys = ['type', 'target', 'source'];
62
+ const usedTargets = new Set();
63
+ const usedSources = new Set();
64
+ beforeProtectionArray.filter(element => {
65
+ // Check if every array element has a type, a target and a source
66
+ const validateMandatoryKeys = mandatoryKeys.every(key => key in element);
67
+ if (!validateMandatoryKeys) {
68
+ console.error('Invalid structure on beforeProtection: each element must have the following structure { type: "type", target: "/path/to/target", source: "/path/to/script"}');
69
+ process.exit(1);
70
+ }
71
+ const {
72
+ target,
73
+ source,
74
+ type
75
+ } = element;
76
+
77
+ // Check if only valid types are being used
78
+ if (type !== _utils.APPEND_JS_TYPE && type !== _utils.PREPEND_JS_TYPE) {
79
+ console.error("Invalid type on beforeProtection: only \"".concat(_utils.APPEND_JS_TYPE, "\" or \"").concat(_utils.PREPEND_JS_TYPE, "\" are allowed."));
80
+ process.exit(1);
81
+ }
82
+
83
+ // Check if the provided files are js, mjs or cjs files
84
+ if (!(0, _utils.isJavascriptFile)(target) || !(0, _utils.isJavascriptFile)(source)) {
85
+ console.error('Invalid extension for beforeProtection target or source files: only *js, mjs and cjs* files can be used to append or prepend.');
86
+ process.exit(1);
87
+ }
88
+
89
+ // Check if the target has already been used as a source
90
+ if (usedTargets.has(source)) {
91
+ console.error("Error on beforeProtection: file \"".concat(source, "\" has already been used as target and can't be used as source."));
92
+ process.exit(1);
93
+ }
94
+ if (usedSources.has(target)) {
95
+ console.error("Error on beforeProtection: file \"".concat(target, "\" has already been used as source and can't be used as target."));
96
+ process.exit(1);
97
+ }
98
+
99
+ // Check if the target and source are the same
100
+ if (target === source) {
101
+ console.error("Error on beforeProtection: File \"".concat(target, "\" can't be used as both a target and a source."));
102
+ process.exit(1);
103
+ }
104
+
105
+ // Add the target and the source to the corresponding sets
106
+ usedTargets.add(target);
107
+ usedSources.add(source);
108
+ });
109
+ return beforeProtectionArray;
110
+ };
111
+ _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')).parse(process.argv);
57
112
  let globSrc, filesSrc, config;
58
113
 
59
114
  // If -c, --config file was provided
@@ -125,6 +180,12 @@ if (config.codeHardeningThreshold) {
125
180
  if (config.profilingDataMode) {
126
181
  config.profilingDataMode = validateProfilingDataMode(config.profilingDataMode);
127
182
  }
183
+ if (config.beforeProtection) {
184
+ config.beforeProtection = validateBeforeProtection(config.beforeProtection);
185
+ }
186
+ if (_commander.default.deleteProtectionOnSuccess) {
187
+ config.deleteProtectionOnSuccess = _commander.default.deleteProtectionOnSuccess === 'true';
188
+ }
128
189
  globSrc = config.filesSrc;
129
190
  // If src paths have been provided
130
191
  if (_commander.default.args.length > 0) {
@@ -203,7 +264,9 @@ const {
203
264
  excludeList,
204
265
  numberOfProtections,
205
266
  ensureCodeAnnotation,
206
- forceAppEnvironment
267
+ forceAppEnvironment,
268
+ beforeProtection,
269
+ deleteProtectionOnSuccess
207
270
  } = config;
208
271
  const params = config.params;
209
272
  const incompatibleOptions = ['sourceMaps', 'instrument', 'startProfiling', 'stopProfiling'];
@@ -313,7 +376,9 @@ if (_commander.default.sourceMaps) {
313
376
  excludeList,
314
377
  ensureCodeAnnotation,
315
378
  numberOfProtections,
316
- forceAppEnvironment
379
+ forceAppEnvironment,
380
+ beforeProtection,
381
+ deleteProtectionOnSuccess
317
382
  });
318
383
  try {
319
384
  if (typeof werror !== 'undefined') {
package/dist/index.js CHANGED
@@ -112,7 +112,8 @@ var _default = exports.default = {
112
112
  * sources: Array.<{filename: string, content: string}>,
113
113
  * filesSrc: Array.<string>,
114
114
  * cwd: string,
115
- * appProfiling: ?object
115
+ * appProfiling: ?object,
116
+ * runBeforeProtection?: Array<{type: string, target: string, source: string }>
116
117
  * }} opts
117
118
  * @returns {Promise<{extension: string, filename: string, content: *}>}
118
119
  */
@@ -121,7 +122,8 @@ var _default = exports.default = {
121
122
  sources,
122
123
  filesSrc,
123
124
  cwd,
124
- appProfiling
125
+ appProfiling,
126
+ runBeforeProtection = []
125
127
  } = _ref;
126
128
  if (sources || filesSrc && filesSrc.length) {
127
129
  // prevent removing sources if profiling state is READY
@@ -145,7 +147,15 @@ var _default = exports.default = {
145
147
  if (debug) {
146
148
  console.log('Creating zip from source files');
147
149
  }
148
- zipped = await (0, _zip.zip)(_filesSrc, cwd);
150
+ if (runBeforeProtection.length > 0) {
151
+ runBeforeProtection.map(element => {
152
+ if (!_filesSrc.includes(element.target)) {
153
+ console.error('Error on beforeProtection: Target files need to be in the files to protect list (or filesSrc).');
154
+ process.exit(1);
155
+ }
156
+ });
157
+ }
158
+ zipped = await (0, _zip.zip)(_filesSrc, cwd, runBeforeProtection);
149
159
  } else if (sources) {
150
160
  if (debug) {
151
161
  console.log('Creating zip from sources');
@@ -257,7 +267,8 @@ var _default = exports.default = {
257
267
  excludeList,
258
268
  numberOfProtections,
259
269
  ensureCodeAnnotation,
260
- forceAppEnvironment
270
+ forceAppEnvironment,
271
+ deleteProtectionOnSuccess
261
272
  } = finalConfig;
262
273
  const {
263
274
  accessKey,
@@ -278,6 +289,7 @@ var _default = exports.default = {
278
289
  });
279
290
  let filesSrc = finalConfig.filesSrc;
280
291
  let filesDest = finalConfig.filesDest;
292
+ let runBeforeProtection = finalConfig.beforeProtection;
281
293
  if (sources) {
282
294
  filesSrc = undefined;
283
295
  }
@@ -317,7 +329,8 @@ var _default = exports.default = {
317
329
  sources,
318
330
  filesSrc,
319
331
  cwd,
320
- appProfiling
332
+ appProfiling,
333
+ runBeforeProtection
321
334
  });
322
335
  } else {
323
336
  console.log('Update source files SKIPPED');
@@ -466,6 +479,15 @@ var _default = exports.default = {
466
479
  if (printProtectionId) {
467
480
  console.log(protection._id);
468
481
  }
482
+
483
+ // change this to have the variable that checks if the protection is to be removed
484
+ if (deleteProtectionOnSuccess) {
485
+ _this.removeProtection(client, protection._id, applicationId).then(() => {
486
+ if (debug) {
487
+ console.log('Protection has been successful and will now be deleted');
488
+ }
489
+ }).catch(error => console.error(error));
490
+ }
469
491
  return protection._id;
470
492
  };
471
493
  if (processedProtections.length === 1) {
package/dist/utils.js CHANGED
@@ -3,10 +3,15 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.PREPEND_JS_TYPE = exports.APPEND_JS_TYPE = void 0;
7
+ exports.concatenate = concatenate;
6
8
  exports.getMatchedFiles = getMatchedFiles;
9
+ exports.isJavascriptFile = isJavascriptFile;
7
10
  exports.validateNProtections = validateNProtections;
8
11
  var _glob = _interopRequireDefault(require("glob"));
9
12
  var _fs = _interopRequireDefault(require("fs"));
13
+ var _fsExtra = require("fs-extra");
14
+ var _path = require("path");
10
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
16
  /**
12
17
  * Return the list of matched files for minimatch patterns.
@@ -34,4 +39,55 @@ function validateNProtections(n) {
34
39
  process.exit(1);
35
40
  }
36
41
  return nProtections;
42
+ }
43
+ const APPEND_JS_TYPE = exports.APPEND_JS_TYPE = 'append-js';
44
+ const PREPEND_JS_TYPE = exports.PREPEND_JS_TYPE = 'prepend-js';
45
+
46
+ /**
47
+ *
48
+ * @param {*} firstFile if prepending: script file; if appending: target file.
49
+ * @param {*} secondFile if prepending: target file; if appending: script file.
50
+ * @returns first and second files concatenated
51
+ */
52
+ function handleScriptConcatenation(firstFile, secondFile) {
53
+ const firstFileContent = firstFile.toString('utf-8');
54
+ const secondFileContent = secondFile.toString('utf-8');
55
+ const concatenatedContent = firstFileContent + "\n" + secondFileContent;
56
+ return concatenatedContent;
57
+ }
58
+
59
+ /**
60
+ *
61
+ * @param {*} scriptObject the object with the script content: { target: '/path/to/target/file', source: '/path/to/script/file', type: 'append-js' | 'prepend-js' }. Its used for both appending and prepending.
62
+ * @param {*} cwd current working directory, passed by argument
63
+ * @param {*} path file path (file being parsed)
64
+ * @param {*} buffer file contents
65
+ */
66
+ function concatenate(scriptObject, cwd, path, buffer) {
67
+ let {
68
+ target
69
+ } = scriptObject;
70
+ if (cwd) {
71
+ target = (0, _path.join)(cwd, target);
72
+ }
73
+ target = (0, _path.normalize)(target);
74
+ if (target === path) {
75
+ const {
76
+ source,
77
+ type
78
+ } = scriptObject;
79
+ if (!(0, _fsExtra.existsSync)(source)) {
80
+ throw new Error('Provided script file does not exist');
81
+ }
82
+ const fileContent = (0, _fsExtra.readFileSync)(target);
83
+ const scriptContent = (0, _fsExtra.readFileSync)(source);
84
+ const concatContent = type === APPEND_JS_TYPE ? handleScriptConcatenation(fileContent, scriptContent) : handleScriptConcatenation(scriptContent, fileContent);
85
+ buffer = Buffer.from(concatContent, 'utf-8');
86
+ }
87
+ return buffer;
88
+ }
89
+ function isJavascriptFile(filename) {
90
+ const fileExtension = (0, _path.extname)(filename);
91
+ const validJsFileExtensions = ['.js', '.mjs', '.cjs'];
92
+ return validJsFileExtensions.includes(fileExtension);
37
93
  }
package/dist/zip.js CHANGED
@@ -21,6 +21,7 @@ var _fsExtra = require("fs-extra");
21
21
  var _path2 = require("path");
22
22
  var _q = require("q");
23
23
  var _util = require("util");
24
+ var _utils = require("./utils");
24
25
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
25
26
  // TODO Replace `sync` functions with async versions
26
27
 
@@ -28,7 +29,7 @@ const debug = !!process.env.DEBUG;
28
29
 
29
30
  // ./zip.js module is excluded from browser-like environments. We take advantage of that here.
30
31
 
31
- async function zip(files, cwd) {
32
+ async function zip(files, cwd, runBeforeProtection) {
32
33
  debug && console.log('Zipping files', (0, _util.inspect)(files));
33
34
  const deferred = (0, _q.defer)();
34
35
  // Flag to detect if any file was added to the zip archive
@@ -77,6 +78,9 @@ async function zip(files, cwd) {
77
78
  name = files[i];
78
79
  }
79
80
  buffer = (0, _fsExtra.readFileSync)(sPath);
81
+ runBeforeProtection.map(element => {
82
+ buffer = (0, _utils.concatenate)(element, cwd, sPath, buffer);
83
+ });
80
84
  } else {
81
85
  // Else if it's a directory path
82
86
  zip.folder(sPath);
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "jscrambler",
3
3
  "description": "Jscrambler API client.",
4
- "version": "6.4.27",
4
+ "version": "7.0.0",
5
5
  "homepage": "https://github.com/jscrambler/jscrambler",
6
6
  "author": "Jscrambler <support@jscrambler.com>",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/jscrambler/jscrambler.git"
9
+ "url": "https://github.com/jscrambler/jscrambler.git",
10
+ "directory": "packages/jscrambler-cli"
10
11
  },
11
12
  "bugs": {
12
13
  "url": "https://github.com/jscrambler/jscrambler/issues"