js-confuser 1.5.9 → 1.7.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 (143) hide show
  1. package/.github/workflows/node.js.yml +2 -2
  2. package/CHANGELOG.md +55 -0
  3. package/README.md +346 -165
  4. package/dist/constants.js +6 -2
  5. package/dist/index.js +9 -21
  6. package/dist/obfuscator.js +19 -31
  7. package/dist/options.js +5 -5
  8. package/dist/order.js +1 -3
  9. package/dist/presets.js +6 -7
  10. package/dist/probability.js +2 -4
  11. package/dist/templates/bufferToString.js +13 -0
  12. package/dist/templates/crash.js +3 -3
  13. package/dist/templates/es5.js +18 -0
  14. package/dist/templates/functionLength.js +16 -0
  15. package/dist/transforms/calculator.js +77 -21
  16. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +980 -367
  17. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -1
  18. package/dist/transforms/controlFlowFlattening/switchCaseObfuscation.js +25 -26
  19. package/dist/transforms/deadCode.js +33 -25
  20. package/dist/transforms/dispatcher.js +8 -4
  21. package/dist/transforms/es5/antiDestructuring.js +2 -0
  22. package/dist/transforms/es5/es5.js +31 -34
  23. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +92 -58
  24. package/dist/transforms/finalizer.js +82 -0
  25. package/dist/transforms/flatten.js +229 -148
  26. package/dist/transforms/identifier/globalAnalysis.js +88 -0
  27. package/dist/transforms/identifier/globalConcealing.js +10 -83
  28. package/dist/transforms/identifier/movedDeclarations.js +35 -88
  29. package/dist/transforms/identifier/renameVariables.js +124 -59
  30. package/dist/transforms/identifier/variableAnalysis.js +58 -62
  31. package/dist/transforms/lock/lock.js +0 -37
  32. package/dist/transforms/minify.js +60 -57
  33. package/dist/transforms/opaquePredicates.js +1 -1
  34. package/dist/transforms/preparation/preparation.js +2 -2
  35. package/dist/transforms/preparation.js +231 -0
  36. package/dist/transforms/renameLabels.js +1 -1
  37. package/dist/transforms/rgf.js +139 -247
  38. package/dist/transforms/stack.js +128 -26
  39. package/dist/transforms/string/encoding.js +150 -179
  40. package/dist/transforms/string/stringCompression.js +14 -15
  41. package/dist/transforms/string/stringConcealing.js +25 -8
  42. package/dist/transforms/string/stringEncoding.js +13 -24
  43. package/dist/transforms/transform.js +12 -19
  44. package/dist/traverse.js +24 -10
  45. package/dist/util/gen.js +17 -1
  46. package/dist/util/identifiers.js +37 -3
  47. package/dist/util/insert.js +35 -4
  48. package/dist/util/random.js +15 -0
  49. package/docs/ControlFlowFlattening.md +595 -0
  50. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  51. package/{Integrity.md → docs/Integrity.md} +2 -2
  52. package/docs/RGF.md +419 -0
  53. package/package.json +5 -5
  54. package/src/constants.ts +3 -0
  55. package/src/index.ts +2 -2
  56. package/src/obfuscator.ts +19 -31
  57. package/src/options.ts +14 -103
  58. package/src/order.ts +1 -5
  59. package/src/presets.ts +6 -7
  60. package/src/probability.ts +2 -3
  61. package/src/templates/bufferToString.ts +68 -0
  62. package/src/templates/crash.ts +15 -19
  63. package/src/templates/es5.ts +131 -0
  64. package/src/templates/functionLength.ts +14 -0
  65. package/src/transforms/calculator.ts +122 -59
  66. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +1583 -571
  67. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +4 -1
  68. package/src/transforms/deadCode.ts +383 -26
  69. package/src/transforms/dispatcher.ts +9 -4
  70. package/src/transforms/es5/antiDestructuring.ts +2 -0
  71. package/src/transforms/es5/es5.ts +32 -77
  72. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +133 -129
  73. package/src/transforms/{hexadecimalNumbers.ts → finalizer.ts} +29 -13
  74. package/src/transforms/flatten.ts +357 -300
  75. package/src/transforms/identifier/globalAnalysis.ts +85 -0
  76. package/src/transforms/identifier/globalConcealing.ts +14 -103
  77. package/src/transforms/identifier/movedDeclarations.ts +49 -102
  78. package/src/transforms/identifier/renameVariables.ts +149 -78
  79. package/src/transforms/identifier/variableAnalysis.ts +66 -73
  80. package/src/transforms/lock/lock.ts +1 -42
  81. package/src/transforms/minify.ts +91 -75
  82. package/src/transforms/opaquePredicates.ts +2 -2
  83. package/src/transforms/preparation.ts +238 -0
  84. package/src/transforms/renameLabels.ts +2 -2
  85. package/src/transforms/rgf.ts +213 -405
  86. package/src/transforms/stack.ts +156 -36
  87. package/src/transforms/string/encoding.ts +115 -212
  88. package/src/transforms/string/stringCompression.ts +27 -18
  89. package/src/transforms/string/stringConcealing.ts +39 -9
  90. package/src/transforms/string/stringEncoding.ts +18 -18
  91. package/src/transforms/transform.ts +21 -23
  92. package/src/traverse.ts +23 -4
  93. package/src/types.ts +2 -1
  94. package/src/util/gen.ts +28 -3
  95. package/src/util/identifiers.ts +43 -2
  96. package/src/util/insert.ts +38 -3
  97. package/src/util/random.ts +13 -0
  98. package/test/code/Cash.test.ts +1 -1
  99. package/test/code/Dynamic.test.ts +12 -10
  100. package/test/code/ES6.src.js +146 -0
  101. package/test/code/ES6.test.ts +28 -2
  102. package/test/index.test.ts +2 -1
  103. package/test/probability.test.ts +44 -0
  104. package/test/templates/template.test.ts +1 -1
  105. package/test/transforms/antiTooling.test.ts +22 -0
  106. package/test/transforms/calculator.test.ts +40 -0
  107. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +702 -160
  108. package/test/transforms/controlFlowFlattening/expressionObfuscation.test.ts +173 -0
  109. package/test/transforms/deadCode.test.ts +66 -15
  110. package/test/transforms/dispatcher.test.ts +20 -1
  111. package/test/transforms/es5/antiDestructuring.test.ts +16 -0
  112. package/test/transforms/flatten.test.ts +399 -86
  113. package/test/transforms/identifier/movedDeclarations.test.ts +63 -8
  114. package/test/transforms/identifier/renameVariables.test.ts +119 -0
  115. package/test/transforms/lock/antiDebug.test.ts +2 -2
  116. package/test/transforms/lock/lock.test.ts +1 -48
  117. package/test/transforms/minify.test.ts +104 -0
  118. package/test/transforms/preparation.test.ts +157 -0
  119. package/test/transforms/rgf.test.ts +261 -381
  120. package/test/transforms/stack.test.ts +143 -21
  121. package/test/transforms/string/stringCompression.test.ts +39 -0
  122. package/test/transforms/string/stringConcealing.test.ts +82 -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/identifiers.test.ts +113 -1
  127. package/test/util/insert.test.ts +57 -3
  128. package/src/transforms/controlFlowFlattening/choiceFlowObfuscation.ts +0 -87
  129. package/src/transforms/controlFlowFlattening/controlFlowObfuscation.ts +0 -203
  130. package/src/transforms/controlFlowFlattening/switchCaseObfuscation.ts +0 -130
  131. package/src/transforms/eval.ts +0 -89
  132. package/src/transforms/hideInitializingCode.ts +0 -432
  133. package/src/transforms/identifier/nameRecycling.ts +0 -280
  134. package/src/transforms/label.ts +0 -64
  135. package/src/transforms/preparation/nameConflicts.ts +0 -102
  136. package/src/transforms/preparation/preparation.ts +0 -176
  137. package/test/transforms/controlFlowFlattening/controlFlowObfuscation.test.ts +0 -101
  138. package/test/transforms/controlFlowFlattening/switchCaseObfuscation.test.ts +0 -120
  139. package/test/transforms/eval.test.ts +0 -131
  140. package/test/transforms/hideInitializingCode.test.ts +0 -336
  141. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
  142. package/test/transforms/preparation/nameConflicts.test.ts +0 -52
  143. 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),
246
+ MemberExpression(Identifier(fnName), Literal("apply"), true),
217
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),
254
+ MemberExpression(Identifier(fnName), Literal("call"), true),
225
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
  }
@@ -9,7 +9,11 @@ import { ok } from "assert";
9
9
  import Obfuscator from "../obfuscator";
10
10
  import { ObfuscateOptions } from "../options";
11
11
  import { ComputeProbabilityMap } from "../probability";
12
- import { reservedIdentifiers, reservedKeywords } from "../constants";
12
+ import {
13
+ placeholderVariablePrefix,
14
+ reservedIdentifiers,
15
+ reservedKeywords,
16
+ } from "../constants";
13
17
  import { ObfuscateOrder } from "../order";
14
18
 
15
19
  /**
@@ -74,11 +78,6 @@ export default class Transform {
74
78
  */
75
79
  after: Transform[];
76
80
 
77
- /**
78
- * Transformations to run at the same time (can cause conflicts so use sparingly)
79
- */
80
- concurrent: Transform[];
81
-
82
81
  constructor(obfuscator, priority: number = -1) {
83
82
  ok(obfuscator instanceof Obfuscator, "obfuscator should be an Obfuscator");
84
83
 
@@ -89,8 +88,6 @@ export default class Transform {
89
88
 
90
89
  this.before = [];
91
90
  this.after = [];
92
-
93
- this.concurrent = [];
94
91
  }
95
92
 
96
93
  /**
@@ -120,14 +117,11 @@ export default class Transform {
120
117
  */
121
118
  this.before.forEach((x) => x.apply(tree));
122
119
 
120
+ /**
121
+ * Run this transformation
122
+ */
123
123
  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());
124
+ return this.input(object, parents);
131
125
  });
132
126
 
133
127
  /**
@@ -186,23 +180,23 @@ export default class Transform {
186
180
  [...Array(size)]
187
181
  .map(() => Math.floor(Math.random() * 10).toString(10))
188
182
  .join("");
189
- return "__p_" + genRanHex(10);
183
+ return placeholderVariablePrefix + genRanHex(10);
190
184
  }
191
185
 
192
186
  /**
193
187
  * Returns an independent name generator with it's own counter.
194
- * @param offset
188
+ * @param overrideMode - Override the user's `identifierGenerator` option
195
189
  * @returns
196
190
  */
197
- getGenerator(offset = 0) {
198
- var count = offset;
191
+ getGenerator(overrideMode?: string) {
192
+ var count = 0;
199
193
  var identifiers = new Set();
200
194
  return {
201
195
  generate: () => {
202
- var retValue;
196
+ var retValue: string;
203
197
  do {
204
198
  count++;
205
- retValue = this.generateIdentifier(-1, count);
199
+ retValue = this.generateIdentifier(-1, count, overrideMode);
206
200
  } while (identifiers.has(retValue));
207
201
 
208
202
  identifiers.add(retValue);
@@ -217,7 +211,11 @@ export default class Transform {
217
211
  * @param length Default length is 6 to 10 characters.
218
212
  * @returns **`string`**
219
213
  */
220
- generateIdentifier(length: number = -1, count = -1): string {
214
+ generateIdentifier(
215
+ length: number = -1,
216
+ count = -1,
217
+ overrideMode?: string
218
+ ): string {
221
219
  if (length == -1) {
222
220
  length = getRandomInteger(6, 8);
223
221
  }
@@ -233,7 +231,7 @@ export default class Transform {
233
231
  var identifier;
234
232
  do {
235
233
  identifier = ComputeProbabilityMap(
236
- this.options.identifierGenerator,
234
+ overrideMode || this.options.identifierGenerator,
237
235
  (mode = "randomized") => {
238
236
  switch (mode) {
239
237
  case "randomized":
package/src/traverse.ts CHANGED
@@ -44,9 +44,9 @@ export function walk(
44
44
  if (typeof object === "object" && object) {
45
45
  var newParents: Node[] = [object as Node, ...parents];
46
46
 
47
- if (!Array.isArray(object)) {
48
- validateChain(object, parents);
49
- }
47
+ // if (!Array.isArray(object)) {
48
+ // validateChain(object, parents);
49
+ // }
50
50
 
51
51
  // 1. Call `onEnter` function and remember any onExit callback returned
52
52
  var onExit = onEnter(object as Node, parents);
@@ -59,7 +59,6 @@ export function walk(
59
59
  return "EXIT";
60
60
  }
61
61
  }
62
- copy.forEach((x) => {});
63
62
  } else {
64
63
  var keys = Object.keys(object);
65
64
  for (var key of keys) {
@@ -99,3 +98,23 @@ export function walk(
99
98
  export default function traverse(tree, onEnter: EnterCallback) {
100
99
  walk(tree, [], onEnter);
101
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,7 +118,8 @@ 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
121
+ callback: (name: string, complete: number, totalTransforms: number) => void,
122
+ performance: Performance
122
123
  ) => Promise<{
123
124
  obfuscated: string;
124
125
  transformationTimes: { [transformName: string]: number };
package/src/util/gen.ts CHANGED
@@ -171,7 +171,12 @@ export function BreakStatement(label?: string) {
171
171
  };
172
172
  }
173
173
 
174
- export function Property(key: Node, value: Node, computed = false) {
174
+ export function Property(
175
+ key: Node,
176
+ value: Node,
177
+ computed = false,
178
+ kind: "init" | "set" | "get" = "init"
179
+ ) {
175
180
  if (!key) {
176
181
  throw new Error("key is undefined");
177
182
  }
@@ -183,7 +188,7 @@ export function Property(key: Node, value: Node, computed = false) {
183
188
  key: key,
184
189
  computed: computed,
185
190
  value: value,
186
- kind: "init",
191
+ kind: kind,
187
192
  method: false,
188
193
  shorthand: false,
189
194
  };
@@ -557,6 +562,22 @@ export function ClassDeclaration(
557
562
  } as Node;
558
563
  }
559
564
 
565
+ export function ClassExpression(
566
+ id: Node | null,
567
+ superClass: Node = null,
568
+ body: Node[] = []
569
+ ) {
570
+ return {
571
+ type: "ClassExpression",
572
+ id: id,
573
+ superClass: superClass,
574
+ body: {
575
+ type: "ClassBody",
576
+ body: body,
577
+ },
578
+ } as Node;
579
+ }
580
+
560
581
  export function ThrowStatement(argument: Node) {
561
582
  return {
562
583
  type: "ThrowStatement",
@@ -607,7 +628,11 @@ export function CatchClause(param: Node = null, body) {
607
628
  };
608
629
  }
609
630
 
610
- export function TryStatement(body: Node[], handler: Node, finallyBody: Node[]) {
631
+ export function TryStatement(
632
+ body: Node[],
633
+ handler: Node,
634
+ finallyBody?: Node[]
635
+ ) {
611
636
  ok(handler);
612
637
  ok(handler.type == "CatchClause");
613
638
  return {
@@ -30,6 +30,23 @@ export function validateChain(object: Node, parents: Node[]) {
30
30
  }
31
31
  }
32
32
 
33
+ function objectPatternCheck(object: Node, parents: Node[]) {
34
+ var objectPatternIndex = parents.findIndex((x) => x.type === "ObjectPattern");
35
+ if (objectPatternIndex == -1) {
36
+ return true;
37
+ }
38
+
39
+ var property = parents[objectPatternIndex].properties.find(
40
+ (property) => parents[objectPatternIndex - 2] === property
41
+ );
42
+
43
+ if (property.key === (parents[objectPatternIndex - 3] || object)) {
44
+ return false;
45
+ }
46
+
47
+ return true;
48
+ }
49
+
33
50
  /**
34
51
  * Returns detailed information about the given Identifier node.
35
52
  * @param object
@@ -74,7 +91,22 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
74
91
  var isVariableDeclaration =
75
92
  varIndex != -1 &&
76
93
  parents[varIndex].id == (parents[varIndex - 1] || object) &&
77
- parents.find((x) => x.type == "VariableDeclaration");
94
+ parents.find((x) => x.type == "VariableDeclaration") &&
95
+ objectPatternCheck(object, parents);
96
+
97
+ // Assignment pattern check!
98
+ if (isVariableDeclaration) {
99
+ var slicedParents = parents.slice(0, varIndex - 1);
100
+ var i = 0;
101
+ for (var parent of slicedParents) {
102
+ var childNode = slicedParents[i - 1] || object;
103
+ if (parent.type === "AssignmentPattern" && parent.right === childNode) {
104
+ isVariableDeclaration = false;
105
+ break;
106
+ }
107
+ i++;
108
+ }
109
+ }
78
110
 
79
111
  var forIndex = parents.findIndex((x) => x.type == "ForStatement");
80
112
  var isForInitializer =
@@ -87,6 +119,12 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
87
119
  functionIndex != -1 &&
88
120
  parents[functionIndex].type == "FunctionDeclaration" &&
89
121
  parents[functionIndex].id == object;
122
+
123
+ var isNamedFunctionExpression =
124
+ functionIndex != -1 &&
125
+ parents[functionIndex].type === "FunctionExpression" &&
126
+ parents[functionIndex].id === object;
127
+
90
128
  var isAFunctionParameter = isFunctionParameter(object, parents);
91
129
 
92
130
  var isClauseParameter = false;
@@ -112,7 +150,9 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
112
150
 
113
151
  var isAssignmentLeft =
114
152
  assignmentIndex !== -1 &&
115
- parents[assignmentIndex].left === (parents[assignmentIndex - 1] || object);
153
+ parents[assignmentIndex].left ===
154
+ (parents[assignmentIndex - 1] || object) &&
155
+ objectPatternCheck(object, parents);
116
156
  var isAssignmentValue =
117
157
  assignmentIndex !== -1 &&
118
158
  parents[assignmentIndex].right === (parents[assignmentIndex - 1] || object);
@@ -272,6 +312,7 @@ export function getIdentifierInfo(object: Node, parents: Node[]) {
272
312
  isDefined:
273
313
  isVariableDeclaration ||
274
314
  isFunctionDeclaration ||
315
+ isNamedFunctionExpression ||
275
316
  isAFunctionParameter ||
276
317
  isClassDeclaration ||
277
318
  isClauseParameter ||
@@ -145,13 +145,13 @@ export function getAllDefiningContexts(o: Node, p: Node[]): Node[] {
145
145
  // Get Function
146
146
  var fn = getFunction(o, p);
147
147
 
148
- contexts.push(fn.body);
148
+ // contexts.push(fn.body);
149
149
  }
150
150
 
151
151
  if (info.isClauseParameter) {
152
152
  var catchClause = p.find((x) => x.type === "CatchClause");
153
153
  if (catchClause) {
154
- contexts.push(catchClause.body);
154
+ return [catchClause];
155
155
  }
156
156
  }
157
157
 
@@ -280,8 +280,19 @@ export function prepend(block: Node, ...nodes: Node[]) {
280
280
  });
281
281
 
282
282
  block.body.splice(moveBy, 0, ...nodes);
283
+ } else if (block.type === "SwitchCase") {
284
+ block.consequent.unshift(...nodes);
283
285
  } else {
284
- 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
+ }
285
296
  }
286
297
  }
287
298
 
@@ -365,3 +376,27 @@ export function isForInitialize(
365
376
 
366
377
  return false;
367
378
  }
379
+
380
+ /**
381
+ * Computes the `function.length` property given the parameter nodes.
382
+ *
383
+ * @param params
384
+ * @returns
385
+ */
386
+ export function computeFunctionLength(params: Node[]): number {
387
+ var count = 0;
388
+
389
+ for (var parameterNode of params) {
390
+ if (
391
+ parameterNode.type === "Identifier" ||
392
+ parameterNode.type === "ObjectPattern" ||
393
+ parameterNode.type === "ArrayPattern"
394
+ ) {
395
+ count++;
396
+ } else {
397
+ break;
398
+ }
399
+ }
400
+
401
+ return count;
402
+ }
@@ -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",