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 +19 -0
- package/README.md +23 -0
- package/dist/bin/jscrambler.js +68 -3
- package/dist/index.js +27 -5
- package/dist/utils.js +56 -0
- package/dist/zip.js +5 -1
- package/package.json +3 -2
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.
|
package/dist/bin/jscrambler.js
CHANGED
|
@@ -53,7 +53,62 @@ const validateForceAppEnvironment = env => {
|
|
|
53
53
|
}
|
|
54
54
|
return normalizeEnvironment;
|
|
55
55
|
};
|
|
56
|
-
|
|
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
|
-
|
|
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": "
|
|
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"
|