js-confuser 1.5.8 → 1.6.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.
Files changed (139) hide show
  1. package/.github/workflows/node.js.yml +2 -2
  2. package/CHANGELOG.md +69 -0
  3. package/README.md +143 -7
  4. package/dist/index.js +33 -4
  5. package/dist/obfuscator.js +30 -31
  6. package/dist/options.js +4 -5
  7. package/dist/order.js +4 -6
  8. package/dist/probability.js +2 -4
  9. package/dist/templates/bufferToString.js +13 -0
  10. package/dist/templates/crash.js +2 -2
  11. package/dist/templates/es5.js +18 -0
  12. package/dist/transforms/antiTooling.js +1 -1
  13. package/dist/transforms/calculator.js +77 -21
  14. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +980 -367
  15. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +8 -3
  16. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +25 -26
  17. package/dist/transforms/deadCode.js +33 -25
  18. package/dist/transforms/dispatcher.js +7 -6
  19. package/dist/transforms/es5/antiClass.js +6 -2
  20. package/dist/transforms/es5/antiDestructuring.js +3 -1
  21. package/dist/transforms/es5/es5.js +31 -34
  22. package/dist/transforms/eval.js +11 -0
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +8 -5
  24. package/dist/transforms/extraction/objectExtraction.js +6 -1
  25. package/dist/transforms/finalizer.js +82 -0
  26. package/dist/transforms/flatten.js +82 -55
  27. package/dist/transforms/hexadecimalNumbers.js +34 -9
  28. package/dist/transforms/identifier/globalAnalysis.js +88 -0
  29. package/dist/transforms/identifier/globalConcealing.js +10 -83
  30. package/dist/transforms/identifier/movedDeclarations.js +2 -8
  31. package/dist/transforms/identifier/renameVariables.js +39 -27
  32. package/dist/transforms/identifier/variableAnalysis.js +58 -62
  33. package/dist/transforms/minify.js +80 -61
  34. package/dist/transforms/opaquePredicates.js +1 -1
  35. package/dist/transforms/preparation/preparation.js +2 -2
  36. package/dist/transforms/preparation.js +231 -0
  37. package/dist/transforms/renameLabels.js +1 -1
  38. package/dist/transforms/rgf.js +4 -5
  39. package/dist/transforms/stack.js +87 -26
  40. package/dist/transforms/string/encoding.js +150 -179
  41. package/dist/transforms/string/stringCompression.js +14 -15
  42. package/dist/transforms/string/stringConcealing.js +25 -8
  43. package/dist/transforms/string/stringEncoding.js +13 -24
  44. package/dist/transforms/transform.js +11 -18
  45. package/dist/traverse.js +24 -18
  46. package/dist/util/compare.js +2 -2
  47. package/dist/util/gen.js +15 -0
  48. package/dist/util/insert.js +31 -7
  49. package/dist/util/random.js +15 -0
  50. package/package.json +5 -5
  51. package/src/index.ts +57 -19
  52. package/src/obfuscator.ts +26 -29
  53. package/src/options.ts +17 -21
  54. package/src/order.ts +4 -8
  55. package/src/probability.ts +2 -3
  56. package/src/templates/bufferToString.ts +68 -0
  57. package/src/templates/crash.ts +5 -9
  58. package/src/templates/es5.ts +131 -0
  59. package/src/transforms/antiTooling.ts +1 -1
  60. package/src/transforms/calculator.ts +122 -59
  61. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +1583 -571
  62. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +18 -3
  63. package/src/transforms/deadCode.ts +383 -26
  64. package/src/transforms/dispatcher.ts +8 -6
  65. package/src/transforms/es5/antiClass.ts +10 -1
  66. package/src/transforms/es5/antiDestructuring.ts +3 -1
  67. package/src/transforms/es5/es5.ts +32 -77
  68. package/src/transforms/eval.ts +18 -0
  69. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +9 -6
  70. package/src/transforms/extraction/objectExtraction.ts +12 -5
  71. package/src/transforms/finalizer.ts +75 -0
  72. package/src/transforms/flatten.ts +194 -151
  73. package/src/transforms/identifier/globalAnalysis.ts +85 -0
  74. package/src/transforms/identifier/globalConcealing.ts +14 -103
  75. package/src/transforms/identifier/movedDeclarations.ts +4 -11
  76. package/src/transforms/identifier/renameVariables.ts +37 -30
  77. package/src/transforms/identifier/variableAnalysis.ts +66 -73
  78. package/src/transforms/minify.ts +116 -77
  79. package/src/transforms/opaquePredicates.ts +2 -2
  80. package/src/transforms/preparation.ts +238 -0
  81. package/src/transforms/renameLabels.ts +2 -2
  82. package/src/transforms/rgf.ts +6 -7
  83. package/src/transforms/stack.ts +97 -37
  84. package/src/transforms/string/encoding.ts +115 -212
  85. package/src/transforms/string/stringCompression.ts +27 -18
  86. package/src/transforms/string/stringConcealing.ts +41 -11
  87. package/src/transforms/string/stringEncoding.ts +18 -18
  88. package/src/transforms/transform.ts +15 -21
  89. package/src/traverse.ts +24 -12
  90. package/src/types.ts +11 -2
  91. package/src/util/compare.ts +2 -2
  92. package/src/util/gen.ts +21 -1
  93. package/src/util/insert.ts +49 -9
  94. package/src/util/random.ts +13 -0
  95. package/test/code/Cash.test.ts +1 -1
  96. package/test/code/Dynamic.test.ts +12 -10
  97. package/test/code/ES6.src.js +136 -0
  98. package/test/code/ES6.test.ts +28 -2
  99. package/test/code/NewFeatures.test.ts +19 -0
  100. package/test/index.test.ts +15 -2
  101. package/test/probability.test.ts +44 -0
  102. package/test/templates/template.test.ts +1 -1
  103. package/test/transforms/antiTooling.test.ts +52 -0
  104. package/test/transforms/calculator.test.ts +40 -0
  105. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +713 -149
  106. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +173 -0
  107. package/test/transforms/deadCode.test.ts +66 -15
  108. package/test/transforms/dispatcher.test.ts +44 -1
  109. package/test/transforms/es5/antiClass.test.ts +33 -0
  110. package/test/transforms/es5/antiDestructuring.test.ts +16 -0
  111. package/test/transforms/eval.test.ts +53 -0
  112. package/test/transforms/extraction/objectExtraction.test.ts +21 -0
  113. package/test/transforms/flatten.test.ts +195 -3
  114. package/test/transforms/identifier/movedDeclarations.test.ts +27 -0
  115. package/test/transforms/identifier/renameVariables.test.ts +108 -0
  116. package/test/transforms/lock/antiDebug.test.ts +2 -2
  117. package/test/transforms/minify.test.ts +151 -0
  118. package/test/transforms/preparation.test.ts +157 -0
  119. package/test/transforms/rgf.test.ts +56 -29
  120. package/test/transforms/stack.test.ts +91 -21
  121. package/test/transforms/string/stringCompression.test.ts +39 -0
  122. package/test/transforms/string/stringConcealing.test.ts +115 -0
  123. package/test/transforms/string/stringEncoding.test.ts +53 -2
  124. package/test/transforms/transform.test.ts +66 -0
  125. package/test/traverse.test.ts +139 -0
  126. package/test/util/compare.test.ts +23 -1
  127. package/src/transforms/controlFlowFlattening/choiceFlowObfuscation.ts +0 -87
  128. package/src/transforms/controlFlowFlattening/controlFlowObfuscation.ts +0 -203
  129. package/src/transforms/controlFlowFlattening/switchCaseObfuscation.ts +0 -130
  130. package/src/transforms/hexadecimalNumbers.ts +0 -31
  131. package/src/transforms/hideInitializingCode.ts +0 -432
  132. package/src/transforms/label.ts +0 -64
  133. package/src/transforms/preparation/nameConflicts.ts +0 -102
  134. package/src/transforms/preparation/preparation.ts +0 -176
  135. package/test/transforms/controlFlowFlattening/controlFlowObfuscation.test.ts +0 -101
  136. package/test/transforms/controlFlowFlattening/switchCaseObfuscation.test.ts +0 -120
  137. package/test/transforms/hideInitializingCode.test.ts +0 -336
  138. package/test/transforms/preparation/nameConflicts.test.ts +0 -52
  139. package/test/transforms/preparation/preparation.test.ts +0 -62
@@ -17,6 +17,7 @@ import {
17
17
  import { append, prepend } from "../../util/insert";
18
18
  import Transform from "../transform";
19
19
  import { isModuleSource } from "./stringConcealing";
20
+ import { chance } from "../../util/random";
20
21
  function LZ_encode(c) {
21
22
  ok(c);
22
23
  var x = "charCodeAt",
@@ -83,7 +84,7 @@ export default class StringCompression extends Transform {
83
84
  map: Map<string, number>;
84
85
  ignore: Set<string>;
85
86
  string: string;
86
- delimiter = "1";
87
+ delimiter = "|";
87
88
 
88
89
  fnName: string;
89
90
 
@@ -110,7 +111,11 @@ export default class StringCompression extends Transform {
110
111
 
111
112
  var encoded = LZ_encode(this.string);
112
113
  if (LZ_decode(encoded) !== this.string) {
113
- this.error(new Error("String failed to be decoded"));
114
+ this.error(
115
+ new Error(
116
+ "String failed to be decoded. Try disabling the 'stringCompression' option."
117
+ )
118
+ );
114
119
  }
115
120
 
116
121
  var getStringParamName = this.getPlaceholder();
@@ -195,37 +200,41 @@ export default class StringCompression extends Transform {
195
200
  return;
196
201
  }
197
202
 
198
- if (!ComputeProbabilityMap(object.value, (x) => x)) {
203
+ if (
204
+ !ComputeProbabilityMap(
205
+ this.options.stringCompression,
206
+ (x) => x,
207
+ object.value
208
+ )
209
+ ) {
199
210
  return;
200
211
  }
201
212
 
213
+ // HARD CODED LIMIT of 10,000 (after 1,000 elements)
214
+ if (this.map.size > 1000 && !chance(this.map.size / 100)) return;
215
+
202
216
  var index = this.map.get(object.value);
217
+
218
+ // New string, add it!
203
219
  if (typeof index !== "number") {
220
+ // Ensure the string gets properly decoded
204
221
  if (LZ_decode(LZ_encode(object.value)) !== object.value) {
205
222
  this.ignore.add(object.value);
206
223
  return;
207
224
  }
208
225
 
209
- var before = this.string;
210
-
211
226
  index = this.map.size;
212
227
  this.map.set(object.value, index);
213
228
  this.string += object.value + this.delimiter;
214
-
215
- // allow rollback if string becomes corrupted
216
- if (LZ_decode(LZ_encode(this.string)) !== this.string) {
217
- this.string = before;
218
- this.map.delete(object.value);
219
- this.ignore.add(object.value);
220
- return;
221
- }
222
229
  }
223
230
  ok(typeof index === "number");
224
231
 
225
- this.replaceIdentifierOrLiteral(
226
- object,
227
- CallExpression(Identifier(this.fnName), [Literal(index)]),
228
- parents
229
- );
232
+ return () => {
233
+ this.replaceIdentifierOrLiteral(
234
+ object,
235
+ CallExpression(Identifier(this.fnName), [Literal(index)]),
236
+ parents
237
+ );
238
+ };
230
239
  }
231
240
  }
@@ -13,14 +13,20 @@ import {
13
13
  Node,
14
14
  ObjectExpression,
15
15
  Property,
16
- ThisExpression,
17
16
  VariableDeclaration,
18
17
  VariableDeclarator,
19
18
  } from "../../util/gen";
20
19
  import { append, prepend } from "../../util/insert";
21
- import { choice, getRandomInteger, getRandomString } from "../../util/random";
20
+ import {
21
+ chance,
22
+ choice,
23
+ getRandomInteger,
24
+ getRandomString,
25
+ } from "../../util/random";
22
26
  import Transform from "../transform";
23
27
  import Encoding from "./encoding";
28
+ import { ComputeProbabilityMap } from "../../probability";
29
+ import { BufferToStringTemplate } from "../../templates/bufferToString";
24
30
 
25
31
  export function isModuleSource(object: Node, parents: Node[]) {
26
32
  if (!parents[0]) {
@@ -89,13 +95,23 @@ export default class StringConcealing extends Transform {
89
95
  super.apply(tree);
90
96
 
91
97
  var cacheName = this.getPlaceholder();
98
+ var bufferToStringName = this.getPlaceholder();
99
+
100
+ // This helper functions convert UInt8 Array to UTf-string
101
+ prepend(
102
+ tree,
103
+ ...BufferToStringTemplate.compile({ name: bufferToStringName })
104
+ );
92
105
 
93
106
  Object.keys(this.encoding).forEach((type) => {
94
107
  var { template } = Encoding[type];
95
108
  var decodeFn = this.getPlaceholder();
96
109
  var getterFn = this.encoding[type];
97
110
 
98
- append(tree, template.single({ name: decodeFn }));
111
+ append(
112
+ tree,
113
+ template.single({ name: decodeFn, bufferToString: bufferToStringName })
114
+ );
99
115
 
100
116
  append(
101
117
  tree,
@@ -165,10 +181,24 @@ export default class StringConcealing extends Transform {
165
181
  return;
166
182
  }
167
183
 
184
+ // Allow user to choose which strings get changed
185
+ if (
186
+ !ComputeProbabilityMap(
187
+ this.options.stringConcealing,
188
+ (x) => x,
189
+ object.value
190
+ )
191
+ ) {
192
+ return;
193
+ }
194
+
195
+ // HARD CODED LIMIT of 10,000 (after 1,000 elements)
196
+ if (this.set.size > 1000 && !chance(this.set.size / 100)) return;
197
+
168
198
  var types = Object.keys(this.encoding);
169
199
 
170
200
  var type = choice(types);
171
- if (!type || (!this.hasAllEncodings && Math.random() > 0.9)) {
201
+ if (!type || (!this.hasAllEncodings && chance(10))) {
172
202
  var allowed = Object.keys(Encoding).filter(
173
203
  (type) => !this.encoding[type]
174
204
  );
@@ -211,23 +241,23 @@ export default class StringConcealing extends Transform {
211
241
  var callExpr = CallExpression(Identifier(fnName), [Literal(index)]);
212
242
 
213
243
  // use `.apply` to fool automated de-obfuscators
214
- if (Math.random() > 0.5) {
244
+ if (chance(10)) {
215
245
  callExpr = CallExpression(
216
- MemberExpression(Identifier(fnName), Identifier("apply"), false),
217
- [ThisExpression(), ArrayExpression([Literal(index)])]
246
+ MemberExpression(Identifier(fnName), Literal("apply"), true),
247
+ [Identifier("undefined"), ArrayExpression([Literal(index)])]
218
248
  );
219
249
  }
220
250
 
221
251
  // use `.call`
222
- else if (Math.random() > 0.5) {
252
+ else if (chance(10)) {
223
253
  callExpr = CallExpression(
224
- MemberExpression(Identifier(fnName), Identifier("call"), false),
225
- [ThisExpression(), Literal(index)]
254
+ MemberExpression(Identifier(fnName), Literal("call"), true),
255
+ [Identifier("undefined"), Literal(index)]
226
256
  );
227
257
  }
228
258
 
229
259
  var referenceType = "call";
230
- if (parents.length && Math.random() < 0.5 / this.variablesMade) {
260
+ if (parents.length && chance(50 - this.variablesMade)) {
231
261
  referenceType = "constantReference";
232
262
  }
233
263
 
@@ -1,8 +1,9 @@
1
1
  import Transform from "../transform";
2
2
  import { choice } from "../../util/random";
3
- import { ObfuscateOrder } from "../../order";
4
3
  import { isDirective } from "../../util/compare";
5
4
  import { isModuleSource } from "./stringConcealing";
5
+ import { ComputeProbabilityMap } from "../../probability";
6
+ import { Identifier } from "../../util/gen";
6
7
 
7
8
  function pad(x: string, len: number): string {
8
9
  while (x.length < len) {
@@ -54,32 +55,30 @@ function toUnicodeRepresentation(str: string) {
54
55
  * - Cost Low
55
56
  */
56
57
  export default class StringEncoding extends Transform {
57
- seen: Set<Node>;
58
-
59
58
  constructor(o) {
60
- super(o, ObfuscateOrder.StringEncoding);
61
- }
62
-
63
- apply(tree) {
64
- this.seen = new Set();
65
- super.apply(tree);
59
+ super(o);
66
60
  }
67
61
 
68
62
  match(object, parents) {
69
63
  return (
70
64
  object.type == "Literal" &&
71
65
  typeof object.value === "string" &&
66
+ object.value.length > 0 &&
72
67
  !isModuleSource(object, parents) &&
73
68
  !isDirective(object, parents)
74
69
  );
75
70
  }
76
71
 
77
72
  transform(object, parents) {
78
- // Todo fix circular json problems
79
- if (this.seen.has(object)) {
73
+ // Allow percentages
74
+ if (
75
+ !ComputeProbabilityMap(
76
+ this.options.stringEncoding,
77
+ (x) => x,
78
+ object.value
79
+ )
80
+ )
80
81
  return;
81
- }
82
- this.seen.add(object);
83
82
 
84
83
  var type = choice(["hexadecimal", "unicode"]);
85
84
 
@@ -87,10 +86,11 @@ export default class StringEncoding extends Transform {
87
86
  type == "hexadecimal" ? toHexRepresentation : toUnicodeRepresentation
88
87
  )(object.value);
89
88
 
90
- // escodegen tries to escape backslashes, here is a work-around
91
- this.replace(object, {
92
- type: "Identifier",
93
- name: `'${escapedString}'`,
94
- });
89
+ return () => {
90
+ if (object.type !== "Literal") return;
91
+
92
+ // ESCodeGen tries to escape backslashes, here is a work-around
93
+ this.replace(object, Identifier(`'${escapedString}'`));
94
+ };
95
95
  }
96
96
  }
@@ -74,11 +74,6 @@ export default class Transform {
74
74
  */
75
75
  after: Transform[];
76
76
 
77
- /**
78
- * Transformations to run at the same time (can cause conflicts so use sparingly)
79
- */
80
- concurrent: Transform[];
81
-
82
77
  constructor(obfuscator, priority: number = -1) {
83
78
  ok(obfuscator instanceof Obfuscator, "obfuscator should be an Obfuscator");
84
79
 
@@ -89,8 +84,6 @@ export default class Transform {
89
84
 
90
85
  this.before = [];
91
86
  this.after = [];
92
-
93
- this.concurrent = [];
94
87
  }
95
88
 
96
89
  /**
@@ -120,14 +113,11 @@ export default class Transform {
120
113
  */
121
114
  this.before.forEach((x) => x.apply(tree));
122
115
 
116
+ /**
117
+ * Run this transformation
118
+ */
123
119
  traverse(tree, (object, parents) => {
124
- var fns = [];
125
- fns.push(this.input(object, parents));
126
-
127
- // Fix 1. Increase performance with multiple transforms on one iteration.
128
- this.concurrent.forEach((x) => fns.push(x.input(object, parents)));
129
-
130
- return () => fns.forEach((x) => x && x());
120
+ return this.input(object, parents);
131
121
  });
132
122
 
133
123
  /**
@@ -191,18 +181,18 @@ export default class Transform {
191
181
 
192
182
  /**
193
183
  * Returns an independent name generator with it's own counter.
194
- * @param offset
184
+ * @param overrideMode - Override the user's `identifierGenerator` option
195
185
  * @returns
196
186
  */
197
- getGenerator(offset = 0) {
198
- var count = offset;
187
+ getGenerator(overrideMode?: string) {
188
+ var count = 0;
199
189
  var identifiers = new Set();
200
190
  return {
201
191
  generate: () => {
202
- var retValue;
192
+ var retValue: string;
203
193
  do {
204
194
  count++;
205
- retValue = this.generateIdentifier(-1, count);
195
+ retValue = this.generateIdentifier(-1, count, overrideMode);
206
196
  } while (identifiers.has(retValue));
207
197
 
208
198
  identifiers.add(retValue);
@@ -217,7 +207,11 @@ export default class Transform {
217
207
  * @param length Default length is 6 to 10 characters.
218
208
  * @returns **`string`**
219
209
  */
220
- generateIdentifier(length: number = -1, count = -1): string {
210
+ generateIdentifier(
211
+ length: number = -1,
212
+ count = -1,
213
+ overrideMode?: string
214
+ ): string {
221
215
  if (length == -1) {
222
216
  length = getRandomInteger(6, 8);
223
217
  }
@@ -233,7 +227,7 @@ export default class Transform {
233
227
  var identifier;
234
228
  do {
235
229
  identifier = ComputeProbabilityMap(
236
- this.options.identifierGenerator,
230
+ overrideMode || this.options.identifierGenerator,
237
231
  (mode = "randomized") => {
238
232
  switch (mode) {
239
233
  case "randomized":
package/src/traverse.ts CHANGED
@@ -39,21 +39,14 @@ export type ExitCallback = () => void;
39
39
  export function walk(
40
40
  object: Node | Node[],
41
41
  parents: Node[],
42
- onEnter: EnterCallback,
43
- seen = new Set<Node>()
42
+ onEnter: EnterCallback
44
43
  ): "EXIT" | void {
45
44
  if (typeof object === "object" && object) {
46
- if (seen.has(object as any)) {
47
- console.log(object);
48
- throw new Error("Already seen: " + (object as any).type);
49
- }
50
- seen.add(object as any);
51
-
52
45
  var newParents: Node[] = [object as Node, ...parents];
53
46
 
54
- if (!Array.isArray(object)) {
55
- validateChain(object, parents);
56
- }
47
+ // if (!Array.isArray(object)) {
48
+ // validateChain(object, parents);
49
+ // }
57
50
 
58
51
  // 1. Call `onEnter` function and remember any onExit callback returned
59
52
  var onExit = onEnter(object as Node, parents);
@@ -66,7 +59,6 @@ export function walk(
66
59
  return "EXIT";
67
60
  }
68
61
  }
69
- copy.forEach((x) => {});
70
62
  } else {
71
63
  var keys = Object.keys(object);
72
64
  for (var key of keys) {
@@ -106,3 +98,23 @@ export function walk(
106
98
  export default function traverse(tree, onEnter: EnterCallback) {
107
99
  walk(tree, [], onEnter);
108
100
  }
101
+
102
+ /**
103
+ * This is debugging function used to test for circular references.
104
+ */
105
+ export function assertNoCircular(object) {
106
+ var seen = new Set();
107
+
108
+ traverse(object, (node, nodeParents) => {
109
+ if (node && typeof node === "object") {
110
+ if (seen.has(node)) {
111
+ console.log(nodeParents);
112
+ console.log(node);
113
+
114
+ throw new Error("FOUND CIRCULAR REFERENCE");
115
+ }
116
+
117
+ seen.add(node);
118
+ }
119
+ });
120
+ }
package/src/types.ts CHANGED
@@ -118,5 +118,14 @@ export type IJsConfuserDebugTransformations = (
118
118
  export type IJsConfuserDebugObfuscation = (
119
119
  code: string,
120
120
  options: ObfuscateOptions,
121
- callback: (name: string, complete: number, totalTransforms: number) => void
122
- ) => Promise<string>;
121
+ callback: (name: string, complete: number, totalTransforms: number) => void,
122
+ performance: Performance
123
+ ) => Promise<{
124
+ obfuscated: string;
125
+ transformationTimes: { [transformName: string]: number };
126
+ parseTime: number;
127
+ compileTime: number;
128
+ obfuscationTime: number;
129
+ totalTransforms: number;
130
+ totalPossibleTransforms: number;
131
+ }>;
@@ -53,8 +53,8 @@ export function isValidIdentifier(name: string): boolean {
53
53
  return false;
54
54
  }
55
55
 
56
- var x = name.match(/^[A-z$_][A-z0-9$_]*/);
57
- return x && x[0] == name;
56
+ var x = name.match(/^[A-Za-z$_][A-Za-z0-9$_]*/);
57
+ return !!(x && x[0] == name);
58
58
  }
59
59
 
60
60
  export function isInsideType(
package/src/util/gen.ts CHANGED
@@ -557,6 +557,22 @@ export function ClassDeclaration(
557
557
  } as Node;
558
558
  }
559
559
 
560
+ export function ClassExpression(
561
+ id: Node | null,
562
+ superClass: Node = null,
563
+ body: Node[] = []
564
+ ) {
565
+ return {
566
+ type: "ClassExpression",
567
+ id: id,
568
+ superClass: superClass,
569
+ body: {
570
+ type: "ClassBody",
571
+ body: body,
572
+ },
573
+ } as Node;
574
+ }
575
+
560
576
  export function ThrowStatement(argument: Node) {
561
577
  return {
562
578
  type: "ThrowStatement",
@@ -607,7 +623,11 @@ export function CatchClause(param: Node = null, body) {
607
623
  };
608
624
  }
609
625
 
610
- export function TryStatement(body: Node[], handler: Node, finallyBody: Node[]) {
626
+ export function TryStatement(
627
+ body: Node[],
628
+ handler: Node,
629
+ finallyBody?: Node[]
630
+ ) {
611
631
  ok(handler);
612
632
  ok(handler.type == "CatchClause");
613
633
  return {
@@ -113,8 +113,14 @@ export function getDefiningContext(o: Node, p: Node[]): Node {
113
113
  var variableDeclaration = p.find((x) => x.type == "VariableDeclaration");
114
114
  ok(variableDeclaration);
115
115
 
116
- if (variableDeclaration.kind === "let") {
117
- return getLexContext(o, p);
116
+ if (
117
+ variableDeclaration.kind === "let" ||
118
+ variableDeclaration.kind === "const"
119
+ ) {
120
+ var context = getVarContext(o, p);
121
+ if (context && context.type === "Program") {
122
+ return getLexContext(o, p);
123
+ }
118
124
  }
119
125
  }
120
126
 
@@ -178,7 +184,7 @@ export function getBlockBody(block: Node): Node[] {
178
184
  return getBlockBody(block.body);
179
185
  }
180
186
 
181
- export function getIndexDirect(object: Node, parent: Node[]): string {
187
+ export function getIndexDirect(object: Node, parent: Node): string {
182
188
  return Object.keys(parent).find((x) => parent[x] == object);
183
189
  }
184
190
 
@@ -255,18 +261,38 @@ export function prepend(block: Node, ...nodes: Node[]) {
255
261
  ok(!Array.isArray(block), "block should not be array");
256
262
 
257
263
  if (block.type == "Program") {
258
- var decs = 0;
264
+ var moveBy = 0;
259
265
  block.body.forEach((stmt, i) => {
260
266
  if (stmt.type == "ImportDeclaration") {
261
- if (decs == i) {
262
- decs++;
267
+ if (moveBy == i) {
268
+ moveBy++;
269
+ }
270
+ }
271
+
272
+ if (
273
+ stmt.type === "ExpressionStatement" &&
274
+ typeof stmt.directive === "string"
275
+ ) {
276
+ if (moveBy == i) {
277
+ moveBy++;
263
278
  }
264
279
  }
265
280
  });
266
281
 
267
- block.body.splice(decs, 0, ...nodes);
282
+ block.body.splice(moveBy, 0, ...nodes);
283
+ } else if (block.type === "SwitchCase") {
284
+ block.consequent.unshift(...nodes);
268
285
  } else {
269
- getBlockBody(block).unshift(...nodes);
286
+ var bodyArray = getBlockBody(block);
287
+
288
+ // Check for 'use strict'
289
+ if (bodyArray[0] && bodyArray[0].directive) {
290
+ // Insert under 'use strict' directive
291
+ bodyArray.splice(1, 0, ...nodes);
292
+ } else {
293
+ // Prepend at the top of the block
294
+ bodyArray.unshift(...nodes);
295
+ }
270
296
  }
271
297
  }
272
298
 
@@ -313,7 +339,10 @@ export function clone<T>(object: T): T {
313
339
  * @param p
314
340
  * @returns
315
341
  */
316
- export function isForInitialize(o, p): "initializer" | "left-hand" | false {
342
+ export function isForInitialize(
343
+ o: Node,
344
+ p: Node[]
345
+ ): "initializer" | "left-hand" | false {
317
346
  validateChain(o, p);
318
347
 
319
348
  var forIndex = p.findIndex(
@@ -322,6 +351,17 @@ export function isForInitialize(o, p): "initializer" | "left-hand" | false {
322
351
  x.type == "ForInStatement" ||
323
352
  x.type == "ForOfStatement"
324
353
  );
354
+
355
+ if (
356
+ p
357
+ .slice(0, forIndex)
358
+ .find((x) =>
359
+ ["ArrowFunctionExpression", "BlockStatement"].includes(x.type)
360
+ )
361
+ ) {
362
+ return false;
363
+ }
364
+
325
365
  if (forIndex !== -1) {
326
366
  if (p[forIndex].type == "ForStatement") {
327
367
  if (p[forIndex].init == (p[forIndex - 1] || o)) {
@@ -8,11 +8,24 @@ import {
8
8
  ArrayExpression,
9
9
  } from "./gen";
10
10
 
11
+ /**
12
+ * Returns a random element from the given array
13
+ * @param choices Array of items
14
+ * @returns One of the items in the array at random
15
+ */
11
16
  export function choice<T>(choices: T[]): T {
12
17
  var index = Math.floor(Math.random() * choices.length);
13
18
  return choices[index];
14
19
  }
15
20
 
21
+ /**
22
+ * Returns a true/false based on the percent chance (0%-100%)
23
+ * @param percentChance AS A PERCENTAGE 0 - 100%
24
+ */
25
+ export function chance(percentChance: number): boolean {
26
+ return Math.random() < percentChance / 100;
27
+ }
28
+
16
29
  /**
17
30
  * **Mutates the given array**
18
31
  * @param array
@@ -4,7 +4,7 @@ import JsConfuser from "../../src/index";
4
4
 
5
5
  var CASH_JS = readFileSync(join(__dirname, "./Cash.src.js"), "utf-8");
6
6
 
7
- it("works with Cash.js on High Preset", async () => {
7
+ test("Variant #1: Cash.js on High Preset (Strict Mode)", async () => {
8
8
  var output = await JsConfuser(CASH_JS, {
9
9
  target: "browser",
10
10
  preset: "high",
@@ -1,10 +1,11 @@
1
1
  import { readFileSync, writeFileSync } from "fs";
2
2
  import { join } from "path";
3
3
  import JsConfuser from "../../src/index";
4
+ import { ObfuscateOptions } from "../../src/options";
4
5
 
5
6
  var SOURCE_JS = readFileSync(join(__dirname, "./Dynamic.src.js"), "utf-8");
6
7
 
7
- it("works on High Preset", async () => {
8
+ test.concurrent("Variant #1: Dynamic.src.js on High Preset", async () => {
8
9
  // `input` is an embedded variable, therefore globalConcealing must be turned off
9
10
  var output = await JsConfuser(SOURCE_JS, {
10
11
  target: "browser",
@@ -22,19 +23,20 @@ it("works on High Preset", async () => {
22
23
  expect(value).toStrictEqual(1738.1738);
23
24
  });
24
25
 
25
- it("work when doubly obfuscated with High Preset", async () => {
26
- // `input` is an embedded variable, therefore globalConcealing must be turned off
27
- var output = await JsConfuser(SOURCE_JS, {
26
+ test.concurrent("Variant #2: Dynamic.src.js on 2x High Preset", async () => {
27
+ var options: ObfuscateOptions = {
28
28
  target: "node",
29
29
  preset: "high",
30
30
  globalConcealing: false,
31
- });
31
+ };
32
32
 
33
- var doublyObfuscated = await JsConfuser(output, {
34
- target: "node",
35
- preset: "high",
36
- globalConcealing: false,
37
- });
33
+ var output = await JsConfuser(SOURCE_JS, options);
34
+
35
+ // writeFileSync("./dev.error.1.js", output, "utf-8");
36
+
37
+ var doublyObfuscated = await JsConfuser(output, options);
38
+
39
+ // writeFileSync("./dev.error.2.js", doublyObfuscated, "utf-8");
38
40
 
39
41
  var value = "never_called";
40
42
  function input(x) {