jscrambler 6.4.27 → 6.4.28

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,11 @@
1
1
  # jscrambler
2
2
 
3
+ ## 6.4.28
4
+
5
+ ### Patch Changes
6
+
7
+ - [3d3cfc6]: Possibility to append or prepend scripts to specific files
8
+
3
9
  ## 6.4.27
4
10
 
5
11
  ### 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,6 +53,61 @@ const validateForceAppEnvironment = env => {
53
53
  }
54
54
  return normalizeEnvironment;
55
55
  };
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
+ };
56
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.").parse(process.argv);
57
112
  let globSrc, filesSrc, config;
58
113
 
@@ -125,6 +180,9 @@ 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
+ }
128
186
  globSrc = config.filesSrc;
129
187
  // If src paths have been provided
130
188
  if (_commander.default.args.length > 0) {
@@ -203,7 +261,8 @@ const {
203
261
  excludeList,
204
262
  numberOfProtections,
205
263
  ensureCodeAnnotation,
206
- forceAppEnvironment
264
+ forceAppEnvironment,
265
+ beforeProtection
207
266
  } = config;
208
267
  const params = config.params;
209
268
  const incompatibleOptions = ['sourceMaps', 'instrument', 'startProfiling', 'stopProfiling'];
@@ -313,7 +372,8 @@ if (_commander.default.sourceMaps) {
313
372
  excludeList,
314
373
  ensureCodeAnnotation,
315
374
  numberOfProtections,
316
- forceAppEnvironment
375
+ forceAppEnvironment,
376
+ beforeProtection
317
377
  });
318
378
  try {
319
379
  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');
@@ -278,6 +288,7 @@ var _default = exports.default = {
278
288
  });
279
289
  let filesSrc = finalConfig.filesSrc;
280
290
  let filesDest = finalConfig.filesDest;
291
+ let runBeforeProtection = finalConfig.beforeProtection;
281
292
  if (sources) {
282
293
  filesSrc = undefined;
283
294
  }
@@ -317,7 +328,8 @@ var _default = exports.default = {
317
328
  sources,
318
329
  filesSrc,
319
330
  cwd,
320
- appProfiling
331
+ appProfiling,
332
+ runBeforeProtection
321
333
  });
322
334
  } else {
323
335
  console.log('Update source files SKIPPED');
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,7 +1,7 @@
1
1
  {
2
2
  "name": "jscrambler",
3
3
  "description": "Jscrambler API client.",
4
- "version": "6.4.27",
4
+ "version": "6.4.28",
5
5
  "homepage": "https://github.com/jscrambler/jscrambler",
6
6
  "author": "Jscrambler <support@jscrambler.com>",
7
7
  "repository": {