@dacely/toildefender 0.1.6 → 0.1.8
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/README.md +1 -1
- package/esutils.js +12 -8
- package/obfuscator.js +101 -12
- package/package.json +6 -6
- package/processors/deadCode.js +32 -0
- package/processors/flattener.js +73 -8
- package/processors/identifiers.js +26 -6
- package/processors/literals.js +82 -2
- package/processors/methods.js +47 -34
- package/processors/modules.js +2 -2
- package/processors/normalizer.js +435 -69
- package/processors/numericVm.js +270 -83
- package/processors/postprocessing.js +4 -4
- package/processors/preprocessing.js +11 -3
- package/processors/scopes.js +366 -50
- package/processors/uglifier.js +22 -2
- package/processors/variables.js +51 -8
package/README.md
CHANGED
|
@@ -158,7 +158,7 @@ function licenseGate(input) {
|
|
|
158
158
|
: { ok: false, total: total - 5 };
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
globalThis.__result = licenseGate("
|
|
161
|
+
globalThis.__result = licenseGate("ToilDefender");
|
|
162
162
|
```
|
|
163
163
|
|
|
164
164
|
The demo artifact is generated with every major protection enabled and
|
package/esutils.js
CHANGED
|
@@ -11,7 +11,7 @@ module.exports = function (logger) {
|
|
|
11
11
|
assert.ok(estest.isNode(node));
|
|
12
12
|
|
|
13
13
|
traverser.visitChildrenEx(node, (child, key) => {
|
|
14
|
-
Object.defineProperty(child, "
|
|
14
|
+
Object.defineProperty(child, "toildefender$parent", {
|
|
15
15
|
value: node,
|
|
16
16
|
configurable: true
|
|
17
17
|
});
|
|
@@ -23,7 +23,7 @@ module.exports = function (logger) {
|
|
|
23
23
|
assert.ok(estest.isNode(node));
|
|
24
24
|
|
|
25
25
|
traverser.visitChildrenEx(node, (child, key) => {
|
|
26
|
-
Object.defineProperty(child, "
|
|
26
|
+
Object.defineProperty(child, "toildefender$parent", {
|
|
27
27
|
value: node,
|
|
28
28
|
configurable: true
|
|
29
29
|
});
|
|
@@ -50,14 +50,14 @@ module.exports = function (logger) {
|
|
|
50
50
|
if (scope.block.body && (scope.block.body.type == "Program" || scope.block.body.type == "BlockStatement")) {
|
|
51
51
|
scope.block.body.body.splice(idx, 0, node);
|
|
52
52
|
|
|
53
|
-
Object.defineProperty(node, "
|
|
53
|
+
Object.defineProperty(node, "toildefender$parent", {
|
|
54
54
|
value: scope.block.body,
|
|
55
55
|
configurable: true
|
|
56
56
|
});
|
|
57
57
|
} else if (scope.block.type == "Program" || scope.block.type == "BlockStatement") {
|
|
58
58
|
scope.block.body.splice(idx, 0, node);
|
|
59
59
|
|
|
60
|
-
Object.defineProperty(node, "
|
|
60
|
+
Object.defineProperty(node, "toildefender$parent", {
|
|
61
61
|
value: scope.block,
|
|
62
62
|
configurable: true
|
|
63
63
|
});
|
|
@@ -74,12 +74,16 @@ module.exports = function (logger) {
|
|
|
74
74
|
assert.equal(estest.isExpression(child), estest.isExpression(replacement), `Replacee ${child.type} is not of the same type as replacement ${replacement.type}`);
|
|
75
75
|
|
|
76
76
|
var _this = this;
|
|
77
|
-
|
|
77
|
+
var parent = this.getParent(child);
|
|
78
|
+
if (parent && parent.type == "Property" && parent.shorthand === true && parent.value == child) {
|
|
79
|
+
parent.shorthand = false;
|
|
80
|
+
}
|
|
81
|
+
root = parent || root;
|
|
78
82
|
traverser.traverseEx(root, [], function (node, stack) {
|
|
79
83
|
if (node == child) {
|
|
80
84
|
this.abort();
|
|
81
|
-
Object.defineProperty(replacement, "
|
|
82
|
-
value: child.
|
|
85
|
+
Object.defineProperty(replacement, "toildefender$parent", {
|
|
86
|
+
value: child.toildefender$parent,
|
|
83
87
|
configurable: true
|
|
84
88
|
});
|
|
85
89
|
_this.setParents(replacement);
|
|
@@ -93,7 +97,7 @@ module.exports = function (logger) {
|
|
|
93
97
|
this.getParent = function (node) {
|
|
94
98
|
assert.ok(estest.isNode(node));
|
|
95
99
|
|
|
96
|
-
var parent = node.
|
|
100
|
+
var parent = node.toildefender$parent;
|
|
97
101
|
var legit = false;
|
|
98
102
|
if (parent) {
|
|
99
103
|
traverser.visitChildren(parent, child => {
|
package/obfuscator.js
CHANGED
|
@@ -76,6 +76,14 @@ var defaultOptions = {
|
|
|
76
76
|
},
|
|
77
77
|
virtualize: "marked"
|
|
78
78
|
},
|
|
79
|
+
controlFlow: {
|
|
80
|
+
ratio: 1,
|
|
81
|
+
seed: "toildefender-control-flow"
|
|
82
|
+
},
|
|
83
|
+
scope: {
|
|
84
|
+
ratio: 1,
|
|
85
|
+
seed: "toildefender-scope"
|
|
86
|
+
},
|
|
79
87
|
protections: {
|
|
80
88
|
virtualMachine: {
|
|
81
89
|
bigintBytecode: true,
|
|
@@ -111,6 +119,25 @@ var featureDeps = {
|
|
|
111
119
|
compress: [ "mangle" ]
|
|
112
120
|
};
|
|
113
121
|
|
|
122
|
+
function isNumericVmInternalNode(node) {
|
|
123
|
+
return node && node.toildefender$numericVmInternal === true;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function takeNumericVmInternalStatements(ast) {
|
|
127
|
+
if (!ast || ast.type != "Program") {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
var retained = [];
|
|
131
|
+
ast.body = ast.body.filter(statement => {
|
|
132
|
+
if (isNumericVmInternalNode(statement)) {
|
|
133
|
+
retained.push(statement);
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
return retained;
|
|
139
|
+
}
|
|
140
|
+
|
|
114
141
|
var featureDescs = {
|
|
115
142
|
dead_code: {
|
|
116
143
|
en: "Insert dead code"
|
|
@@ -365,7 +392,7 @@ exports.do = function (options) {
|
|
|
365
392
|
}
|
|
366
393
|
|
|
367
394
|
function hasMangleUnsupportedSyntax(root) {
|
|
368
|
-
return
|
|
395
|
+
return false;
|
|
369
396
|
}
|
|
370
397
|
|
|
371
398
|
function dispatcherForMethod(method) {
|
|
@@ -380,6 +407,35 @@ exports.do = function (options) {
|
|
|
380
407
|
}
|
|
381
408
|
return "main";
|
|
382
409
|
}
|
|
410
|
+
|
|
411
|
+
function normalizeRatio(value) {
|
|
412
|
+
var ratio = Number(value);
|
|
413
|
+
if (!Number.isFinite(ratio)) {
|
|
414
|
+
return 1;
|
|
415
|
+
}
|
|
416
|
+
if (ratio < 0) {
|
|
417
|
+
return 0;
|
|
418
|
+
}
|
|
419
|
+
if (ratio > 1) {
|
|
420
|
+
return 1;
|
|
421
|
+
}
|
|
422
|
+
return ratio;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function hashString32(value) {
|
|
426
|
+
var h = 0x811c9dc5;
|
|
427
|
+
for (var i = 0; i < value.length; i += 1) {
|
|
428
|
+
h ^= value.charCodeAt(i);
|
|
429
|
+
h = Math.imul(h, 0x01000193) >>> 0;
|
|
430
|
+
}
|
|
431
|
+
return h >>> 0;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function methodControlFlowScore(method, index) {
|
|
435
|
+
var name = method && method.id && method.id.name || "";
|
|
436
|
+
var seed = options.controlFlow && options.controlFlow.seed || "";
|
|
437
|
+
return hashString32(`${seed}:${index}:${name}`) / 0x100000000;
|
|
438
|
+
}
|
|
383
439
|
|
|
384
440
|
options = _.merge({}, defaultOptions, options); // first argument gets mutated
|
|
385
441
|
if (options.protections.virtualMachine.enabled) {
|
|
@@ -407,11 +463,19 @@ exports.do = function (options) {
|
|
|
407
463
|
} else {
|
|
408
464
|
options.features = options.forceFeatures;
|
|
409
465
|
}
|
|
466
|
+
var controlFlowRatio = normalizeRatio(options.controlFlow && options.controlFlow.ratio);
|
|
467
|
+
var controlFlowActive = options.features.control_flow && controlFlowRatio > 0;
|
|
468
|
+
var scopeRatio = normalizeRatio(options.scope && options.scope.ratio);
|
|
410
469
|
|
|
411
470
|
var parseOptions = {};
|
|
412
471
|
var scopeOptions = {
|
|
413
472
|
optimistic: true // required or things in the global scope just get lost
|
|
414
473
|
};
|
|
474
|
+
var lexicalScopeOptions = {
|
|
475
|
+
ecmaVersion: 6,
|
|
476
|
+
optimistic: true,
|
|
477
|
+
sourceType: "script"
|
|
478
|
+
};
|
|
415
479
|
|
|
416
480
|
var logger = new Logger(options.logAdapter);
|
|
417
481
|
var customBindAdded = false;
|
|
@@ -504,13 +568,17 @@ exports.do = function (options) {
|
|
|
504
568
|
var variables = new prVariables(logger);
|
|
505
569
|
variables.removeFunctionExpressionIds(ast);
|
|
506
570
|
variables.functionDeclarationToExpression(ast, escope.analyze(ast, scopeOptions));
|
|
507
|
-
variables.obfuscateIdentifiers(ast, escope.analyze(ast,
|
|
571
|
+
variables.obfuscateIdentifiers(ast, escope.analyze(ast, lexicalScopeOptions));
|
|
508
572
|
variables.redefineParameters(ast, escope.analyze(ast, scopeOptions));
|
|
509
573
|
});
|
|
510
574
|
|
|
511
575
|
// Move identifiers into scope objects
|
|
512
576
|
doTask("create_scope_objects", true, () => {
|
|
513
|
-
scopes.createScopeObjects(ast, escope.analyze(ast,
|
|
577
|
+
scopes.createScopeObjects(ast, escope.analyze(ast, lexicalScopeOptions), {
|
|
578
|
+
ratio: scopeRatio,
|
|
579
|
+
seed: options.scope && options.scope.seed || "toildefender-scope",
|
|
580
|
+
forceProgram: controlFlowActive
|
|
581
|
+
});
|
|
514
582
|
});
|
|
515
583
|
|
|
516
584
|
// Calculate entry points for all methods
|
|
@@ -526,7 +594,7 @@ exports.do = function (options) {
|
|
|
526
594
|
// Extract function declarations and expressions
|
|
527
595
|
var fns;
|
|
528
596
|
doTask("extract_methods", true, () => {
|
|
529
|
-
var scopeManager = escope.analyze(ast,
|
|
597
|
+
var scopeManager = escope.analyze(ast, lexicalScopeOptions);
|
|
530
598
|
fns = methods.extractMethods(ast);
|
|
531
599
|
fns = fns.map(method => {
|
|
532
600
|
var refers = methods.methodRefersToArguments(method, scopeManager);
|
|
@@ -538,15 +606,25 @@ exports.do = function (options) {
|
|
|
538
606
|
methodEntryPoints[method.id.name].dispatcher = dispatcherForMethod(method);
|
|
539
607
|
}
|
|
540
608
|
});
|
|
541
|
-
|
|
542
|
-
|
|
609
|
+
var selectedMethodEntryPoints = {};
|
|
610
|
+
fns.forEach((method, index) => {
|
|
611
|
+
if (!method || !method.id || !methodEntryPoints[method.id.name]) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
if (controlFlowRatio >= 1 || methodControlFlowScore(method, index) < controlFlowRatio) {
|
|
615
|
+
selectedMethodEntryPoints[method.id.name] = methodEntryPoints[method.id.name];
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
if (controlFlowActive) {
|
|
620
|
+
methods.replaceFunctionCalls(ast, selectedMethodEntryPoints);
|
|
543
621
|
fns.forEach(method => {
|
|
544
|
-
methods.replaceFunctionCalls(method.body,
|
|
622
|
+
methods.replaceFunctionCalls(method.body, selectedMethodEntryPoints);
|
|
545
623
|
});
|
|
546
624
|
}
|
|
547
625
|
});
|
|
548
626
|
|
|
549
|
-
doTask("control_flow",
|
|
627
|
+
doTask("control_flow", controlFlowActive, () => {
|
|
550
628
|
// Apply control flow flattening and merge methods
|
|
551
629
|
var flattener = new prFlattener(logger, rng);
|
|
552
630
|
var entry = rng.get(), exit = rng.get();
|
|
@@ -566,8 +644,15 @@ exports.do = function (options) {
|
|
|
566
644
|
}
|
|
567
645
|
};
|
|
568
646
|
var syncFns = [];
|
|
647
|
+
var retainedFns = [];
|
|
648
|
+
var retainedInternalFns = takeNumericVmInternalStatements(ast);
|
|
569
649
|
|
|
570
|
-
fns.forEach(method => {
|
|
650
|
+
fns.forEach((method, index) => {
|
|
651
|
+
var selected = controlFlowRatio >= 1 || methodControlFlowScore(method, index) < controlFlowRatio;
|
|
652
|
+
if (!selected) {
|
|
653
|
+
retainedFns.push(method);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
571
656
|
var dispatcher = dispatcherForMethod(method);
|
|
572
657
|
if (dispatcher == "main") {
|
|
573
658
|
syncFns.push(method);
|
|
@@ -623,19 +708,23 @@ exports.do = function (options) {
|
|
|
623
708
|
if (asyncPrograms.length > 0) {
|
|
624
709
|
ast = {
|
|
625
710
|
type: "Program",
|
|
626
|
-
body: Array.prototype.concat.apply([], asyncPrograms.map(program => program.body)).concat(syncAst.body)
|
|
711
|
+
body: retainedInternalFns.concat(retainedFns).concat(Array.prototype.concat.apply([], asyncPrograms.map(program => program.body)).concat(syncAst.body))
|
|
627
712
|
};
|
|
628
713
|
} else {
|
|
629
|
-
ast =
|
|
714
|
+
ast = {
|
|
715
|
+
type: "Program",
|
|
716
|
+
body: retainedInternalFns.concat(retainedFns).concat(syncAst.body)
|
|
717
|
+
};
|
|
630
718
|
}
|
|
631
719
|
})
|
|
632
720
|
.otherwise(() => {
|
|
721
|
+
var retainedInternalFns = takeNumericVmInternalStatements(ast);
|
|
633
722
|
if (ast.type == "Program") {
|
|
634
723
|
ast.type = "BlockStatement";
|
|
635
724
|
}
|
|
636
725
|
ast = {
|
|
637
726
|
type: "Program",
|
|
638
|
-
body: fns.concat([ ast ])
|
|
727
|
+
body: retainedInternalFns.concat(fns).concat([ ast ])
|
|
639
728
|
};
|
|
640
729
|
});
|
|
641
730
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dacely/toildefender",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "Modern JavaScript code protection, bytecode virtualization, and obfuscation for the Toil tech stack.",
|
|
5
5
|
"author": "Dacely",
|
|
6
6
|
"contributors": [
|
|
@@ -63,13 +63,13 @@
|
|
|
63
63
|
"dependencies": {
|
|
64
64
|
"@babel/parser": "^8.0.0",
|
|
65
65
|
"escodegen": "^2.1.0",
|
|
66
|
-
"escope": "
|
|
66
|
+
"escope": "^4.0.0",
|
|
67
67
|
"esprima": "^4.0.1",
|
|
68
68
|
"esshorten": "1.1.1",
|
|
69
|
-
"estraverse": "
|
|
70
|
-
"expr-eval": "
|
|
71
|
-
"lodash": "4.
|
|
72
|
-
"minimist": "1.2.
|
|
69
|
+
"estraverse": "^5.3.0",
|
|
70
|
+
"expr-eval-fork": "^3.0.3",
|
|
71
|
+
"lodash": "^4.18.1",
|
|
72
|
+
"minimist": "^1.2.8"
|
|
73
73
|
},
|
|
74
74
|
"engines": {
|
|
75
75
|
"node": ">=24.11.0",
|
package/processors/deadCode.js
CHANGED
|
@@ -13,6 +13,33 @@ function isClassMethodBody(stack) {
|
|
|
13
13
|
return stack.some(frame => frame.node.type == "MethodDefinition" || frame.node.type == "ClassBody");
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function containsLexicalDeclaration(node) {
|
|
17
|
+
if (
|
|
18
|
+
node.type == "ClassDeclaration" ||
|
|
19
|
+
node.type == "FunctionDeclaration" ||
|
|
20
|
+
(node.type == "VariableDeclaration" && node.kind != "var")
|
|
21
|
+
) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
var found = false;
|
|
26
|
+
traverser.traverseEx(node, [], function (child) {
|
|
27
|
+
if (child != node && estest.isFunction(child)) {
|
|
28
|
+
return child;
|
|
29
|
+
}
|
|
30
|
+
if (
|
|
31
|
+
child.type == "ClassDeclaration" ||
|
|
32
|
+
child.type == "FunctionDeclaration" ||
|
|
33
|
+
(child.type == "VariableDeclaration" && child.kind != "var")
|
|
34
|
+
) {
|
|
35
|
+
found = true;
|
|
36
|
+
this.abort();
|
|
37
|
+
}
|
|
38
|
+
return child;
|
|
39
|
+
});
|
|
40
|
+
return found;
|
|
41
|
+
}
|
|
42
|
+
|
|
16
43
|
module.exports = class DeadCode {
|
|
17
44
|
|
|
18
45
|
constructor (logger) {
|
|
@@ -41,6 +68,11 @@ module.exports = class DeadCode {
|
|
|
41
68
|
|
|
42
69
|
var varValue = _.sample(KEYWORDS);
|
|
43
70
|
|
|
71
|
+
var selected = node.body.slice(pos, pos + len);
|
|
72
|
+
if (selected.some(containsLexicalDeclaration)) {
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
|
|
44
76
|
var spliced = node.body.splice(pos, len);
|
|
45
77
|
node.body.splice(pos, 0,
|
|
46
78
|
{
|
package/processors/flattener.js
CHANGED
|
@@ -173,7 +173,7 @@ module.exports = class Flattener {
|
|
|
173
173
|
expression: {
|
|
174
174
|
type: "AssignmentExpression",
|
|
175
175
|
operator: "=",
|
|
176
|
-
left: { type: "Identifier", name: "
|
|
176
|
+
left: { type: "Identifier", name: "toildefender$tobethrown" },
|
|
177
177
|
right: { type: "Literal", value: null }
|
|
178
178
|
}
|
|
179
179
|
},
|
|
@@ -229,7 +229,7 @@ module.exports = class Flattener {
|
|
|
229
229
|
declarations: [
|
|
230
230
|
{
|
|
231
231
|
type: "VariableDeclarator",
|
|
232
|
-
id: { type: "Identifier", name: "
|
|
232
|
+
id: { type: "Identifier", name: "toildefender$tobethrown" },
|
|
233
233
|
init: null
|
|
234
234
|
}
|
|
235
235
|
]
|
|
@@ -742,7 +742,7 @@ module.exports = class Flattener {
|
|
|
742
742
|
expression: {
|
|
743
743
|
type: "AssignmentExpression",
|
|
744
744
|
operator: "=",
|
|
745
|
-
left: node.handler.
|
|
745
|
+
left: node.handler.toildefender$exception,
|
|
746
746
|
right: { type: "Identifier", name: "e" }
|
|
747
747
|
}
|
|
748
748
|
},
|
|
@@ -777,16 +777,81 @@ module.exports = class Flattener {
|
|
|
777
777
|
* @returns {Node}
|
|
778
778
|
*/
|
|
779
779
|
unifyPrefixStatements (ast) {
|
|
780
|
+
var scopeObjects = new Map();
|
|
780
781
|
var maximumScopeIndex = 0;
|
|
782
|
+
|
|
783
|
+
function scopeNameFromReference(node) {
|
|
784
|
+
if (
|
|
785
|
+
node &&
|
|
786
|
+
node.type == "MemberExpression" &&
|
|
787
|
+
node.object &&
|
|
788
|
+
node.object.type == "Identifier" &&
|
|
789
|
+
_.startsWith(node.object.name, "$$scope") &&
|
|
790
|
+
node.property &&
|
|
791
|
+
node.property.type == "Literal" &&
|
|
792
|
+
typeof node.property.value == "number"
|
|
793
|
+
) {
|
|
794
|
+
return node.object.name;
|
|
795
|
+
}
|
|
796
|
+
return null;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function ensureScopeObject(name) {
|
|
800
|
+
if (!scopeObjects.has(name)) {
|
|
801
|
+
scopeObjects.set(name, {
|
|
802
|
+
max: -1,
|
|
803
|
+
offset: 0
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
return scopeObjects.get(name);
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
traverser.traverse(ast, [], (node, stack) => {
|
|
810
|
+
if (node.toildefender$scopeObject) {
|
|
811
|
+
var declaration = node.declarations && node.declarations[0];
|
|
812
|
+
if (declaration && declaration.id && declaration.id.type == "Identifier") {
|
|
813
|
+
ensureScopeObject(declaration.id.name);
|
|
814
|
+
}
|
|
815
|
+
} else if (node.toildefender$scopeObjectReference) {
|
|
816
|
+
var name = scopeNameFromReference(node);
|
|
817
|
+
if (name) {
|
|
818
|
+
var info = ensureScopeObject(name);
|
|
819
|
+
info.max = Math.max(info.max, node.property.value);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return node;
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
if (scopeObjects.size > 1) {
|
|
826
|
+
return ast;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
var nextScopeOffset = 0;
|
|
830
|
+
scopeObjects.forEach(info => {
|
|
831
|
+
info.offset = nextScopeOffset;
|
|
832
|
+
nextScopeOffset += info.max + 1;
|
|
833
|
+
});
|
|
781
834
|
|
|
782
835
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
783
|
-
if (node.
|
|
836
|
+
if (node.toildefender$reassigningArguments && !node.toildefender$followsSlicingArguments) {
|
|
784
837
|
node = { type: "EmptyStatement" };
|
|
785
|
-
} else if (node.
|
|
838
|
+
} else if (node.toildefender$scopeObject) {
|
|
786
839
|
node = { type: "EmptyStatement" };
|
|
787
|
-
} else if (node.
|
|
788
|
-
|
|
840
|
+
} else if (node.toildefender$scopeObjectReference) {
|
|
841
|
+
var name = scopeNameFromReference(node);
|
|
842
|
+
var info = name ? scopeObjects.get(name) : null;
|
|
843
|
+
if (info) {
|
|
844
|
+
node.property.value += info.offset;
|
|
845
|
+
maximumScopeIndex = Math.max(maximumScopeIndex, node.property.value);
|
|
846
|
+
}
|
|
847
|
+
if (node.object && node.object.type == "Identifier") {
|
|
848
|
+
node.object.name = "$$unifiedScope";
|
|
849
|
+
}
|
|
789
850
|
} else if (node.type == "Identifier" && _.startsWith(node.name, "$$scope")) {
|
|
851
|
+
var parent = stack[1] && stack[1].node;
|
|
852
|
+
if (parent && parent.toildefender$scopeObjectReference) {
|
|
853
|
+
return node;
|
|
854
|
+
}
|
|
790
855
|
node.name = "$$unifiedScope";
|
|
791
856
|
}
|
|
792
857
|
return node;
|
|
@@ -819,7 +884,7 @@ module.exports = class Flattener {
|
|
|
819
884
|
declarations: [
|
|
820
885
|
{
|
|
821
886
|
type: "VariableDeclarator",
|
|
822
|
-
id: { type: "Identifier", name: "
|
|
887
|
+
id: { type: "Identifier", name: "toildefender$arguments" },
|
|
823
888
|
init: { type: "Identifier", name: "arguments" }
|
|
824
889
|
}
|
|
825
890
|
]
|
|
@@ -33,6 +33,17 @@ function isBigIntLiteral(node) {
|
|
|
33
33
|
return node.type == "Literal" && typeof node.value == "bigint";
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
function canMoveLiteral(node) {
|
|
37
|
+
if (node.type != "Literal" || isBigIntLiteral(node) || node.regex) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return typeof node.value == "string";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isNumericVmInternalFunction(stack) {
|
|
44
|
+
return stack.some(frame => frame.node && frame.node.toildefender$numericVmInternal === true);
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
module.exports = class Identifiers {
|
|
37
48
|
|
|
38
49
|
constructor (logger) {
|
|
@@ -65,6 +76,9 @@ module.exports = class Identifiers {
|
|
|
65
76
|
assert.ok(estest.isNode(ast));
|
|
66
77
|
|
|
67
78
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
79
|
+
if (isNumericVmInternalFunction(stack)) {
|
|
80
|
+
return node;
|
|
81
|
+
}
|
|
68
82
|
if (node.type == "MemberExpression"
|
|
69
83
|
&& !node.computed) {
|
|
70
84
|
assert(node.property.type == "Identifier");
|
|
@@ -79,7 +93,7 @@ module.exports = class Identifiers {
|
|
|
79
93
|
}
|
|
80
94
|
|
|
81
95
|
/**
|
|
82
|
-
* Replace objects with an array via
|
|
96
|
+
* Replace objects with an array via toildefender$toObject.
|
|
83
97
|
* @param {Node} ast Root node
|
|
84
98
|
* @returns {Node} Root node
|
|
85
99
|
*/
|
|
@@ -88,6 +102,9 @@ module.exports = class Identifiers {
|
|
|
88
102
|
options = options || {};
|
|
89
103
|
|
|
90
104
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
105
|
+
if (isNumericVmInternalFunction(stack)) {
|
|
106
|
+
return node;
|
|
107
|
+
}
|
|
91
108
|
if (node.type == "ObjectExpression") {
|
|
92
109
|
if (options.objectPacking === false) {
|
|
93
110
|
return node;
|
|
@@ -108,7 +125,7 @@ module.exports = class Identifiers {
|
|
|
108
125
|
|
|
109
126
|
return {
|
|
110
127
|
type: "CallExpression",
|
|
111
|
-
callee: { type: "Identifier", name: "
|
|
128
|
+
callee: { type: "Identifier", name: "toildefender$toObject" },
|
|
112
129
|
arguments: [
|
|
113
130
|
literal(String(utils.hash(schema.join(",")))),
|
|
114
131
|
{
|
|
@@ -198,7 +215,7 @@ module.exports = class Identifiers {
|
|
|
198
215
|
}
|
|
199
216
|
|
|
200
217
|
/**
|
|
201
|
-
* Move all literals into the
|
|
218
|
+
* Move all literals into the toildefender$literals array.
|
|
202
219
|
* @param {Node} ast Root node
|
|
203
220
|
* @param {ScopeManager} scopeManager Scope manager
|
|
204
221
|
* @returns {Node} Root node
|
|
@@ -211,7 +228,10 @@ module.exports = class Identifiers {
|
|
|
211
228
|
var vars = [];
|
|
212
229
|
|
|
213
230
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
214
|
-
if (
|
|
231
|
+
if (isNumericVmInternalFunction(stack)) {
|
|
232
|
+
return node;
|
|
233
|
+
}
|
|
234
|
+
if (canMoveLiteral(node) && stack.length > 0 && stack[1].node.type != "Property") {
|
|
215
235
|
var idx = vars.indexOf(node.value);
|
|
216
236
|
if (idx == -1) {
|
|
217
237
|
idx = vars.length;
|
|
@@ -220,7 +240,7 @@ module.exports = class Identifiers {
|
|
|
220
240
|
|
|
221
241
|
return {
|
|
222
242
|
type: "MemberExpression",
|
|
223
|
-
object: { type: "Identifier", name: "
|
|
243
|
+
object: { type: "Identifier", name: "toildefender$literals" },
|
|
224
244
|
property: { type: "Literal", value: idx },
|
|
225
245
|
computed: true
|
|
226
246
|
};
|
|
@@ -235,7 +255,7 @@ module.exports = class Identifiers {
|
|
|
235
255
|
declarations: [
|
|
236
256
|
{
|
|
237
257
|
type: "VariableDeclarator",
|
|
238
|
-
id: { type: "Identifier", name: "
|
|
258
|
+
id: { type: "Identifier", name: "toildefender$literals" },
|
|
239
259
|
init: {
|
|
240
260
|
type: "ArrayExpression",
|
|
241
261
|
elements: vars.map(x => ({ type: "Literal", value: x }))
|
package/processors/literals.js
CHANGED
|
@@ -147,11 +147,79 @@ function makeStringByteArrayCall(str) {
|
|
|
147
147
|
|
|
148
148
|
return {
|
|
149
149
|
type: "CallExpression",
|
|
150
|
-
callee: { type: "Identifier", name: "
|
|
150
|
+
callee: { type: "Identifier", name: "toildefender$fromCharCodes" },
|
|
151
151
|
arguments: str.split("").map(x => ({ type: "Literal", value: x.charCodeAt() }))
|
|
152
152
|
};
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
function isUnencodedPropertyKey(stack) {
|
|
156
|
+
var parentFrame = stack[1];
|
|
157
|
+
if (!parentFrame || parentFrame.node.type != "Property") {
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
return parentFrame.key == "key" && parentFrame.node.computed !== true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function isNumericVmInternalFunction(stack) {
|
|
164
|
+
return stack.some(frame => frame.node && frame.node.toildefender$numericVmInternal === true);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function makeStringExpression(str) {
|
|
168
|
+
if (str.length == 0) {
|
|
169
|
+
return { type: "Literal", value: "" };
|
|
170
|
+
}
|
|
171
|
+
return makeStringGenerator(str);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function makeStringCallExpression(expr) {
|
|
175
|
+
return {
|
|
176
|
+
type: "CallExpression",
|
|
177
|
+
callee: { type: "Identifier", name: "String" },
|
|
178
|
+
arguments: [expr]
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function concatExpressions(left, right) {
|
|
183
|
+
return {
|
|
184
|
+
type: "BinaryExpression",
|
|
185
|
+
operator: "+",
|
|
186
|
+
left: left,
|
|
187
|
+
right: right
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function makeTemplateExpression(node) {
|
|
192
|
+
assert.equal(node.type, "TemplateLiteral");
|
|
193
|
+
|
|
194
|
+
var expression;
|
|
195
|
+
for (var i = 0; i < node.quasis.length; i += 1) {
|
|
196
|
+
var quasi = node.quasis[i];
|
|
197
|
+
var cooked = quasi.value && typeof quasi.value.cooked == "string" ? quasi.value.cooked : "";
|
|
198
|
+
var quasiExpression = makeStringExpression(cooked);
|
|
199
|
+
expression = expression ? concatExpressions(expression, quasiExpression) : quasiExpression;
|
|
200
|
+
|
|
201
|
+
if (i < node.expressions.length) {
|
|
202
|
+
expression = concatExpressions(expression, makeStringCallExpression(node.expressions[i]));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return expression || { type: "Literal", value: "" };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function makeRegexExpression(node) {
|
|
210
|
+
assert.equal(node.type, "Literal");
|
|
211
|
+
assert.ok(node.regex);
|
|
212
|
+
|
|
213
|
+
return {
|
|
214
|
+
type: "NewExpression",
|
|
215
|
+
callee: { type: "Identifier", name: "RegExp" },
|
|
216
|
+
arguments: [
|
|
217
|
+
makeStringExpression(node.regex.pattern || ""),
|
|
218
|
+
makeStringExpression(node.regex.flags || "")
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
155
223
|
module.exports = class Literals {
|
|
156
224
|
|
|
157
225
|
constructor (logger) {
|
|
@@ -172,6 +240,9 @@ module.exports = class Literals {
|
|
|
172
240
|
var stringMap = {};
|
|
173
241
|
|
|
174
242
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
243
|
+
if (isNumericVmInternalFunction(stack)) {
|
|
244
|
+
return node;
|
|
245
|
+
}
|
|
175
246
|
if (node.type == "Literal" && typeof node.value == "string") {
|
|
176
247
|
var idx = stringMap["_" + node.value];
|
|
177
248
|
if (!idx) {
|
|
@@ -217,10 +288,19 @@ module.exports = class Literals {
|
|
|
217
288
|
assert.ok(estest.isNode(ast));
|
|
218
289
|
|
|
219
290
|
ast = traverser.traverse(ast, [], (node, stack) => {
|
|
291
|
+
if (isNumericVmInternalFunction(stack)) {
|
|
292
|
+
return node;
|
|
293
|
+
}
|
|
294
|
+
if (node.type == "TemplateLiteral") {
|
|
295
|
+
return makeTemplateExpression(node);
|
|
296
|
+
}
|
|
297
|
+
if (node.type == "Literal" && node.regex) {
|
|
298
|
+
return makeRegexExpression(node);
|
|
299
|
+
}
|
|
220
300
|
if (node.type == "Literal"
|
|
221
301
|
&& typeof node.value == "string"
|
|
222
302
|
&& stack.length > 1
|
|
223
|
-
&& stack
|
|
303
|
+
&& !isUnencodedPropertyKey(stack)) {
|
|
224
304
|
return makeStringGenerator(node.value);
|
|
225
305
|
}
|
|
226
306
|
|