@dacely/toildefender 0.1.5 → 0.1.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dacely/toildefender",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Modern JavaScript code protection, bytecode virtualization, and obfuscation for the Toil tech stack.",
5
5
  "author": "Dacely",
6
6
  "contributors": [
@@ -46,8 +46,10 @@ function blockToArray (node) {
46
46
 
47
47
  if (Array.isArray(node.body)) {
48
48
  return node.body;
49
- } else {
49
+ } else if (node.body) {
50
50
  return [ node.body ];
51
+ } else {
52
+ return [ node ];
51
53
  }
52
54
  }
53
55
 
@@ -376,6 +378,28 @@ function containsThisExpression(node) {
376
378
  return found;
377
379
  }
378
380
 
381
+ function blockNeedsLexicalScope(node) {
382
+ if (node.type != "BlockStatement") {
383
+ return false;
384
+ }
385
+
386
+ var needsScope = false;
387
+ traverser.traverseEx(node, [], function (child) {
388
+ if (child != node && estest.isFunction(child)) {
389
+ return child;
390
+ }
391
+ if (
392
+ (child.type == "VariableDeclaration" && child.kind != "var")
393
+ || child.type == "ClassDeclaration"
394
+ ) {
395
+ needsScope = true;
396
+ this.abort();
397
+ }
398
+ return child;
399
+ });
400
+ return needsScope;
401
+ }
402
+
379
403
  module.exports = class Normalizer {
380
404
 
381
405
  constructor (logger) {
@@ -439,10 +463,13 @@ module.exports = class Normalizer {
439
463
  simplifyBlockStatement (node) {
440
464
  assert.ok(estest.isNode(node));
441
465
 
442
- function getBlockBodys(node) {
466
+ function getBlockBodys(node, isRoot) {
443
467
  if (node.type == "Program" || node.type == "BlockStatement") {
468
+ if (!isRoot && blockNeedsLexicalScope(node)) {
469
+ return [ node ];
470
+ }
444
471
  var stmts = [];
445
- node.body.forEach(stmt => utils.push(stmts, getBlockBodys(stmt)));
472
+ node.body.forEach(stmt => utils.push(stmts, getBlockBodys(stmt, false)));
446
473
  return stmts;
447
474
  } else {
448
475
  return [ node ];
@@ -451,7 +478,7 @@ module.exports = class Normalizer {
451
478
 
452
479
  return {
453
480
  type: node.type,
454
- body: getBlockBodys(node)
481
+ body: getBlockBodys(node, true)
455
482
  };
456
483
  }
457
484
 
@@ -3,8 +3,202 @@
3
3
  var assert = require("assert");
4
4
 
5
5
  var esshorten = require("esshorten");
6
+ var escope = require("escope");
6
7
 
7
8
  var estest = require("../estest");
9
+ var traverser = require("../traverser");
10
+
11
+ var RESERVED_WORDS = new Set([
12
+ "await",
13
+ "break",
14
+ "case",
15
+ "catch",
16
+ "class",
17
+ "const",
18
+ "continue",
19
+ "debugger",
20
+ "default",
21
+ "delete",
22
+ "do",
23
+ "else",
24
+ "enum",
25
+ "export",
26
+ "extends",
27
+ "false",
28
+ "finally",
29
+ "for",
30
+ "function",
31
+ "if",
32
+ "import",
33
+ "in",
34
+ "instanceof",
35
+ "new",
36
+ "null",
37
+ "return",
38
+ "super",
39
+ "switch",
40
+ "this",
41
+ "throw",
42
+ "true",
43
+ "try",
44
+ "typeof",
45
+ "var",
46
+ "void",
47
+ "while",
48
+ "with",
49
+ "yield",
50
+ "arguments",
51
+ "undefined"
52
+ ]);
53
+
54
+ var FIRST_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$";
55
+ var REST_CHARS = FIRST_CHARS + "0123456789";
56
+
57
+ function containsModernBindings(ast) {
58
+ var found = false;
59
+ traverser.traverseEx(ast, [], function (node) {
60
+ if (
61
+ (node.type == "VariableDeclaration" && node.kind != "var")
62
+ || node.type == "ClassDeclaration"
63
+ || node.type == "ClassExpression"
64
+ ) {
65
+ found = true;
66
+ this.abort();
67
+ }
68
+ return node;
69
+ });
70
+ return found;
71
+ }
72
+
73
+ function shortName(index) {
74
+ var name = FIRST_CHARS[index % FIRST_CHARS.length];
75
+ index = Math.floor(index / FIRST_CHARS.length);
76
+ while (index > 0) {
77
+ index -= 1;
78
+ name += REST_CHARS[index % REST_CHARS.length];
79
+ index = Math.floor(index / REST_CHARS.length);
80
+ }
81
+ return name;
82
+ }
83
+
84
+ function isRenamableVariable(scope, variable) {
85
+ if (scope.type == "global") {
86
+ return false;
87
+ }
88
+ if (variable.name == "arguments" || variable.name == "undefined") {
89
+ return false;
90
+ }
91
+ if (variable.tainted) {
92
+ return false;
93
+ }
94
+ if (!variable.identifiers || variable.identifiers.length == 0) {
95
+ return false;
96
+ }
97
+ if (variable.defs && variable.defs.some(def => def.type == "ClassName")) {
98
+ return false;
99
+ }
100
+ return true;
101
+ }
102
+
103
+ function reserveUnrenamedNames(scopeManager, renamable) {
104
+ var reserved = new Set(RESERVED_WORDS);
105
+ scopeManager.scopes.forEach(scope => {
106
+ scope.variables.forEach(variable => {
107
+ if (!renamable.has(variable)) {
108
+ reserved.add(variable.name);
109
+ }
110
+ });
111
+ scope.through.forEach(reference => {
112
+ if (!reference.resolved) {
113
+ reserved.add(reference.identifier.name);
114
+ }
115
+ });
116
+ });
117
+ return reserved;
118
+ }
119
+
120
+ function buildParentMap(ast) {
121
+ var parents = new WeakMap();
122
+ traverser.traverse(ast, [], function (node, stack) {
123
+ var parentFrame = stack[1];
124
+ if (parentFrame) {
125
+ parents.set(node, parentFrame.node);
126
+ }
127
+ return node;
128
+ });
129
+ return parents;
130
+ }
131
+
132
+ function renameIdentifier(identifier, name, parents) {
133
+ var parent = parents.get(identifier);
134
+ if (
135
+ parent
136
+ && parent.type == "Property"
137
+ && parent.shorthand === true
138
+ && (parent.key === identifier || parent.value === identifier)
139
+ ) {
140
+ parent.shorthand = false;
141
+ parent.key = {
142
+ type: "Identifier",
143
+ name: identifier.name
144
+ };
145
+ parent.value = {
146
+ type: "Identifier",
147
+ name: name
148
+ };
149
+ parents.set(parent.key, parent);
150
+ parents.set(parent.value, parent);
151
+ return;
152
+ }
153
+
154
+ identifier.name = name;
155
+ }
156
+
157
+ function modernMangle(ast) {
158
+ var scopeManager = escope.analyze(ast, {
159
+ ecmaVersion: 6,
160
+ optimistic: true,
161
+ sourceType: "script"
162
+ });
163
+
164
+ var variables = [];
165
+ scopeManager.scopes.forEach(scope => {
166
+ scope.variables.forEach(variable => {
167
+ if (isRenamableVariable(scope, variable)) {
168
+ variables.push({ scope: scope, variable: variable });
169
+ }
170
+ });
171
+ });
172
+
173
+ variables.sort((left, right) => {
174
+ var leftWeight = left.variable.references.length + left.variable.identifiers.length;
175
+ var rightWeight = right.variable.references.length + right.variable.identifiers.length;
176
+ return rightWeight - leftWeight;
177
+ });
178
+
179
+ var renamable = new Set(variables.map(entry => entry.variable));
180
+ var used = reserveUnrenamedNames(scopeManager, renamable);
181
+ var parents = buildParentMap(ast);
182
+ var next = 0;
183
+
184
+ variables.forEach(entry => {
185
+ var name;
186
+ do {
187
+ name = shortName(next);
188
+ next += 1;
189
+ } while (used.has(name) || RESERVED_WORDS.has(name));
190
+ used.add(name);
191
+
192
+ entry.variable.identifiers.forEach(identifier => {
193
+ renameIdentifier(identifier, name, parents);
194
+ });
195
+ entry.variable.references.forEach(reference => {
196
+ renameIdentifier(reference.identifier, name, parents);
197
+ });
198
+ });
199
+
200
+ return ast;
201
+ }
8
202
 
9
203
  module.exports = class Uglifier {
10
204
 
@@ -19,8 +213,11 @@ module.exports = class Uglifier {
19
213
  */
20
214
  uglify (ast) {
21
215
  assert.ok(estest.isNode(ast));
22
-
216
+
217
+ if (containsModernBindings(ast)) {
218
+ return modernMangle(ast);
219
+ }
23
220
  return esshorten.mangle(ast);
24
221
  }
25
222
 
26
- };
223
+ };