js-confuser 1.5.7 → 1.5.9

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 (73) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/dist/index.js +45 -4
  3. package/dist/obfuscator.js +10 -5
  4. package/dist/options.js +6 -7
  5. package/dist/order.js +3 -3
  6. package/dist/transforms/antiTooling.js +1 -1
  7. package/dist/transforms/controlFlowFlattening/controlFlowFlattening.js +16 -2
  8. package/dist/transforms/controlFlowFlattening/expressionObfuscation.js +4 -2
  9. package/dist/transforms/dispatcher.js +3 -3
  10. package/dist/transforms/es5/antiClass.js +6 -2
  11. package/dist/transforms/es5/antiDestructuring.js +1 -1
  12. package/dist/transforms/eval.js +11 -0
  13. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +4 -4
  14. package/dist/transforms/extraction/objectExtraction.js +6 -1
  15. package/dist/transforms/flatten.js +73 -50
  16. package/dist/transforms/hexadecimalNumbers.js +34 -9
  17. package/dist/transforms/identifier/movedDeclarations.js +1 -1
  18. package/dist/transforms/identifier/nameRecycling.js +8 -2
  19. package/dist/transforms/identifier/renameVariables.js +9 -0
  20. package/dist/transforms/lock/antiDebug.js +1 -1
  21. package/dist/transforms/minify.js +22 -6
  22. package/dist/transforms/rgf.js +4 -4
  23. package/dist/transforms/stack.js +1 -1
  24. package/dist/transforms/string/stringConcealing.js +77 -40
  25. package/dist/transforms/transform.js +1 -1
  26. package/dist/traverse.js +0 -8
  27. package/dist/util/compare.js +2 -2
  28. package/dist/util/insert.js +20 -6
  29. package/package.json +2 -2
  30. package/src/index.ts +57 -19
  31. package/src/obfuscator.ts +6 -1
  32. package/src/options.ts +20 -6
  33. package/src/order.ts +3 -3
  34. package/src/transforms/antiTooling.ts +1 -1
  35. package/src/transforms/controlFlowFlattening/controlFlowFlattening.ts +16 -1
  36. package/src/transforms/controlFlowFlattening/expressionObfuscation.ts +14 -2
  37. package/src/transforms/dispatcher.ts +4 -3
  38. package/src/transforms/es5/antiClass.ts +10 -1
  39. package/src/transforms/es5/antiDestructuring.ts +1 -1
  40. package/src/transforms/eval.ts +18 -0
  41. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +5 -5
  42. package/src/transforms/extraction/objectExtraction.ts +12 -5
  43. package/src/transforms/flatten.ts +181 -128
  44. package/src/transforms/hexadecimalNumbers.ts +37 -9
  45. package/src/transforms/identifier/movedDeclarations.ts +1 -1
  46. package/src/transforms/identifier/nameRecycling.ts +14 -3
  47. package/src/transforms/identifier/renameVariables.ts +19 -0
  48. package/src/transforms/lock/antiDebug.ts +1 -1
  49. package/src/transforms/minify.ts +37 -5
  50. package/src/transforms/rgf.ts +4 -3
  51. package/src/transforms/stack.ts +3 -1
  52. package/src/transforms/string/stringConcealing.ts +120 -56
  53. package/src/transforms/transform.ts +1 -1
  54. package/src/traverse.ts +1 -8
  55. package/src/types.ts +9 -1
  56. package/src/util/compare.ts +2 -2
  57. package/src/util/insert.ts +37 -8
  58. package/test/code/ES6.src.js +14 -0
  59. package/test/code/NewFeatures.test.ts +19 -0
  60. package/test/index.test.ts +13 -1
  61. package/test/transforms/antiTooling.test.ts +30 -0
  62. package/test/transforms/controlFlowFlattening/controlFlowFlattening.test.ts +58 -0
  63. package/test/transforms/dispatcher.test.ts +24 -0
  64. package/test/transforms/es5/antiClass.test.ts +33 -0
  65. package/test/transforms/eval.test.ts +53 -0
  66. package/test/transforms/extraction/objectExtraction.test.ts +21 -0
  67. package/test/transforms/flatten.test.ts +146 -3
  68. package/test/transforms/identifier/nameRecycling.test.ts +39 -0
  69. package/test/transforms/identifier/renameVariables.test.ts +64 -0
  70. package/test/transforms/minify.test.ts +66 -0
  71. package/test/transforms/rgf.test.ts +56 -0
  72. package/test/transforms/string/stringConcealing.test.ts +33 -0
  73. package/test/util/compare.test.ts +23 -1
@@ -15,6 +15,7 @@ import {
15
15
  AssignmentExpression,
16
16
  VariableDeclarator,
17
17
  Identifier,
18
+ CallExpression,
18
19
  } from "../util/gen";
19
20
  import {
20
21
  getBlockBody,
@@ -22,11 +23,14 @@ import {
22
23
  isForInitialize,
23
24
  isLexContext,
24
25
  getFunction,
26
+ prepend,
27
+ append,
25
28
  } from "../util/insert";
26
29
  import { isValidIdentifier, isEquivalent } from "../util/compare";
27
30
  import { walk, isBlock } from "../traverse";
28
31
  import { ok } from "assert";
29
32
  import { isLexicalScope } from "../util/scope";
33
+ import Template from "../templates/template";
30
34
 
31
35
  /**
32
36
  * Basic transformations to reduce code size.
@@ -37,12 +41,13 @@ import { isLexicalScope } from "../util/scope";
37
41
  * - `x['y']` **->** `x.y`
38
42
  */
39
43
  export default class Minify extends Transform {
40
- variables: Map<Node, Location[]>;
44
+ /**
45
+ * A helper function that is introduced preserve function semantics
46
+ */
47
+ arrowFunctionName: string;
41
48
 
42
49
  constructor(o) {
43
50
  super(o, ObfuscateOrder.Minify);
44
-
45
- this.variables = new Map();
46
51
  }
47
52
 
48
53
  match(object: Node, parents: Node[]) {
@@ -239,17 +244,42 @@ export default class Minify extends Transform {
239
244
  });
240
245
 
241
246
  if (canTransform) {
247
+ if (!this.arrowFunctionName) {
248
+ this.arrowFunctionName = this.getPlaceholder();
249
+
250
+ append(
251
+ parents[parents.length - 1] || object,
252
+ Template(`
253
+ function ${this.arrowFunctionName}(arrowFn){
254
+ return function(){ return arrowFn(...arguments) }
255
+ }
256
+ `).single()
257
+ );
258
+ }
259
+
260
+ const wrap = (object: Node) => {
261
+ return CallExpression(Identifier(this.arrowFunctionName), [
262
+ clone(object),
263
+ ]);
264
+ };
265
+
242
266
  if (object.type == "FunctionExpression") {
243
267
  object.type = "ArrowFunctionExpression";
268
+
269
+ this.replace(object, wrap(clone(object)));
244
270
  } else {
245
271
  var arrow = { ...clone(object), type: "ArrowFunctionExpression" };
246
272
  this.replace(
247
273
  object,
248
- VariableDeclaration(VariableDeclarator(object.id.name, arrow))
274
+ VariableDeclaration(
275
+ VariableDeclarator(object.id.name, wrap(arrow))
276
+ )
249
277
  );
250
278
 
251
279
  var x = this.transform(arrow, []);
252
- x();
280
+ if (typeof x === "function") {
281
+ x();
282
+ }
253
283
  }
254
284
  }
255
285
  };
@@ -442,6 +472,8 @@ export default class Minify extends Transform {
442
472
  }
443
473
 
444
474
  if (
475
+ object.consequent &&
476
+ object.consequent.body &&
445
477
  object.consequent.body.length == 1 &&
446
478
  object.alternate &&
447
479
  object.alternate.body.length == 1
@@ -31,6 +31,7 @@ import {
31
31
  isFunction,
32
32
  prepend,
33
33
  getDefiningContext,
34
+ clone,
34
35
  } from "../util/insert";
35
36
  import { getRandomString } from "../util/random";
36
37
  import Transform from "./transform";
@@ -300,7 +301,8 @@ export default class RGF extends Transform {
300
301
  [],
301
302
  [
302
303
  ReturnStatement(
303
- CallExpression(memberExpression, [
304
+ // clone() is required!
305
+ CallExpression(clone(memberExpression), [
304
306
  Identifier(referenceArray),
305
307
  SpreadElement(Identifier("arguments")),
306
308
  ])
@@ -328,8 +330,6 @@ export default class RGF extends Transform {
328
330
 
329
331
  queue.forEach(([object, parents]) => {
330
332
  var name = object?.id?.name;
331
- var hasName = !!name;
332
- var params = object.params.map((x) => x.name) || [];
333
333
  var signature = referenceSignatures[names.get(name)];
334
334
 
335
335
  var embeddedName = name || this.getPlaceholder();
@@ -349,6 +349,7 @@ export default class RGF extends Transform {
349
349
  },
350
350
  eval: false,
351
351
  hideInitializingCode: false,
352
+ stringEncoding: false,
352
353
  });
353
354
  var transforms = Object.values(obfuscator.transforms).filter(
354
355
  (x) => x.priority > this.priority
@@ -55,7 +55,9 @@ export default class Stack extends Transform {
55
55
  return () => {
56
56
  // Uncaught SyntaxError: Getter must not have any formal parameters.
57
57
  // Uncaught SyntaxError: Setter must have exactly one formal parameter
58
- var propIndex = parents.findIndex((x) => x.type == "Property");
58
+ var propIndex = parents.findIndex(
59
+ (x) => x.type === "Property" || x.type === "MethodDefinition"
60
+ );
59
61
  if (propIndex !== -1) {
60
62
  if (parents[propIndex].value === (parents[propIndex - 1] || object)) {
61
63
  if (parents[propIndex].kind !== "init" || parents[propIndex].method) {
@@ -6,22 +6,18 @@ import { isDirective } from "../../util/compare";
6
6
  import {
7
7
  ArrayExpression,
8
8
  CallExpression,
9
- ConditionalExpression,
10
- FunctionDeclaration,
11
9
  FunctionExpression,
12
10
  Identifier,
13
- IfStatement,
14
11
  Literal,
15
12
  MemberExpression,
16
13
  Node,
17
- ReturnStatement,
18
- SequenceExpression,
14
+ ObjectExpression,
15
+ Property,
19
16
  ThisExpression,
20
- UpdateExpression,
21
17
  VariableDeclaration,
22
18
  VariableDeclarator,
23
19
  } from "../../util/gen";
24
- import { append, isLexContext, isVarContext, prepend } from "../../util/insert";
20
+ import { append, prepend } from "../../util/insert";
25
21
  import { choice, getRandomInteger, getRandomString } from "../../util/random";
26
22
  import Transform from "../transform";
27
23
  import Encoding from "./encoding";
@@ -65,6 +61,7 @@ export default class StringConcealing extends Transform {
65
61
  ignore = new Set<string>();
66
62
  variablesMade = 1;
67
63
  encoding: { [type: string]: string } = Object.create(null);
64
+ gen: ReturnType<Transform["getGenerator"]>;
68
65
 
69
66
  hasAllEncodings: boolean;
70
67
 
@@ -75,11 +72,12 @@ export default class StringConcealing extends Transform {
75
72
  this.index = Object.create(null);
76
73
  this.arrayExpression = ArrayExpression([]);
77
74
  this.hasAllEncodings = false;
75
+ this.gen = this.getGenerator();
78
76
 
79
77
  // Pad array with useless strings
80
- var dead = getRandomInteger(4, 10);
78
+ var dead = getRandomInteger(5, 15);
81
79
  for (var i = 0; i < dead; i++) {
82
- var str = getRandomString(getRandomInteger(4, 20));
80
+ var str = getRandomString(getRandomInteger(5, 40));
83
81
  var fn = this.transform(Literal(str), []);
84
82
  if (fn) {
85
83
  fn();
@@ -134,7 +132,7 @@ export default class StringConcealing extends Transform {
134
132
  VariableDeclarator("a", this.arrayExpression)
135
133
  ),
136
134
  Template(
137
- `return (${flowIntegrity} ? a.pop() : ${flowIntegrity}++, a)`
135
+ `return (${flowIntegrity} ? a["pop"]() : ${flowIntegrity}++, a)`
138
136
  ).single(),
139
137
  ]
140
138
  ),
@@ -159,7 +157,11 @@ export default class StringConcealing extends Transform {
159
157
  transform(object: Node, parents: Node[]) {
160
158
  return () => {
161
159
  // Empty strings are discarded
162
- if (!object.value || this.ignore.has(object.value)) {
160
+ if (
161
+ !object.value ||
162
+ this.ignore.has(object.value) ||
163
+ object.value.length == 0
164
+ ) {
163
165
  return;
164
166
  }
165
167
 
@@ -188,69 +190,131 @@ export default class StringConcealing extends Transform {
188
190
  var encoded = encoder.encode(object.value);
189
191
  if (encoder.decode(encoded) != object.value) {
190
192
  this.ignore.add(object.value);
191
- this.warn(object.value.slice(0, 100));
193
+ this.warn(type, object.value.slice(0, 100));
192
194
  return;
193
195
  }
194
196
 
195
- // Fix 1. weird undefined error
196
- if (object.value && object.value.length > 0) {
197
- var index = -1;
198
- if (!this.set.has(object.value)) {
199
- this.arrayExpression.elements.push(Literal(encoded));
200
- index = this.arrayExpression.elements.length - 1;
201
- this.index[object.value] = [index, fnName];
197
+ var index = -1;
198
+ if (!this.set.has(object.value)) {
199
+ this.arrayExpression.elements.push(Literal(encoded));
200
+ index = this.arrayExpression.elements.length - 1;
201
+ this.index[object.value] = [index, fnName];
202
202
 
203
- this.set.add(object.value);
204
- } else {
205
- [index, fnName] = this.index[object.value];
206
- ok(typeof index === "number");
207
- }
203
+ this.set.add(object.value);
204
+ } else {
205
+ [index, fnName] = this.index[object.value];
206
+ ok(typeof index === "number");
207
+ }
208
208
 
209
- ok(index != -1, "index == -1");
209
+ ok(index != -1, "index == -1");
210
210
 
211
- var callExpr = CallExpression(Identifier(fnName), [Literal(index)]);
211
+ var callExpr = CallExpression(Identifier(fnName), [Literal(index)]);
212
212
 
213
- // use `.apply` to fool automated de-obfuscators
214
- if (Math.random() > 0.5) {
215
- callExpr = CallExpression(
216
- MemberExpression(Identifier(fnName), Identifier("apply"), false),
217
- [ThisExpression(), ArrayExpression([Literal(index)])]
218
- );
219
- }
213
+ // use `.apply` to fool automated de-obfuscators
214
+ if (Math.random() > 0.5) {
215
+ callExpr = CallExpression(
216
+ MemberExpression(Identifier(fnName), Identifier("apply"), false),
217
+ [Identifier("undefined"), ArrayExpression([Literal(index)])]
218
+ );
219
+ }
220
+
221
+ // use `.call`
222
+ else if (Math.random() > 0.5) {
223
+ callExpr = CallExpression(
224
+ MemberExpression(Identifier(fnName), Identifier("call"), false),
225
+ [Identifier("undefined"), Literal(index)]
226
+ );
227
+ }
228
+
229
+ var referenceType = "call";
230
+ if (parents.length && Math.random() < 0.5 / this.variablesMade) {
231
+ referenceType = "constantReference";
232
+ }
233
+
234
+ var newExpr: Node = callExpr;
235
+
236
+ if (referenceType === "constantReference") {
237
+ // Define the string earlier, reference the name here
238
+ this.variablesMade++;
220
239
 
221
- // use `.call`
222
- else if (Math.random() > 0.5) {
223
- callExpr = CallExpression(
224
- MemberExpression(Identifier(fnName), Identifier("call"), false),
225
- [ThisExpression(), Literal(index)]
226
- );
240
+ var constantReferenceType = choice(["variable", "array", "object"]);
241
+
242
+ var place = choice(parents.filter((node) => isBlock(node)));
243
+ if (!place) {
244
+ this.error(new Error("No lexical block to insert code"));
227
245
  }
228
246
 
229
- var constantReference =
230
- parents.length && Math.random() > 0.5 / this.variablesMade;
247
+ switch (constantReferenceType) {
248
+ case "variable":
249
+ var name = this.getPlaceholder();
250
+
251
+ prepend(
252
+ place,
253
+ VariableDeclaration(VariableDeclarator(name, callExpr))
254
+ );
255
+
256
+ newExpr = Identifier(name);
257
+ break;
258
+ case "array":
259
+ if (!place.$stringConcealingArray) {
260
+ place.$stringConcealingArray = ArrayExpression([]);
261
+ place.$stringConcealingArrayName = this.getPlaceholder();
231
262
 
232
- if (constantReference) {
233
- // Define the string earlier, reference the name here
263
+ prepend(
264
+ place,
265
+ VariableDeclaration(
266
+ VariableDeclarator(
267
+ place.$stringConcealingArrayName,
268
+ place.$stringConcealingArray
269
+ )
270
+ )
271
+ );
272
+ }
234
273
 
235
- var name = this.getPlaceholder();
274
+ var arrayIndex = place.$stringConcealingArray.elements.length;
236
275
 
237
- var place = choice(parents.filter((node) => isBlock(node)));
238
- if (!place) {
239
- this.error(Error("No lexical block to insert code"));
240
- }
276
+ place.$stringConcealingArray.elements.push(callExpr);
241
277
 
242
- place.body.unshift(
243
- VariableDeclaration(VariableDeclarator(name, callExpr))
244
- );
278
+ var memberExpression = MemberExpression(
279
+ Identifier(place.$stringConcealingArrayName),
280
+ Literal(arrayIndex),
281
+ true
282
+ );
245
283
 
246
- this.replaceIdentifierOrLiteral(object, Identifier(name), parents);
284
+ newExpr = memberExpression;
285
+ break;
286
+ case "object":
287
+ if (!place.$stringConcealingObject) {
288
+ place.$stringConcealingObject = ObjectExpression([]);
289
+ place.$stringConcealingObjectName = this.getPlaceholder();
247
290
 
248
- this.variablesMade++;
249
- } else {
250
- // Direct call to the getter function
251
- this.replaceIdentifierOrLiteral(object, callExpr, parents);
291
+ prepend(
292
+ place,
293
+ VariableDeclaration(
294
+ VariableDeclarator(
295
+ place.$stringConcealingObjectName,
296
+ place.$stringConcealingObject
297
+ )
298
+ )
299
+ );
300
+ }
301
+
302
+ var propName = this.gen.generate();
303
+ var property = Property(Literal(propName), callExpr, true);
304
+ place.$stringConcealingObject.properties.push(property);
305
+
306
+ var memberExpression = MemberExpression(
307
+ Identifier(place.$stringConcealingObjectName),
308
+ Literal(propName),
309
+ true
310
+ );
311
+
312
+ newExpr = memberExpression;
313
+ break;
252
314
  }
253
315
  }
316
+
317
+ this.replaceIdentifierOrLiteral(object, newExpr, parents);
254
318
  };
255
319
  }
256
320
  }
@@ -407,7 +407,7 @@ export default class Transform {
407
407
  }
408
408
 
409
409
  /**
410
- * Verbose logging for warning/importing messages.
410
+ * Verbose logging for warning/important messages.
411
411
  * @param messages
412
412
  */
413
413
  warn(...messages: any[]) {
package/src/traverse.ts CHANGED
@@ -39,16 +39,9 @@ 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
47
  if (!Array.isArray(object)) {
package/src/types.ts CHANGED
@@ -119,4 +119,12 @@ export type IJsConfuserDebugObfuscation = (
119
119
  code: string,
120
120
  options: ObfuscateOptions,
121
121
  callback: (name: string, complete: number, totalTransforms: number) => void
122
- ) => Promise<string>;
122
+ ) => Promise<{
123
+ obfuscated: string;
124
+ transformationTimes: { [transformName: string]: number };
125
+ parseTime: number;
126
+ compileTime: number;
127
+ obfuscationTime: number;
128
+ totalTransforms: number;
129
+ totalPossibleTransforms: number;
130
+ }>;
@@ -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(
@@ -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,16 +261,25 @@ 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);
268
283
  } else {
269
284
  getBlockBody(block).unshift(...nodes);
270
285
  }
@@ -313,7 +328,10 @@ export function clone<T>(object: T): T {
313
328
  * @param p
314
329
  * @returns
315
330
  */
316
- export function isForInitialize(o, p): "initializer" | "left-hand" | false {
331
+ export function isForInitialize(
332
+ o: Node,
333
+ p: Node[]
334
+ ): "initializer" | "left-hand" | false {
317
335
  validateChain(o, p);
318
336
 
319
337
  var forIndex = p.findIndex(
@@ -322,6 +340,17 @@ export function isForInitialize(o, p): "initializer" | "left-hand" | false {
322
340
  x.type == "ForInStatement" ||
323
341
  x.type == "ForOfStatement"
324
342
  );
343
+
344
+ if (
345
+ p
346
+ .slice(0, forIndex)
347
+ .find((x) =>
348
+ ["ArrowFunctionExpression", "BlockStatement"].includes(x.type)
349
+ )
350
+ ) {
351
+ return false;
352
+ }
353
+
325
354
  if (forIndex !== -1) {
326
355
  if (p[forIndex].type == "ForStatement") {
327
356
  if (p[forIndex].init == (p[forIndex - 1] || o)) {
@@ -73,3 +73,17 @@ for (var x of [3, 3, 5]) {
73
73
  sum += x;
74
74
  }
75
75
  expect(sum).toStrictEqual(11);
76
+
77
+ // Variant #12 More complex for-loop initializer
78
+ var outside = 12;
79
+ for (
80
+ var myFunction = function () {
81
+ return outside;
82
+ };
83
+ false;
84
+
85
+ ) {}
86
+
87
+ var TEST_OUTPUT = myFunction();
88
+
89
+ expect(TEST_OUTPUT).toStrictEqual(12);
@@ -0,0 +1,19 @@
1
+ import JsConfuser from "../../src/index";
2
+
3
+ // https://github.com/MichaelXF/js-confuser/issues/79
4
+ test("Variant #1: Support BigInt Literals (1n)", async () => {
5
+ var code = `
6
+ TEST_OUTPUT = 1n;
7
+ `;
8
+
9
+ var output = await JsConfuser(code, {
10
+ target: "node",
11
+ renameVariables: true,
12
+ });
13
+
14
+ var TEST_OUTPUT;
15
+ eval(output);
16
+
17
+ expect(typeof TEST_OUTPUT).toStrictEqual("bigint");
18
+ expect(TEST_OUTPUT).toStrictEqual(1n);
19
+ });
@@ -230,7 +230,19 @@ describe("debugObfuscation", () => {
230
230
  callback
231
231
  );
232
232
 
233
- expect(typeof output).toStrictEqual("string");
233
+ expect(typeof output).toStrictEqual("object");
234
+ expect(typeof output.obfuscated).toStrictEqual("string");
235
+ expect(typeof output.obfuscationTime).toStrictEqual("number");
236
+ expect(typeof output.compileTime).toStrictEqual("number");
237
+ expect(typeof output.parseTime).toStrictEqual("number");
238
+ expect(typeof output.totalPossibleTransforms).toStrictEqual("number");
239
+ expect(typeof output.totalTransforms).toStrictEqual("number");
240
+ expect(typeof output.transformationTimes).toStrictEqual("object");
241
+ expect(typeof output.transformationTimes.RenameVariables).toStrictEqual(
242
+ "number"
243
+ );
244
+
245
+ eval(output.obfuscated);
234
246
  expect(called).toStrictEqual(true);
235
247
  });
236
248
  });
@@ -0,0 +1,30 @@
1
+ import JsConfuser from "../../src/index";
2
+
3
+ // https://github.com/MichaelXF/js-confuser/issues/74
4
+ test("Variant #1: Don't break Symbols", async () => {
5
+ if (typeof Symbol !== "undefined") {
6
+ for (var i = 0; i < 6; i++) {
7
+ var output = await JsConfuser(
8
+ `
9
+
10
+ var sym1 = Symbol();
11
+
12
+ if (true) {
13
+ sym1;
14
+ sym1;
15
+ sym1;
16
+ }
17
+
18
+ TEST_OUTPUT = sym1;
19
+
20
+ `,
21
+ { target: "node", renameVariables: true }
22
+ );
23
+
24
+ var TEST_OUTPUT;
25
+
26
+ eval(output);
27
+ expect(typeof TEST_OUTPUT).toStrictEqual("symbol");
28
+ }
29
+ }
30
+ });
@@ -672,3 +672,61 @@ test("Variant #20: Don't apply when functions are redefined", async () => {
672
672
  eval(output);
673
673
  expect(TEST_ARRAY).toStrictEqual([0, 0, 0]);
674
674
  });
675
+
676
+ // https://github.com/MichaelXF/js-confuser/issues/70
677
+ test("Variant #21: Don't move Import Declarations", async () => {
678
+ var output = await JsConfuser(
679
+ `
680
+ import {createHash} from "crypto";
681
+ var inputString = "Hash this string";
682
+ var hashed = createHash("sha256").update(inputString).digest("hex");
683
+ TEST_OUTPUT = hashed;
684
+ `,
685
+ {
686
+ target: "node",
687
+ controlFlowFlattening: true,
688
+ }
689
+ );
690
+
691
+ // Ensure Control Flow FLattening was applied
692
+ expect(output).toContain("switch");
693
+
694
+ // Ensure the import declaration wasn't moved
695
+ expect(output.startsWith("import")).toStrictEqual(true);
696
+
697
+ // Convert to runnable code
698
+ output = output.replace(
699
+ `import{createHash}from'crypto';`,
700
+ "const {createHash}=require('crypto');"
701
+ );
702
+
703
+ var TEST_OUTPUT = "";
704
+
705
+ eval(output);
706
+
707
+ expect(TEST_OUTPUT).toStrictEqual(
708
+ "1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
709
+ );
710
+ });
711
+
712
+ // https://github.com/MichaelXF/js-confuser/issues/81
713
+ test("Variant #22: Don't break typeof expression", async () => {
714
+ var output = await JsConfuser(
715
+ `
716
+ TEST_OUTPUT = false;
717
+ if(typeof nonExistentVariable === "undefined") {
718
+ TEST_OUTPUT = true;
719
+ }
720
+ `,
721
+ {
722
+ target: "node",
723
+ controlFlowFlattening: true,
724
+ }
725
+ );
726
+
727
+ var TEST_OUTPUT;
728
+
729
+ eval(output);
730
+
731
+ expect(TEST_OUTPUT).toStrictEqual(true);
732
+ });