js-confuser 1.6.0 → 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 (59) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +215 -170
  3. package/dist/constants.js +6 -2
  4. package/dist/obfuscator.js +0 -6
  5. package/dist/options.js +4 -4
  6. package/dist/presets.js +6 -7
  7. package/dist/templates/crash.js +2 -2
  8. package/dist/templates/functionLength.js +16 -0
  9. package/dist/transforms/dispatcher.js +4 -1
  10. package/dist/transforms/extraction/duplicateLiteralsRemoval.js +89 -58
  11. package/dist/transforms/flatten.js +224 -147
  12. package/dist/transforms/identifier/movedDeclarations.js +38 -85
  13. package/dist/transforms/identifier/renameVariables.js +94 -41
  14. package/dist/transforms/lock/lock.js +0 -37
  15. package/dist/transforms/minify.js +2 -2
  16. package/dist/transforms/rgf.js +139 -246
  17. package/dist/transforms/stack.js +42 -1
  18. package/dist/transforms/transform.js +1 -1
  19. package/dist/util/gen.js +2 -1
  20. package/dist/util/identifiers.js +37 -3
  21. package/dist/util/insert.js +24 -3
  22. package/docs/ControlFlowFlattening.md +595 -0
  23. package/{Countermeasures.md → docs/Countermeasures.md} +1 -15
  24. package/{Integrity.md → docs/Integrity.md} +2 -2
  25. package/docs/RGF.md +419 -0
  26. package/package.json +1 -1
  27. package/src/constants.ts +3 -0
  28. package/src/obfuscator.ts +0 -4
  29. package/src/options.ts +9 -86
  30. package/src/presets.ts +6 -7
  31. package/src/templates/crash.ts +10 -10
  32. package/src/templates/functionLength.ts +14 -0
  33. package/src/transforms/dispatcher.ts +5 -1
  34. package/src/transforms/extraction/duplicateLiteralsRemoval.ts +130 -129
  35. package/src/transforms/flatten.ts +357 -290
  36. package/src/transforms/identifier/movedDeclarations.ts +50 -96
  37. package/src/transforms/identifier/renameVariables.ts +120 -56
  38. package/src/transforms/lock/lock.ts +1 -42
  39. package/src/transforms/minify.ts +11 -2
  40. package/src/transforms/rgf.ts +214 -404
  41. package/src/transforms/stack.ts +62 -0
  42. package/src/transforms/transform.ts +6 -2
  43. package/src/util/gen.ts +7 -2
  44. package/src/util/identifiers.ts +43 -2
  45. package/src/util/insert.ts +26 -2
  46. package/test/code/ES6.src.js +24 -0
  47. package/test/transforms/flatten.test.ts +352 -88
  48. package/test/transforms/identifier/movedDeclarations.test.ts +37 -9
  49. package/test/transforms/identifier/renameVariables.test.ts +37 -0
  50. package/test/transforms/lock/lock.test.ts +1 -48
  51. package/test/transforms/minify.test.ts +19 -0
  52. package/test/transforms/rgf.test.ts +262 -353
  53. package/test/transforms/stack.test.ts +52 -0
  54. package/test/util/identifiers.test.ts +113 -1
  55. package/test/util/insert.test.ts +57 -3
  56. package/src/transforms/eval.ts +0 -89
  57. package/src/transforms/identifier/nameRecycling.ts +0 -280
  58. package/test/transforms/eval.test.ts +0 -131
  59. package/test/transforms/identifier/nameRecycling.test.ts +0 -205
@@ -1,15 +1,13 @@
1
1
  import JsConfuser from "../../src/index";
2
2
 
3
- it("should bring independent to the global level", async () => {
3
+ test("Variant #1: Function Declaration", async () => {
4
4
  var output = await JsConfuser.obfuscate(
5
5
  `
6
- function container(){
7
- function nested(param){
8
-
9
- }
10
-
11
- nested();
6
+ function myFunction(){
7
+ return 10;
12
8
  }
9
+
10
+ TEST_OUTPUT = myFunction();
13
11
  `,
14
12
  {
15
13
  target: "node",
@@ -17,18 +15,24 @@ it("should bring independent to the global level", async () => {
17
15
  }
18
16
  );
19
17
 
20
- // Ensure flatten was applied
21
- expect(output).toContain("[param]");
18
+ expect(output).toContain("_flat_myFunction");
19
+
20
+ var TEST_OUTPUT;
21
+ eval(output);
22
+
23
+ expect(TEST_OUTPUT).toStrictEqual(10);
22
24
  });
23
25
 
24
- it("should have correct return values", async () => {
26
+ test("Variant #2: Function Expression", async () => {
25
27
  var output = await JsConfuser.obfuscate(
26
28
  `
27
- function TEST_FUNCTION(){
28
- return 10;
29
+ var outsideVar = "Correct Value";
30
+
31
+ var myFunction = function(){
32
+ return outsideVar;
29
33
  }
30
34
 
31
- input(TEST_FUNCTION());
35
+ TEST_OUTPUT = myFunction();
32
36
  `,
33
37
  {
34
38
  target: "node",
@@ -36,21 +40,22 @@ it("should have correct return values", async () => {
36
40
  }
37
41
  );
38
42
 
39
- var value = "never_called",
40
- input = (x) => (value = x);
43
+ expect(output).toContain("_flat_myFunction");
41
44
 
45
+ var TEST_OUTPUT;
42
46
  eval(output);
43
- expect(value).toStrictEqual(10);
47
+
48
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
44
49
  });
45
50
 
46
- it("should have correct parameters", async () => {
51
+ test("Variant #3: Simple parameters", async () => {
47
52
  var output = await JsConfuser.obfuscate(
48
53
  `
49
- function TEST_FUNCTION(x, y){
50
- input(x + y);
54
+ function myFunction(x, y){
55
+ TEST_OUTPUT = x + y;
51
56
  }
52
57
 
53
- TEST_FUNCTION(10, 15);
58
+ myFunction(10, 15);
54
59
  `,
55
60
  {
56
61
  target: "node",
@@ -58,14 +63,15 @@ it("should have correct parameters", async () => {
58
63
  }
59
64
  );
60
65
 
61
- var value = "never_called",
62
- input = (x) => (value = x);
66
+ expect(output).toContain("_flat_myFunction");
67
+
68
+ var TEST_OUTPUT;
63
69
 
64
70
  eval(output);
65
- expect(value).toStrictEqual(25);
71
+ expect(TEST_OUTPUT).toStrictEqual(25);
66
72
  });
67
73
 
68
- it("should have correct parameters when nested", async () => {
74
+ test("Variant #4: Simple parameters nested", async () => {
69
75
  var output = await JsConfuser.obfuscate(
70
76
  `
71
77
  function TEST_FUNCTION(x){
@@ -91,7 +97,7 @@ it("should have correct parameters when nested", async () => {
91
97
  expect(value).toStrictEqual(10);
92
98
  });
93
99
 
94
- it("should have correct return values when nested", async () => {
100
+ test("Variant #5: Correct return values when nested", async () => {
95
101
  var output = await JsConfuser.obfuscate(
96
102
  `
97
103
  function TEST_FUNCTION(){
@@ -117,7 +123,7 @@ it("should have correct return values when nested", async () => {
117
123
  expect(value).toStrictEqual(10);
118
124
  });
119
125
 
120
- it("should have correct values when deeply nested", async () => {
126
+ test("Variant #6: Correct values when deeply nested", async () => {
121
127
  var output = await JsConfuser.obfuscate(
122
128
  `
123
129
  function TEST_FUNCTION(x, y){
@@ -148,7 +154,7 @@ it("should have correct values when deeply nested", async () => {
148
154
  expect(value).toStrictEqual(15);
149
155
  });
150
156
 
151
- it("should have correct values when modifying local variables", async () => {
157
+ test("Variant #7: Correct values when modifying local variables", async () => {
152
158
  var output = await JsConfuser.obfuscate(
153
159
  `
154
160
  function TEST_FUNCTION(x, y){
@@ -182,7 +188,7 @@ it("should have correct values when modifying local variables", async () => {
182
188
  expect(value).toStrictEqual(17);
183
189
  });
184
190
 
185
- it("should work with dispatcher", async () => {
191
+ test("Variant #8: Work with dispatcher", async () => {
186
192
  var output = await JsConfuser.obfuscate(
187
193
  `
188
194
  function container(x){
@@ -211,34 +217,8 @@ it("should work with dispatcher", async () => {
211
217
  expect(value).toStrictEqual(100);
212
218
  });
213
219
 
214
- it("should not change functions with const", async () => {
215
- var output = await JsConfuser.obfuscate(
216
- `
217
- function TEST_FUNCTION(x, y){
218
- const CONSTANT = 20;
219
-
220
- return CONSTANT + x + y;
221
- }
222
-
223
- input(TEST_FUNCTION(10, 5));
224
- `,
225
- {
226
- target: "node",
227
- flatten: true,
228
- }
229
- );
230
-
231
- expect(output).not.toContain("set");
232
-
233
- var value = "never_called",
234
- input = (x) => (value = x);
235
-
236
- eval(output);
237
- expect(value).toStrictEqual(35);
238
- });
239
-
240
220
  // https://github.com/MichaelXF/js-confuser/issues/25
241
- it("should work when pattern-based assignment expressions are involved", async () => {
221
+ test("Variant #9: Work with pattern-based assignment expressions", async () => {
242
222
  var output = await JsConfuser.obfuscate(
243
223
  `
244
224
  var i = 0;
@@ -257,7 +237,7 @@ it("should work when pattern-based assignment expressions are involved", async (
257
237
  );
258
238
 
259
239
  // Ensure flatten was applied
260
- expect(output).toContain("[i],[]");
240
+ expect(output).toContain("_flat_");
261
241
 
262
242
  var value = "never_called",
263
243
  input = (x) => (value = x);
@@ -266,7 +246,7 @@ it("should work when pattern-based assignment expressions are involved", async (
266
246
  expect(value).toStrictEqual(1);
267
247
  });
268
248
 
269
- it("should work on async functions", async () => {
249
+ test("Variant #10: Async function", async () => {
270
250
  var output = await JsConfuser.obfuscate(
271
251
  `
272
252
  async function timeout(ms){
@@ -310,33 +290,7 @@ it("should work on async functions", async () => {
310
290
  }, 2000);
311
291
  });
312
292
 
313
- it("should work on Function Expressions", async () => {
314
- var output = await JsConfuser.obfuscate(
315
- `
316
- var outsideVar = "Correct Value";
317
-
318
- var myFunctionExpression = function(){
319
- return outsideVar;
320
- }
321
-
322
- TEST_OUTPUT = myFunctionExpression();
323
- `,
324
- {
325
- target: "node",
326
- flatten: true,
327
- }
328
- );
329
-
330
- // Ensure flatten applied
331
- expect(output).toContain("_flat_myFunctionExpression");
332
-
333
- var TEST_OUTPUT;
334
- eval(output);
335
-
336
- expect(TEST_OUTPUT).toStrictEqual("Correct Value");
337
- });
338
-
339
- it("should work on Properties", async () => {
293
+ test("Variant #11: Work with properties", async () => {
340
294
  var output = await JsConfuser.obfuscate(
341
295
  `
342
296
  var outsideVar = "Incorrect Value";
@@ -369,7 +323,7 @@ it("should work on Properties", async () => {
369
323
  );
370
324
 
371
325
  // Ensure flatten applied
372
- expect(output).toContain("_flat_myInitProperty");
326
+ expect(output).toContain("_flat_");
373
327
 
374
328
  var TEST_OUTPUT;
375
329
  eval(output);
@@ -377,7 +331,7 @@ it("should work on Properties", async () => {
377
331
  expect(TEST_OUTPUT).toStrictEqual("Correct Value");
378
332
  });
379
333
 
380
- it("should work with RGF enabled", async () => {
334
+ test("Variant #12: Work with RGF enabled", async () => {
381
335
  var output = await JsConfuser.obfuscate(
382
336
  `
383
337
  var outsideVar = "Correct Value";
@@ -407,7 +361,7 @@ it("should work with RGF enabled", async () => {
407
361
  expect(TEST_OUTPUT).toStrictEqual("Correct Value");
408
362
  });
409
363
 
410
- it("should work with assignment expressions in the return statement", async () => {
364
+ test("Variant #13: Work with assignment expression in the return statement", async () => {
411
365
  var output = await JsConfuser(
412
366
  `
413
367
  var outside;
@@ -433,7 +387,7 @@ it("should work with assignment expressions in the return statement", async () =
433
387
  expect(TEST_OUTPUT).toStrictEqual("Correct Value");
434
388
  });
435
389
 
436
- it("should work with 'use strict' directive", async () => {
390
+ test("Variant #14: Work with 'use strict' directive", async () => {
437
391
  var output = await JsConfuser(
438
392
  `
439
393
  function myFunction(){
@@ -455,3 +409,313 @@ it("should work with 'use strict' directive", async () => {
455
409
 
456
410
  expect(TEST_OUTPUT).toStrictEqual("Correct Value");
457
411
  });
412
+
413
+ // https://github.com/MichaelXF/js-confuser/issues/89
414
+ test("Variant #15: Work with functions with invalid identifier names", async () => {
415
+ var output = await JsConfuser(
416
+ `
417
+ // Input
418
+ var object = {
419
+ "my-function": function () {
420
+ TEST_OUTPUT = "Success";
421
+ },
422
+ };
423
+
424
+ object["my-function"](); // "Success"
425
+ `,
426
+ { target: "node", flatten: true }
427
+ );
428
+
429
+ var TEST_OUTPUT;
430
+ eval(output);
431
+
432
+ expect(TEST_OUTPUT).toStrictEqual("Success");
433
+ });
434
+
435
+ test("Variant #16: Multiple test", async () => {
436
+ var output = await JsConfuser(
437
+ `
438
+ "use strict";
439
+
440
+ function assertLegalInvocation(){
441
+ if(this !== undefined) throw new Error("Illegal Invocation");
442
+ }
443
+
444
+ (function (){
445
+ var requiredValue = {};
446
+ var value;
447
+ var counter = 0;
448
+
449
+ function getCorrectValueObject(requiredParameter){
450
+ function nestedFunction(){
451
+ var requiredCounterValue = 1;
452
+ if(requiredParameter === requiredValue) {
453
+ if(counter === requiredCounterValue) {
454
+ return {value: "Correct Value"};
455
+ }
456
+ }
457
+
458
+ return {value: "Incorrect Value"};
459
+ }
460
+
461
+ return nestedFunction();
462
+ }
463
+
464
+ function setValue(){
465
+ // Test update-expression
466
+ counter++;
467
+
468
+ // Test call-expression
469
+ assertLegalInvocation();
470
+
471
+ function nestedSetValue(){
472
+ // Test destructuring
473
+ ({value} = getCorrectValueObject(requiredValue));
474
+ }
475
+
476
+ nestedSetValue()
477
+ }
478
+
479
+ function myFunction(myParameter1, myParameter2, myParameter3){
480
+ setValue();
481
+
482
+ myParameter1 = TEST_OUTPUT = value;
483
+ }
484
+
485
+ myFunction();
486
+ })();
487
+ `,
488
+ { target: "node", flatten: true }
489
+ );
490
+
491
+ expect(output).toContain("_flat_getCorrectValue");
492
+ expect(output).toContain("_flat_setValue");
493
+ expect(output).toContain("_flat_myFunction");
494
+
495
+ var TEST_OUTPUT;
496
+ eval(output);
497
+
498
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
499
+ });
500
+
501
+ test("Variant #17: Don't apply to generator functions", async () => {
502
+ var output = await JsConfuser(
503
+ `
504
+ function* myGeneratorFunction(){
505
+ yield "Correct Value";
506
+ }
507
+
508
+ TEST_OUTPUT = (myGeneratorFunction()).next().value;
509
+ `,
510
+ { target: "node", flatten: true }
511
+ );
512
+
513
+ expect(output).not.toContain("_flat_myGeneratorFunction");
514
+
515
+ var TEST_OUTPUT;
516
+ eval(output);
517
+
518
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
519
+ });
520
+
521
+ test("Variant #18: Redefined variable in nested scope", async () => {
522
+ var output = await JsConfuser(
523
+ `
524
+ (function (){
525
+ var outsideVar = "Incorrect Value 1";
526
+ function myFunction1(){
527
+ (()=>{
528
+ var outsideVar = "Correct Value 1";
529
+ TEST_OUTPUT_1 = outsideVar;
530
+ })();
531
+ };
532
+
533
+ myFunction1();
534
+
535
+ outsideVar = "Correct Value 2";
536
+
537
+ function myFunction2(){
538
+ (()=>{
539
+ var outsideVar = "Incorrect Value 2";
540
+ })();
541
+ TEST_OUTPUT_2 = outsideVar;
542
+ };
543
+
544
+ myFunction2();
545
+ })();
546
+ `,
547
+ {
548
+ target: "node",
549
+ flatten: true,
550
+ renameVariables: (x) => !x.includes("_flat_"),
551
+ }
552
+ );
553
+
554
+ expect(output).toContain("_flat_myFunction1");
555
+ expect(output).toContain("_flat_myFunction2");
556
+
557
+ var TEST_OUTPUT_1, TEST_OUTPUT_2;
558
+ eval(output);
559
+
560
+ expect(TEST_OUTPUT_1).toStrictEqual("Correct Value 1");
561
+ expect(TEST_OUTPUT_2).toStrictEqual("Correct Value 2");
562
+ });
563
+
564
+ test("Variant #19: Nested function declaration", async () => {
565
+ var output = await JsConfuser(
566
+ `
567
+ function myFunction(){
568
+ TEST_OUTPUT = nestedFunctionDeclaration();
569
+
570
+ function nestedFunctionDeclaration(){
571
+ return "Correct Value";
572
+ }
573
+ }
574
+
575
+ myFunction();
576
+ `,
577
+ {
578
+ target: "node",
579
+ flatten: true,
580
+ }
581
+ );
582
+
583
+ expect(output).toContain("_flat_myFunction");
584
+
585
+ var TEST_OUTPUT;
586
+ eval(output);
587
+
588
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
589
+ });
590
+
591
+ test("Variant #20: Don't apply to functions that use 'this' 'arguments' or 'eval'", async () => {
592
+ var output = await JsConfuser(
593
+ `
594
+ function myFunction(){
595
+ usesEval();
596
+ }
597
+
598
+ function usesThis(){
599
+ return this;
600
+ }
601
+ function usesArguments(){
602
+ return arguments;
603
+ }
604
+ function usesEval(){
605
+ eval("TEST_OUTPUT = 'Correct Value'");
606
+ }
607
+
608
+ myFunction();
609
+ `,
610
+ {
611
+ target: "node",
612
+ flatten: true,
613
+ }
614
+ );
615
+
616
+ expect(output).toContain("_flat_myFunction");
617
+ expect(output).not.toContain("_flat_usesThis");
618
+ expect(output).not.toContain("_flat_usesArguments");
619
+ expect(output).not.toContain("_flat_usesEval");
620
+
621
+ var TEST_OUTPUT;
622
+ eval(output);
623
+
624
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
625
+ });
626
+
627
+ test("Variant #21: Preserve function.length property", async () => {
628
+ var output = await JsConfuser(
629
+ `
630
+ function oneParameter(a){};
631
+ var twoParameters = function({a},{b,c},...d){};
632
+ function threeParameters(a,b,c,d = 1,{e},...f){};
633
+
634
+ TEST_OUTPUT = oneParameter.length + twoParameters.length + threeParameters.length;
635
+ `,
636
+ {
637
+ target: "node",
638
+ flatten: true,
639
+ }
640
+ );
641
+
642
+ expect(output).toContain("_flat_oneParameter");
643
+ expect(output).not.toContain("_flat_twoParameters");
644
+ expect(output).not.toContain("_flat_threeParameters");
645
+
646
+ var TEST_OUTPUT;
647
+ eval(output);
648
+
649
+ expect(TEST_OUTPUT).toStrictEqual(6);
650
+ });
651
+
652
+ test("Variant #22: Modify object properties", async () => {
653
+ var output = await JsConfuser(
654
+ `
655
+ var myObject = {};
656
+
657
+ function setProperty(property, value){
658
+ myObject[property] = value;
659
+ }
660
+
661
+ function getProperty(property){
662
+ return myObject[property];
663
+ }
664
+
665
+ setProperty("TEST_PROPERTY", "Correct Value");
666
+ TEST_OUTPUT = getProperty("TEST_PROPERTY");
667
+ `,
668
+ { target: "node", flatten: true }
669
+ );
670
+
671
+ expect(output).toContain("_flat_setProperty");
672
+ expect(output).toContain("_flat_getProperty");
673
+
674
+ var TEST_OUTPUT;
675
+
676
+ eval(output);
677
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
678
+ });
679
+
680
+ test("Variant #23: Reference original function name", async () => {
681
+ var output = await JsConfuser(
682
+ `
683
+ (function (){
684
+ function myFunction(){
685
+ var valueToCheck = myFunction;
686
+ TEST_OUTPUT = typeof valueToCheck === "function";
687
+ }
688
+
689
+ myFunction();
690
+ })();
691
+ `,
692
+ { target: "node", flatten: true }
693
+ );
694
+
695
+ expect(output).toContain("_flat_myFunction");
696
+
697
+ var TEST_OUTPUT;
698
+
699
+ eval(output);
700
+ expect(TEST_OUTPUT).toStrictEqual(true);
701
+ });
702
+
703
+ test("Variant #24: Typeof expression", async () => {
704
+ var output = await JsConfuser(
705
+ `
706
+ function myFunction(){
707
+ TEST_OUTPUT = typeof nonExistentVariable === "undefined";
708
+ }
709
+
710
+ myFunction();
711
+ `,
712
+ { target: "node", flatten: true }
713
+ );
714
+
715
+ expect(output).toContain("_flat_myFunction");
716
+
717
+ var TEST_OUTPUT;
718
+
719
+ eval(output);
720
+ expect(TEST_OUTPUT).toStrictEqual(true);
721
+ });
@@ -12,7 +12,7 @@ test("Variant #1: Move variable 'y' to top", async () => {
12
12
  movedDeclarations: true,
13
13
  });
14
14
 
15
- expect(output).toContain("var y;");
15
+ expect(output).toContain("var x=10,y;");
16
16
  expect(output).toContain("y=15");
17
17
 
18
18
  var TEST_VARIABLE;
@@ -34,7 +34,7 @@ test("Variant #2: Move variable 'y' and 'z' to top", async () => {
34
34
  movedDeclarations: true,
35
35
  });
36
36
 
37
- expect(output).toContain("var y,z");
37
+ expect(output).toContain("var x=10,y,z;");
38
38
  expect(output).toContain("y=15");
39
39
  expect(output).toContain("z=5");
40
40
 
@@ -44,7 +44,7 @@ test("Variant #2: Move variable 'y' and 'z' to top", async () => {
44
44
  expect(TEST_VARIABLE).toStrictEqual(30);
45
45
  });
46
46
 
47
- test("Variant #2: Don't move 'y' (destructuring)", async () => {
47
+ test("Variant #3: Don't move 'y' (destructuring)", async () => {
48
48
  var code = `
49
49
  var x = 10;
50
50
  var [y] = [15];
@@ -64,7 +64,7 @@ test("Variant #2: Don't move 'y' (destructuring)", async () => {
64
64
  expect(TEST_VARIABLE).toStrictEqual(25);
65
65
  });
66
66
 
67
- test("Variant #3: Don't move 'y' (nested lexical scope)", async () => {
67
+ test("Variant #4: Move 'y' (nested lexical scope)", async () => {
68
68
  var code = `
69
69
  var x = 10;
70
70
  var y = 15;
@@ -81,7 +81,7 @@ test("Variant #3: Don't move 'y' (nested lexical scope)", async () => {
81
81
  movedDeclarations: true,
82
82
  });
83
83
 
84
- expect(output).toContain("var y=15");
84
+ expect(output).toContain("var x=10,y;");
85
85
 
86
86
  var TEST_VARIABLE;
87
87
  eval(output);
@@ -89,7 +89,7 @@ test("Variant #3: Don't move 'y' (nested lexical scope)", async () => {
89
89
  expect(TEST_VARIABLE).toStrictEqual(20);
90
90
  });
91
91
 
92
- test("Variant #4: Move 'y' (for statement initializer)", async () => {
92
+ test("Variant #5: Move 'y' (for statement initializer)", async () => {
93
93
  var code = `
94
94
  var x = 10;
95
95
  for ( var y = 0; y < 15; y++ ) {
@@ -111,7 +111,7 @@ test("Variant #4: Move 'y' (for statement initializer)", async () => {
111
111
  expect(TEST_VARIABLE).toStrictEqual(25);
112
112
  });
113
113
 
114
- test("Variant #5: Move 'y' (for-in left-hand initializer)", async () => {
114
+ test("Variant #6: Move 'y' (for-in left-hand initializer)", async () => {
115
115
  var code = `
116
116
  var x = 10;
117
117
  for ( var y in [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] ) {
@@ -134,7 +134,7 @@ test("Variant #5: Move 'y' (for-in left-hand initializer)", async () => {
134
134
  expect(TEST_VARIABLE).toStrictEqual(25);
135
135
  });
136
136
 
137
- test("Variant #6: Don't move const or let variables", async () => {
137
+ test("Variant #7: Don't move const or let variables", async () => {
138
138
  var code = `
139
139
  var fillerExpr;
140
140
 
@@ -158,7 +158,7 @@ test("Variant #6: Don't move const or let variables", async () => {
158
158
  expect(TEST_VARIABLE).toStrictEqual(25);
159
159
  });
160
160
 
161
- test("Variant #7: Work with 'use strict'", async () => {
161
+ test("Variant #8: Work with 'use strict'", async () => {
162
162
  var code = `
163
163
  function myFunction(){
164
164
  'use strict';
@@ -184,3 +184,31 @@ test("Variant #7: Work with 'use strict'", async () => {
184
184
 
185
185
  expect(TEST_OUTPUT).toStrictEqual(true);
186
186
  });
187
+
188
+ test("Variant #9: Defined variable without an initializer", async () => {
189
+ var code = `
190
+ var x;
191
+ x = 1;
192
+ var y;
193
+ y = 2;
194
+ TEST_OUTPUT = x + y;
195
+ `;
196
+
197
+ var output1 = await JsConfuser(code, {
198
+ target: "node",
199
+ movedDeclarations: true,
200
+ controlFlowFlattening: true,
201
+ duplicateLiteralsRemoval: true,
202
+ });
203
+
204
+ var output2 = await JsConfuser(output1, {
205
+ target: "node",
206
+ movedDeclarations: true,
207
+ controlFlowFlattening: true,
208
+ duplicateLiteralsRemoval: true,
209
+ });
210
+
211
+ var TEST_OUTPUT;
212
+ eval(output2);
213
+ expect(TEST_OUTPUT).toStrictEqual(3);
214
+ });