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
@@ -1,6 +1,6 @@
1
1
  import JsConfuser from "../../../src/index";
2
2
 
3
- test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async () => {
3
+ test("Variant #1: Obfuscate code and still execute in correct order", async () => {
4
4
  var code = `
5
5
  var array = [];
6
6
 
@@ -15,7 +15,7 @@ test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async (
15
15
  array.push(9);
16
16
  array.push(10);
17
17
 
18
- input(array);
18
+ TEST_OUTPUT = array;
19
19
  `;
20
20
 
21
21
  var output = await JsConfuser(code, {
@@ -23,14 +23,17 @@ test("Variant #1: Execute in the correct order (ControlFlowFlattening)", async (
23
23
  controlFlowFlattening: true,
24
24
  });
25
25
 
26
- function input(array) {
27
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
28
- }
26
+ // Ensure Control Flow Flattening applied
27
+ expect(output).toContain("while");
29
28
 
29
+ // Ensure the output is the exact same
30
+ var TEST_OUTPUT;
30
31
  eval(output);
32
+
33
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
31
34
  });
32
35
 
33
- test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
36
+ test("Variant #2: Obfuscate for loops", async () => {
34
37
  var code = `
35
38
  var array = [];
36
39
 
@@ -38,7 +41,7 @@ test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
38
41
  array.push(i);
39
42
  }
40
43
 
41
- input(array);
44
+ TEST_OUTPUT = array;
42
45
  `;
43
46
 
44
47
  var output = await JsConfuser(code, {
@@ -46,16 +49,20 @@ test("Variant #2: Obfuscate for loops (ControlFlowObfuscation)", async () => {
46
49
  controlFlowFlattening: true,
47
50
  });
48
51
 
49
- expect(output).toContain("switch");
52
+ // Ensure Control Flow Flattening applied
53
+ expect(output).toContain("while");
50
54
 
51
- function input(array) {
52
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
53
- }
55
+ // Ensure the for statement got flattened
56
+ expect(output).not.toContain("for");
57
+
58
+ // Ensure the output is the exact same
59
+ var TEST_OUTPUT;
54
60
 
55
61
  eval(output);
62
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
56
63
  });
57
64
 
58
- test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
65
+ test("Variant #3: Obfuscate while loops", async () => {
59
66
  var code = `
60
67
  var array = [];
61
68
  var i = 1;
@@ -65,7 +72,7 @@ test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
65
72
  i++
66
73
  }
67
74
 
68
- input(array);
75
+ TEST_OUTPUT = array;
69
76
  `;
70
77
 
71
78
  var output = await JsConfuser(code, {
@@ -73,13 +80,14 @@ test("Variant #3: Obfuscate while loops (ControlFlowObfuscation)", async () => {
73
80
  controlFlowFlattening: true,
74
81
  });
75
82
 
76
- expect(output).toContain("switch");
77
-
78
- function input(array) {
79
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
80
- }
83
+ // Ensure Control Flow Flattening applied
84
+ expect(output).toContain("while");
81
85
 
86
+ // Ensure the output is the exact same
87
+ var TEST_OUTPUT;
82
88
  eval(output);
89
+
90
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
83
91
  });
84
92
 
85
93
  test("Variant #4: Work with break statements", async () => {
@@ -94,7 +102,7 @@ test("Variant #4: Work with break statements", async () => {
94
102
  TEST_ARRAY.push(i);
95
103
  }
96
104
 
97
- input(TEST_ARRAY);
105
+ TEST_OUTPUT = TEST_ARRAY;
98
106
  `;
99
107
 
100
108
  var output = await JsConfuser(code, {
@@ -102,19 +110,20 @@ test("Variant #4: Work with break statements", async () => {
102
110
  controlFlowFlattening: true,
103
111
  });
104
112
 
113
+ // Ensure Control Flow Flattening applied
105
114
  expect(output).toContain("switch");
106
115
  expect(output).toContain("while");
107
116
 
108
- function input(array) {
109
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
110
- }
117
+ // Ensure the output is the exact same
118
+ var TEST_OUTPUT;
111
119
 
112
120
  eval(output);
121
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
113
122
  });
114
123
 
115
- test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, ControlFlowFlattening)", async () => {
124
+ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables)", async () => {
116
125
  var code = `
117
- let array = [];
126
+ let array = [];
118
127
 
119
128
  array.push(1);
120
129
  array.push(2);
@@ -127,7 +136,7 @@ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, Co
127
136
  array.push(9);
128
137
  array.push(10);
129
138
 
130
- input(array);
139
+ TEST_OUTPUT = array;
131
140
  `;
132
141
 
133
142
  var output = await JsConfuser(code, {
@@ -135,17 +144,24 @@ test("Variant #5: Don't obfuscate code with `let` (Lexically bound variables, Co
135
144
  controlFlowFlattening: true,
136
145
  });
137
146
 
138
- expect(output).not.toContain("switch");
147
+ // Ensure Control Flow Flattening did NOT apply here
148
+ expect(output).not.toContain("while");
149
+
150
+ // Ensure the output is the exact same
151
+ var TEST_OUTPUT;
152
+ eval(output);
153
+
154
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
139
155
  });
140
156
 
141
- test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables, ControlFlowObfuscation)", async () => {
157
+ test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables)", async () => {
142
158
  var code = `
143
- var array=[];
144
- for ( let i =1; i <= 10; i++ ) {
145
- array.push(i);
146
- }
159
+ var array=[];
160
+ for ( let i =1; i <= 10; i++ ) {
161
+ array.push(i);
162
+ }
147
163
 
148
- input(array);
164
+ TEST_OUTPUT = array;
149
165
  `;
150
166
 
151
167
  var output = await JsConfuser(code, {
@@ -153,10 +169,17 @@ test("Variant #6: Don't obfuscate code with `let` (Lexically bound variables, Co
153
169
  controlFlowFlattening: true,
154
170
  });
155
171
 
156
- expect(output).not.toContain("switch");
172
+ // Ensure Control Flow Flattening did NOT apply here
173
+ expect(output).not.toContain("while");
174
+
175
+ // Ensure the output is the exact same
176
+ var TEST_OUTPUT;
177
+ eval(output);
178
+
179
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
157
180
  });
158
181
 
159
- test("Variant #7: Accept percentages", async () => {
182
+ test("Variant #7: Allow option to be set a percentage threshold", async () => {
160
183
  var code = `
161
184
  var array = [];
162
185
  var i = 1;
@@ -166,7 +189,7 @@ test("Variant #7: Accept percentages", async () => {
166
189
  i++
167
190
  }
168
191
 
169
- input(array);
192
+ TEST_OUTPUT = array;
170
193
  `;
171
194
 
172
195
  var output = await JsConfuser(code, {
@@ -174,27 +197,48 @@ test("Variant #7: Accept percentages", async () => {
174
197
  controlFlowFlattening: 0.5,
175
198
  });
176
199
 
177
- // expect(output).toContain("switch");
178
- // expect(output).toContain("while");
179
-
180
- function input(array) {
181
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
182
- }
200
+ // Ensure the output is the exact same
201
+ var TEST_OUTPUT;
183
202
 
184
203
  eval(output);
204
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
185
205
  });
186
206
 
187
- it("Variant #8: Work when obfuscated multiple times", async () => {
207
+ test("Variant #8: Work when obfuscated multiple times", async () => {
188
208
  var code = `
189
209
  var array = [];
190
- var i = 1;
191
210
 
192
- while ( i <= 10 ) {
193
- array.push(i);
194
- i++
195
- }
211
+ switch(true){
212
+ case true: // Always true
213
+ if(true){ // Always true
214
+ var i;
215
+
216
+ for ( i = 1; i <= 10; i++ ) {
217
+ if(typeof i === "number") { // Always true
218
+ array.push(i);
219
+
220
+ var filler1;
221
+ var filler2;
222
+ var filler3;
223
+ }
224
+
225
+ var filler1;
226
+ var filler2;
227
+ var filler3;
228
+ }
229
+
230
+ var filler1;
231
+ var filler2;
232
+ var filler3;
233
+ }
196
234
 
197
- input(array);
235
+ var filler1;
236
+ var filler2;
237
+ var filler3;
238
+ break;
239
+ }
240
+
241
+ TEST_OUTPUT = array;
198
242
  `; // [1,2,3,4,5,6,7,8,9,10]
199
243
 
200
244
  var output = await JsConfuser(code, {
@@ -202,19 +246,19 @@ it("Variant #8: Work when obfuscated multiple times", async () => {
202
246
  controlFlowFlattening: true,
203
247
  });
204
248
 
249
+ // Ensure Control Flow Flattening applied
250
+ expect(output).toContain("while");
251
+
205
252
  var doublyObfuscated = await JsConfuser(output, {
206
253
  target: "node",
207
254
  controlFlowFlattening: true,
208
255
  });
209
256
 
210
- // expect(output).toContain("switch");
211
- // expect(output).toContain("while");
212
-
213
- function input(array) {
214
- expect(array).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
215
- }
257
+ // Ensure the output is the exact same
258
+ var TEST_OUTPUT;
216
259
 
217
260
  eval(doublyObfuscated);
261
+ expect(TEST_OUTPUT).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
218
262
  });
219
263
 
220
264
  test("Variant #9: Don't entangle floats or NaN", async () => {
@@ -539,43 +583,36 @@ test("Variant #17: Flatten with infinite for loop and break", async () => {
539
583
  expect(TEST_ARRAY).toStrictEqual([1, 2, 3, 4, 5]);
540
584
  });
541
585
 
542
- test("Variant #18: Flatten certain functions", async () => {
586
+ test("Variant #20: Work with redefined functions", async () => {
543
587
  var output = await JsConfuser(
544
588
  `
545
-
546
- var x = -1;
589
+ var counter = 0;
547
590
  function increment(){
548
- x++;
549
- return x;
550
- }
591
+ counter++;
551
592
 
552
- increment();
593
+ if(counter == 3) {
594
+ increment = () => { counter = "Correct Value" }
595
+ }
596
+ }
553
597
 
554
- function breakSequencingAttempt1(){
598
+ increment(); // 1
599
+ increment(); // 2
600
+ increment(); // 3
555
601
 
556
- }
602
+ var originalIncrement = increment;
603
+ increment = ()=>{};
557
604
 
558
- var element1 = increment();
559
- var element2 = increment();
560
- var element3;
561
-
562
- element3 = increment();
605
+ increment(); // 3
606
+ increment(); // 3
607
+ increment(); // 3
563
608
 
564
- function breakSequencingAttempt2(){
609
+ increment = undefined;
565
610
 
566
- }
611
+ increment = typeof increment === "undefined" ? originalIncrement : 0;
567
612
 
568
- TEST_ARRAY = [
569
- element1,
570
- element2,
571
- element3,
572
- ]
613
+ increment(); // "Correct Value"
573
614
 
574
- var fillerExpr1;
575
- var fillerExpr2;
576
- var fillerExpr3;
577
- var fillerExpr4;
578
- var fillerExpr5;
615
+ TEST_OUTPUT = counter;
579
616
  `,
580
617
  {
581
618
  target: "node",
@@ -583,38 +620,59 @@ test("Variant #18: Flatten certain functions", async () => {
583
620
  }
584
621
  );
585
622
 
586
- expect(output).not.toContain("function increment");
587
- expect(output).not.toContain("increment");
623
+ // Ensure Control Flow Flattening applied
624
+ expect(output).toContain("while");
588
625
 
589
- var TEST_ARRAY;
626
+ var TEST_OUTPUT;
590
627
 
591
628
  eval(output);
592
- expect(TEST_ARRAY).toStrictEqual([1, 2, 3]);
629
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
593
630
  });
594
631
 
595
- test("Variant #19: Don't flatten non-extractable functions", async () => {
632
+ // https://github.com/MichaelXF/js-confuser/issues/70
633
+ test("Variant #21: Don't move Import Declarations", async () => {
596
634
  var output = await JsConfuser(
597
635
  `
598
-
599
- var x = -1;
600
- function increment(){
601
- x++;
602
- return x;
636
+ import {createHash} from "crypto";
637
+ var inputString = "Hash this string";
638
+ var hashed = createHash("sha256").update(inputString).digest("hex");
639
+ TEST_OUTPUT = hashed;
640
+ `,
641
+ {
642
+ target: "node",
643
+ controlFlowFlattening: true,
603
644
  }
645
+ );
604
646
 
605
- increment();
647
+ // Ensure Control Flow FLattening was applied
648
+ expect(output).toContain("switch");
606
649
 
607
- TEST_ARRAY = [
608
- increment(),
609
- increment(),
610
- increment()
611
- ]
650
+ // Ensure the import declaration wasn't moved
651
+ expect(output.startsWith("import")).toStrictEqual(true);
612
652
 
613
- var fillerExpr1;
614
- var fillerExpr2;
615
- var fillerExpr3;
616
- var fillerExpr4;
617
- var fillerExpr5;
653
+ // Convert to runnable code
654
+ output = output.replace(
655
+ `import{createHash}from'crypto';`,
656
+ "const {createHash}=require('crypto');"
657
+ );
658
+
659
+ var TEST_OUTPUT = "";
660
+
661
+ eval(output);
662
+
663
+ expect(TEST_OUTPUT).toStrictEqual(
664
+ "1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
665
+ );
666
+ });
667
+
668
+ // https://github.com/MichaelXF/js-confuser/issues/81
669
+ test("Variant #22: Don't break typeof expression", async () => {
670
+ var output = await JsConfuser(
671
+ `
672
+ TEST_OUTPUT = false;
673
+ if(typeof nonExistentVariable === "undefined") {
674
+ TEST_OUTPUT = true;
675
+ }
618
676
  `,
619
677
  {
620
678
  target: "node",
@@ -622,89 +680,595 @@ test("Variant #19: Don't flatten non-extractable functions", async () => {
622
680
  }
623
681
  );
624
682
 
625
- expect(output).toContain("function increment");
626
-
627
- var TEST_ARRAY;
683
+ var TEST_OUTPUT;
628
684
 
629
685
  eval(output);
630
- expect(TEST_ARRAY).toStrictEqual([1, 2, 3]);
686
+
687
+ expect(TEST_OUTPUT).toStrictEqual(true);
631
688
  });
632
689
 
633
- test("Variant #20: Don't apply when functions are redefined", async () => {
690
+ test("Variant #23: Don't break Super calls", async () => {
634
691
  var output = await JsConfuser(
635
692
  `
693
+ class MyClass1 {
694
+ constructor(val){
695
+ this.val = val;
696
+ }
697
+ }
698
+ class MyClass2 extends MyClass1 {
699
+ constructor(){
700
+ super(10);
701
+
702
+ // Ensure ControlFlowFlattening applies here
703
+ var filler1;
704
+ var filler2;
705
+ var filler3;
706
+ }
707
+ }
636
708
 
637
- var x = -1;
638
- function increment(){
639
- x++;
640
- return x;
709
+ var myObject = new MyClass2();
710
+ TEST_OUTPUT = myObject.val; // 10
711
+ `,
712
+ { target: "node", controlFlowFlattening: true }
713
+ );
714
+
715
+ var TEST_OUTPUT;
716
+ eval(output);
717
+
718
+ expect(TEST_OUTPUT).toStrictEqual(10);
719
+ });
720
+
721
+ test("Variant #24: Nested function-calls with labeled breaks/continues", async () => {
722
+ var code = `
723
+ function myFunction(){
724
+ function x( STOP_VALUE ){
725
+ function y(){
726
+ function dead(){
727
+ var STOP_VALUE = 3;
728
+ }
729
+ function z(){
730
+ a: for(var i = 0; i < 10; i++) {
731
+ if (i == STOP_VALUE) {
732
+ var counter = 0;
733
+ b: for ( var j = 0; j < 10; j++ ) {
734
+ if(j == 4) {
735
+ counter -= 50;
736
+ continue b;
737
+ }
738
+ counter++;
739
+ if(j == 8) {
740
+ break a;
741
+ }
742
+ }
743
+ }
744
+ }
745
+
746
+ var filler1;
747
+ var filler2;
748
+ var filler3;
749
+ return i + j + counter;
750
+ }
751
+
752
+ var filler1;
753
+ var filler2;
754
+ var filler3;
755
+ return z();
756
+ }
757
+
758
+ var filler1;
759
+ var filler2;
760
+ var filler3;
761
+ return y();
641
762
  }
642
763
 
643
- // redefined function declaration
644
- increment = function(){
645
- return 0;
764
+ var filler1;
765
+ var filler2;
766
+ var filler3;
767
+ return x( 1 );
768
+ }
769
+
770
+ var x = myFunction();
771
+
772
+ TEST_OUTPUT = x;`;
773
+
774
+ var output = await JsConfuser(code, {
775
+ target: "node",
776
+ controlFlowFlattening: true,
777
+ renameVariables: true,
778
+ identifierGenerator: "mangled",
779
+ stack: true,
780
+ });
781
+
782
+ var TEST_OUTPUT;
783
+ eval(output);
784
+
785
+ expect(TEST_OUTPUT).toStrictEqual(-33);
786
+ });
787
+
788
+ test("Variant #25: Don't break call expressions to bound functions", async () => {
789
+ var code = `
790
+ var array = [];
791
+ array.push(1);
792
+ array.push(2);
793
+ array.push(3);
794
+ array.push(4);
795
+ array.push(5);
796
+
797
+ TEST_OUTPUT = array;
798
+ `;
799
+
800
+ var output = await JsConfuser(code, {
801
+ target: "node",
802
+ controlFlowFlattening: true,
803
+ });
804
+
805
+ // Ensure Control Flow Flattening applied
806
+ expect(output).toContain("while");
807
+
808
+ var TEST_OUTPUT;
809
+ eval(output);
810
+
811
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3, 4, 5]);
812
+ });
813
+
814
+ test("Variant #26: Add opaque predicates and still work", async () => {
815
+ var output = await JsConfuser(
816
+ `
817
+ TEST_OUTPUT = [];
818
+ if(true) TEST_OUTPUT.push(1);
819
+ else TEST_OUTPUT.push("Incorrect If Statement")
820
+
821
+ switch(true){
822
+ case true: TEST_OUTPUT.push(2); break;
823
+ default:
824
+ TEST_OUTPUT.push("Incorrect Switch")
825
+ }
826
+
827
+ TEST_OUTPUT.push( true ? 3 : "Incorrect Conditional Statement" );
828
+ `,
829
+ { target: "node", controlFlowFlattening: true }
830
+ );
831
+
832
+ expect(output).toContain("while");
833
+ var TEST_OUTPUT = [];
834
+
835
+ eval(output);
836
+
837
+ expect(TEST_OUTPUT).toStrictEqual([1, 2, 3]);
838
+ });
839
+
840
+ test("Variant #27: Work on async/generator functions", async () => {
841
+ var output = await JsConfuser(
842
+ `
843
+ "use strict";
844
+ async function myAsyncFunction(){
845
+ await (1);
846
+ }
847
+
848
+ function* myGeneratorFunction(){
849
+ yield "Correct Value";
850
+ }
851
+
852
+ var x = myAsyncFunction();
853
+ var generatorObject = myGeneratorFunction();
854
+
855
+ TEST_OUTPUT = generatorObject.next().value;
856
+
857
+ var fillerVar1;
858
+ var fillerVar2;
859
+ var fillerVar3;
860
+ `,
861
+ { target: "node", controlFlowFlattening: true }
862
+ );
863
+
864
+ // Ensure Control Flow Flattening applied
865
+ expect(output).toContain("while");
866
+
867
+ var TEST_OUTPUT;
868
+ eval(output);
869
+
870
+ expect(TEST_OUTPUT).toStrictEqual("Correct Value");
871
+ });
872
+
873
+ test("Variant #28: Don't break update expressions", async () => {
874
+ var code = `
875
+ var counter = 0;
876
+
877
+ counter++; // 1
878
+ counter++; // 2
879
+ counter++; // 3
880
+ counter++; // 4
881
+ counter++; // 5
882
+ counter++; // 6
883
+ counter++; // 7
884
+ counter++; // 8
885
+ counter++; // 9
886
+ counter++; // 10
887
+
888
+ TEST_OUTPUT = counter;
889
+ `;
890
+
891
+ var output = await JsConfuser(code, {
892
+ target: "node",
893
+ controlFlowFlattening: true,
894
+ });
895
+
896
+ // Ensure Control Flow Flattening applied
897
+ expect(output).toContain("while");
898
+
899
+ var TEST_OUTPUT;
900
+ eval(output);
901
+
902
+ expect(TEST_OUTPUT).toStrictEqual(10);
903
+ });
904
+
905
+ test("Variant #29: Nested labeled break and continue statements with RGF enabled", async () => {
906
+ var code = `
907
+ function labeledBreaksAndContinues() {
908
+ var flag = true;
909
+
910
+ label_1: for (var i = 0; i < 20; i++) {
911
+ b: switch (i) {
912
+ case 15:
913
+ c: do {
914
+ if (i !== 15) {
915
+ break c;
916
+ }
917
+ flag = true;
918
+
919
+ break label_1;
920
+
921
+ var fillerVar1;
922
+ var fillerVar2;
923
+ var fillerVar3;
924
+ } while (i == 15);
925
+
926
+ break;
927
+
928
+ case 10:
929
+ continue label_1;
930
+
931
+ default:
932
+ flag = false;
933
+ break b;
934
+ }
935
+
936
+ var fillerVar1;
937
+ var fillerVar2;
938
+ var fillerVar3;
646
939
  }
940
+
941
+ var fillerVar1;
942
+ var fillerVar2;
943
+ var fillerVar3;
944
+
945
+ if (flag) {
946
+ return i;
947
+ }
948
+ }
647
949
 
648
- increment();
950
+ var fillerVar1;
951
+ var fillerVar2;
952
+ var fillerVar3;
953
+
954
+ TEST_OUTPUT = labeledBreaksAndContinues();
955
+ `; // This complex code produces the value of 15
649
956
 
650
- TEST_ARRAY = [
651
- increment(),
652
- increment(),
653
- increment()
654
- ]
957
+ var output = await JsConfuser(code, {
958
+ target: "node",
959
+ controlFlowFlattening: true,
960
+ rgf: true,
961
+ renameVariables: true,
962
+ identifierGenerator: "mangled",
963
+ });
655
964
 
656
- var fillerExpr1;
657
- var fillerExpr2;
658
- var fillerExpr3;
659
- var fillerExpr4;
660
- var fillerExpr5;
661
- `,
965
+ // Run the code
966
+ var TEST_OUTPUT;
967
+ eval(output);
968
+
969
+ expect(TEST_OUTPUT).toStrictEqual(15);
970
+ });
971
+
972
+ test("Variant #30: Obfuscate switch statements", async () => {
973
+ var output = await JsConfuser(
974
+ `
975
+ switch("DON'T CHANGE ME"){} // Empty switch for testing
976
+
977
+ switch(true){
978
+ case 0:
979
+ TEST_OUTPUT = "Incorrect Value (1)";
980
+ break
981
+
982
+ case true:
983
+ TEST_OUTPUT = "First Correct Value";
984
+ break;
985
+
986
+ case 1:
987
+ case 2:
988
+ case 3:
989
+ case false:
990
+ TEST_OUTPUT = "Incorrect Value (2)";
991
+ break;
992
+ };
993
+
994
+ switch(TEST_OUTPUT){
995
+ case true:
996
+ TEST_OUTPUT = "Incorrect Value (3)";
997
+ break;
998
+
999
+ case false:
1000
+ TEST_OUTPUT = "Incorrect Value (4)";
1001
+ break;
1002
+
1003
+ default:
1004
+ TEST_OUTPUT = "Second Correct Value";
1005
+ }
1006
+
1007
+ switch(true){
1008
+ case false:
1009
+ throw new Error();
1010
+ break;
1011
+
1012
+ default:
1013
+ TEST_OUTPUT = "Incorrect Value";
1014
+
1015
+ case true:
1016
+ TEST_OUTPUT = "Third Correct Value";
1017
+ // Fall-through test
1018
+ case 10:
1019
+ TEST_OUTPUT = "Fourth Correct Value";
1020
+
1021
+ }
1022
+
1023
+ switch(true){
1024
+ default:
1025
+ throw new Error("NO");
1026
+ break;
1027
+ case true:
1028
+
1029
+ break;
1030
+ }
1031
+
1032
+ var hitDefault = false;
1033
+
1034
+ switch(true){
1035
+ case 1:
1036
+ case 2:
1037
+
1038
+ default:
1039
+ hitDefault = true;
1040
+ break;
1041
+
1042
+ case 3:
1043
+ case 4:
1044
+ break;
1045
+ }
1046
+
1047
+ if(!hitDefault) {
1048
+ throw new Error("Did not hit default case");
1049
+ }
1050
+
1051
+ var filler1;
1052
+ var filler2;
1053
+ var filler3;
1054
+ `,
662
1055
  {
663
1056
  target: "node",
664
1057
  controlFlowFlattening: true,
665
1058
  }
666
1059
  );
667
1060
 
668
- expect(output).not.toContain("switch");
1061
+ // Ensure Control Flow Flattening applied
1062
+ expect(output).toContain("while");
669
1063
 
670
- var TEST_ARRAY;
1064
+ // Ensure switch-statements got changed
1065
+ expect(output).not.toContain("switch(true)");
1066
+ expect(output).not.toContain("switch(TEST_OUTPUT)");
671
1067
 
1068
+ var TEST_OUTPUT;
672
1069
  eval(output);
673
- expect(TEST_ARRAY).toStrictEqual([0, 0, 0]);
1070
+
1071
+ expect(TEST_OUTPUT).toStrictEqual("Fourth Correct Value");
674
1072
  });
675
1073
 
676
- // https://github.com/MichaelXF/js-confuser/issues/70
677
- test("Variant #21: Don't move Import Declarations", async () => {
1074
+ test("Variant #31: Don't break nested function calls", async () => {
678
1075
  var output = await JsConfuser(
679
1076
  `
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,
1077
+ var i;
1078
+ var counter = 0;
1079
+ for(i = 0; i < 10;) {
1080
+ function logger(x){
1081
+ counter++;
1082
+ }
1083
+
1084
+ logger("Hello World");
1085
+ i++
688
1086
  }
1087
+
1088
+ TEST_OUTPUT = counter;
1089
+ `,
1090
+ { target: "node", controlFlowFlattening: true }
689
1091
  );
690
1092
 
691
- // Ensure Control Flow FLattening was applied
692
- expect(output).toContain("switch");
1093
+ expect(output).toContain("while");
693
1094
 
694
- // Ensure the import declaration wasn't moved
695
- expect(output.startsWith("import")).toStrictEqual(true);
1095
+ var TEST_OUTPUT;
1096
+ eval(output);
696
1097
 
697
- // Convert to runnable code
698
- output = output.replace(
699
- `import{createHash}from'crypto';`,
700
- "const {createHash}=require('crypto');"
1098
+ expect(TEST_OUTPUT).toStrictEqual(10);
1099
+ });
1100
+
1101
+ test("Variant #32: Don't break same name function calls", async () => {
1102
+ var output = await JsConfuser(
1103
+ `
1104
+ var counter = 0;
1105
+
1106
+ function a(){
1107
+ // Outer a called
1108
+ counter *= 2;
1109
+ }
1110
+
1111
+ var i;
1112
+
1113
+ for(i = 0; i < 10;) {
1114
+ function a(){
1115
+ // Inner a called
1116
+ counter += 1;
1117
+ }
1118
+
1119
+ a(); // Inner a
1120
+ i++;
1121
+ }
1122
+
1123
+ a(); // Inner a, Outer a got renamed
1124
+
1125
+ TEST_OUTPUT = counter;
1126
+ `,
1127
+ { target: "node", controlFlowFlattening: true }
701
1128
  );
702
1129
 
703
- var TEST_OUTPUT = "";
1130
+ expect(output).toContain("while");
704
1131
 
1132
+ var TEST_OUTPUT;
705
1133
  eval(output);
706
1134
 
707
- expect(TEST_OUTPUT).toStrictEqual(
708
- "1cac63f39fd68d8c531f27b807610fb3d50f0fc3f186995767fb6316e7200a3e"
1135
+ expect(TEST_OUTPUT).toStrictEqual(11);
1136
+ });
1137
+
1138
+ test("Variant #33: Don't break same name function declarations that are not ran", async () => {
1139
+ var output = await JsConfuser(
1140
+ `
1141
+ var counter = 0;
1142
+
1143
+ function a(){
1144
+ // Outer a called
1145
+ counter += 1;
1146
+ }
1147
+
1148
+ for(var i = 0; i < 10;) {
1149
+ if(false){
1150
+ function a(){
1151
+ // Inner a called
1152
+ counter = 0;
1153
+ }
1154
+ }
1155
+
1156
+
1157
+ a(); // Outer a
1158
+ i++;
1159
+ }
1160
+
1161
+ a(); // Outer a
1162
+
1163
+ TEST_OUTPUT = counter;
1164
+ `,
1165
+ { target: "node", controlFlowFlattening: true }
709
1166
  );
1167
+
1168
+ expect(output).toContain("while");
1169
+
1170
+ var TEST_OUTPUT;
1171
+ eval(output);
1172
+
1173
+ expect(TEST_OUTPUT).toStrictEqual(11);
1174
+ });
1175
+
1176
+ test("Variant #34: Flatten If, For, While, Do-while, and Switch statements multiple times", async () => {
1177
+ var code = `
1178
+ var counter = -1;
1179
+
1180
+ function incrementCounter(){
1181
+ counter++;
1182
+ }
1183
+
1184
+ if(false) {
1185
+
1186
+ } else {
1187
+ counter = 0;
1188
+ }
1189
+
1190
+ if(true){
1191
+ for(var i = 0; i < 10; i++) {
1192
+ switch(i){
1193
+ default:
1194
+ i++;
1195
+ break;
1196
+
1197
+ case 6:
1198
+ if(false){
1199
+ function incrementCounter(){
1200
+ throw new Error("Fake counter function");
1201
+ }
1202
+ }
1203
+
1204
+ while(i != 10) {
1205
+ incrementCounter();
1206
+ if(counter > 10) break;
1207
+ }
1208
+
1209
+ do {
1210
+ counter--;
1211
+ } while (counter > 5)
1212
+ // Fall-through
1213
+ case 8:
1214
+ counter *= 2;
1215
+ break;
1216
+ }
1217
+ }
1218
+ } else {
1219
+ counter = "Incorrect Value";
1220
+ }
1221
+ if(false){
1222
+ counter = "Incorrect Value";
1223
+ }
1224
+
1225
+ TEST_OUTPUT = counter;
1226
+ `;
1227
+
1228
+ var firstObfuscation = await JsConfuser(code, {
1229
+ target: "node",
1230
+ controlFlowFlattening: true,
1231
+ });
1232
+ var secondObfuscation = await JsConfuser(firstObfuscation, {
1233
+ target: "node",
1234
+ controlFlowFlattening: true,
1235
+ });
1236
+
1237
+ var TEST_OUTPUT;
1238
+ eval(secondObfuscation);
1239
+
1240
+ expect(TEST_OUTPUT).toStrictEqual(10);
1241
+ });
1242
+
1243
+ test("Variant #35: Redefined function declaration + variable declaration", async () => {
1244
+ var code = `
1245
+ function push(str){
1246
+ TEST_OUTPUT.push(str);
1247
+ }
1248
+
1249
+ x();
1250
+
1251
+ var x = ()=>push("Top x");
1252
+
1253
+ x()
1254
+
1255
+ function x(){ push("Bottom x") }
1256
+
1257
+ if(true){
1258
+
1259
+ x();
1260
+
1261
+ function x(){ push("Nested x") }
1262
+ }
1263
+ `;
1264
+
1265
+ var output = await JsConfuser(code, {
1266
+ target: "node",
1267
+ controlFlowFlattening: true,
1268
+ });
1269
+
1270
+ var TEST_OUTPUT = [];
1271
+ eval(output);
1272
+
1273
+ expect(TEST_OUTPUT).toStrictEqual(["Bottom x", "Top x", "Nested x"]);
710
1274
  });