jscrambler 6.4.26 → 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 +12 -0
- package/README.md +23 -0
- package/dist/bin/jscrambler.js +62 -2
- package/dist/index.js +16 -4
- package/dist/utils.js +56 -0
- package/dist/zip.js +5 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
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
|
+
|
|
9
|
+
## 6.4.27
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [c346840]: Use private key for tagging
|
|
14
|
+
|
|
3
15
|
## 6.4.26
|
|
4
16
|
|
|
5
17
|
### 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,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
|
-
|
|
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