js-confuser 1.5.5 → 1.5.6

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.
package/CHANGELOG.md CHANGED
@@ -1,8 +1,26 @@
1
+ # `1.5.6`
2
+ Website changed and RGF fixes
3
+
4
+ The website is back at a different domain now: [https://master--hungry-shannon-c1ce6b.netlify.app/](https://master--hungry-shannon-c1ce6b.netlify.app/)
5
+
6
+ This update focuses on fixing RGF bugs
7
+
8
+ - Fixed [#64](https://github.com/MichaelXF/js-confuser/issues/64)
9
+ - - RGF to properly handle Arrow functions and function expressions
10
+
11
+ - RGF will no longer change getter/setter methods
12
+
13
+ - RGF will no longer change class methods
14
+
15
+ - RGF now works when using `mangled` variable names
16
+
17
+ - Minify will remove unreachable code following a Throw statement
18
+
1
19
  # `1.5.5`
2
20
  Updates
3
21
 
4
22
  - Fixed [#53](https://github.com/MichaelXF/js-confuser/issues/53)
5
- - - Shuffle to not use common varialbe names like `x`
23
+ - - Shuffle to not use common variable names like `x`
6
24
 
7
25
  - Fixed [#60](https://github.com/MichaelXF/js-confuser/issues/60)
8
26
  - - Rename Variables to properly handle function parameters
@@ -447,7 +465,7 @@ Available now on NPM: https://www.npmjs.com/package/js-confuser
447
465
  # `1.1.7`
448
466
  Website is Live
449
467
 
450
- - [JsConfuser.com](https://jsconfuser.com) is live!
468
+ - [https://master--hungry-shannon-c1ce6b.netlify.app/](https://master--hungry-shannon-c1ce6b.netlify.app/) is live!
451
469
  - Check out the [js-confuser-website](https://github.com/MichaelXF/js-confuser-website) repo for more info
452
470
 
453
471
  - **⚠️ Breaking change**: `Rename Globals` is now enabled by default
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # JS Confuser
2
2
 
3
- JS-Confuser is a JavaScript obfuscation tool to make your programs _impossible_ to read. [Try the web version](https://jsconfuser.com).
3
+ JS-Confuser is a JavaScript obfuscation tool to make your programs _impossible_ to read. [Try the web version](https://master--hungry-shannon-c1ce6b.netlify.app/).
4
4
 
5
- [![NPM](https://img.shields.io/badge/NPM-%23000000.svg?style=for-the-badge&logo=npm&logoColor=white)](https://npmjs.com/package/js-confuser) [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/MichaelXF/js-confuser) [![Netlify](https://img.shields.io/badge/netlify-%23000000.svg?style=for-the-badge&logo=netlify&logoColor=#00C7B7)](https://jsconfuser.com)
5
+ [![NPM](https://img.shields.io/badge/NPM-%23000000.svg?style=for-the-badge&logo=npm&logoColor=white)](https://npmjs.com/package/js-confuser) [![GitHub](https://img.shields.io/badge/github-%23121011.svg?style=for-the-badge&logo=github&logoColor=white)](https://github.com/MichaelXF/js-confuser) [![Netlify](https://img.shields.io/badge/netlify-%23000000.svg?style=for-the-badge&logo=netlify&logoColor=#00C7B7)](https://master--hungry-shannon-c1ce6b.netlify.app/)
6
6
 
7
7
  ## Key features
8
8
 
@@ -224,7 +224,7 @@ class Flatten extends _transform.default {
224
224
 
225
225
  var call = (0, _gen.VariableDeclaration)([(0, _gen.VariableDeclarator)(resultName, (0, _gen.ArrayExpression)([])), (0, _gen.VariableDeclarator)("_", (0, _gen.AssignmentExpression)("=", identifier, (0, _gen.ArrayExpression)([(0, _gen.ArrayExpression)(input.map(_gen.Identifier)), (0, _gen.ArrayExpression)([...newParamNodes]), (0, _gen.Identifier)(resultName)])))]); // result.pop()
226
226
 
227
- var pop = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), (0, _gen.Identifier)("pop"), false), []); // var result = newFn.call([...refs], ...arguments)
227
+ var pop = (0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(resultName), (0, _gen.Identifier)(propName), false), (0, _gen.Literal)("pop"), true), []); // var result = newFn.call([...refs], ...arguments)
228
228
  // modified1 = result.pop();
229
229
  // modified2 = result.pop();
230
230
  // ...modifiedN = result.pop();...
@@ -103,8 +103,10 @@ class VariableAnalysis extends _transform.default {
103
103
  var definingContexts = info.spec.isDefined ? (0, _insert.getAllDefiningContexts)(o, p) : (0, _insert.getReferencingContexts)(o, p, info);
104
104
  (0, _assert.ok)(definingContexts.length);
105
105
  definingContexts.forEach(definingContext => {
106
- (0, _assert.ok)((0, _insert.isContext)(definingContext), "".concat(definingContext.type, " is not a context"));
107
-
106
+ // ok(
107
+ // isContext(definingContext),
108
+ // `${definingContext.type} is not a context`
109
+ // );
108
110
  if (isDefined) {
109
111
  // Add to defined Map
110
112
  if (!this.defined.has(definingContext)) {
@@ -53,7 +53,7 @@ class Minify extends _transform.default {
53
53
  var earlyReturn = body.length;
54
54
  var fnDecs = [];
55
55
  body.forEach((stmt, i) => {
56
- if (stmt.type == "ReturnStatement" || stmt.type == "BreakStatement" || stmt.type == "ContinueStatement") {
56
+ if (stmt.type === "ReturnStatement" || stmt.type === "BreakStatement" || stmt.type === "ContinueStatement" || stmt.type === "ThrowStatement") {
57
57
  if (earlyReturn > i + 1) {
58
58
  earlyReturn = i + 1;
59
59
  }
@@ -136,7 +136,7 @@ class Minify extends _transform.default {
136
136
  if (!lastDec || lastDec.kind !== x.kind || !lastDec.declarations.length) {
137
137
  lastDec = x;
138
138
  } else {
139
- lastDec.declarations.push(...x.declarations);
139
+ lastDec.declarations.push(...(0, _insert.clone)(x.declarations));
140
140
  remove.unshift(i);
141
141
  }
142
142
  } else {
@@ -25,6 +25,8 @@ var _identifiers = require("../util/identifiers");
25
25
 
26
26
  var _insert = require("../util/insert");
27
27
 
28
+ var _random = require("../util/random");
29
+
28
30
  var _transform = _interopRequireDefault(require("./transform"));
29
31
 
30
32
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -69,9 +71,22 @@ class RGF extends _transform.default {
69
71
  var collect = [];
70
72
  var queue = [];
71
73
  var names = new Map();
74
+ var referenceSignatures = {};
72
75
  var definingNodes = new Map();
73
76
  (0, _traverse.walk)(contextObject, contextParents, (object, parents) => {
74
77
  if (object !== contextObject && (0, _insert.isFunction)(object) && !object.$requiresEval && !object.async && !object.generator && (0, _insert.getVarContext)(parents[0], parents.slice(1)) === contextObject) {
78
+ // Discard getter/setter methods
79
+ if (parents[0].type === "Property" && parents[0].value === object) {
80
+ if (parents[0].method || parents[0].kind === "get" || parents[0].kind === "set") {
81
+ return;
82
+ }
83
+ } // Discard class methods
84
+
85
+
86
+ if (parents[0].type === "MethodDefinition" && parents[0].value === object) {
87
+ return;
88
+ }
89
+
75
90
  var defined = new Set(),
76
91
  referenced = new Set();
77
92
  var isBound = false;
@@ -159,6 +174,7 @@ class RGF extends _transform.default {
159
174
  if (object.type == "FunctionDeclaration" && typeof object.id.name === "string") {
160
175
  var index = names.size;
161
176
  names.set(object.id.name, index);
177
+ referenceSignatures[index] = (0, _random.getRandomString)(10);
162
178
  definingNodes.set(object.id.name, object.id);
163
179
  }
164
180
  }
@@ -166,9 +182,10 @@ class RGF extends _transform.default {
166
182
 
167
183
  if (!queue.length) {
168
184
  return;
169
- }
185
+ } // An array containing all the function declarations
186
+
170
187
 
171
- var referenceArray = this.generateIdentifier();
188
+ var referenceArray = "_" + (0, _random.getRandomString)(10);
172
189
  (0, _traverse.walk)(contextObject, contextParents, (o, p) => {
173
190
  if (o.type == "Identifier" && !_constants.reservedIdentifiers.has(o.name)) {
174
191
  var index = names.get(o.name);
@@ -185,7 +202,19 @@ class RGF extends _transform.default {
185
202
 
186
203
  if (pointingTo == shouldBe) {
187
204
  this.log(o.name, "->", "".concat(referenceArray, "[").concat(index, "]"));
188
- this.replace(o, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(referenceArray), (0, _gen.Literal)(index), true), [(0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]));
205
+ var memberExpression = (0, _gen.MemberExpression)((0, _gen.Identifier)(referenceArray), (0, _gen.Literal)(index), true); // Allow re-assignment to the RGF function
206
+
207
+ if (p[0] && p[0].type === "AssignmentExpression" && p[0].left === o) {
208
+ // fn = ...
209
+ this.replace(o, memberExpression);
210
+ } else {
211
+ // fn()
212
+ // fn
213
+ // In most cases the identifier is being used like this (call expression, or referenced to be called later)
214
+ // Replace it with a simple wrapper function that will pass on the reference array
215
+ var conditionalExpression = (0, _gen.ConditionalExpression)((0, _template.default)("typeof ".concat(referenceArray, "[").concat(index, "] === \"function\" && ").concat(referenceArray, "[").concat(index, "][\"").concat(referenceSignatures[index] || "_", "\"]")).single().expression, (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)(memberExpression, [(0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]), memberExpression);
216
+ this.replace(o, conditionalExpression);
217
+ }
189
218
  }
190
219
  }
191
220
  }
@@ -202,6 +231,7 @@ class RGF extends _transform.default {
202
231
  var name = object === null || object === void 0 ? void 0 : (_object$id2 = object.id) === null || _object$id2 === void 0 ? void 0 : _object$id2.name;
203
232
  var hasName = !!name;
204
233
  var params = object.params.map(x => x.name) || [];
234
+ var signature = referenceSignatures[names.get(name)];
205
235
  var embeddedName = name || this.getPlaceholder(); // Since `new Function` is completely isolated, create an entire new obfuscator and run remaining transformations.
206
236
  // RGF runs early and needs completed code before converting to a string.
207
237
  // (^ the variables haven't been renamed yet)
@@ -222,7 +252,7 @@ class RGF extends _transform.default {
222
252
  };
223
253
  var tree = {
224
254
  type: "Program",
225
- body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedName), (0, _gen.Identifier)("call"), false), [(0, _gen.ThisExpression)(), (0, _gen.SpreadElement)((0, _template.default)("Array.prototype.slice.call(arguments, 1)").single().expression)]))]
255
+ body: [embeddedFunction, (0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)((0, _gen.Identifier)(embeddedName), (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.SpreadElement)((0, _template.default)("Array.prototype.slice.call(arguments, 1)").single().expression)]))]
226
256
  };
227
257
  tree.__hiddenDeclarations = (0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)(referenceArray));
228
258
  tree.__hiddenDeclarations.hidden = true;
@@ -241,8 +271,17 @@ class RGF extends _transform.default {
241
271
  var toString = (0, _compiler.compileJsSync)(tree, this.options);
242
272
  var newFunction = (0, _gen.NewExpression)((0, _gen.Identifier)("Function"), [(0, _gen.Literal)(referenceArray), (0, _gen.Literal)(toString)]);
243
273
 
244
- if (hasName) {
245
- arrayExpression.elements[names.get(name)] = newFunction;
274
+ function applySignature(fn) {
275
+ if (!signature) {
276
+ return fn;
277
+ } // This code marks the function object with a unique property
278
+
279
+
280
+ return (0, _gen.CallExpression)((0, _gen.FunctionExpression)([], [(0, _gen.VariableDeclaration)((0, _gen.VariableDeclarator)("fn", fn)), (0, _gen.ExpressionStatement)((0, _gen.AssignmentExpression)("=", (0, _gen.MemberExpression)((0, _gen.Identifier)("fn"), (0, _gen.Literal)(signature), true), (0, _gen.Literal)(true))), (0, _gen.ReturnStatement)((0, _gen.Identifier)("fn"))]), []);
281
+ }
282
+
283
+ if (object.type === "FunctionDeclaration") {
284
+ arrayExpression.elements[names.get(name)] = applySignature(newFunction);
246
285
 
247
286
  if (Array.isArray(parents[0])) {
248
287
  parents[0].splice(parents[0].indexOf(object), 1);
@@ -250,7 +289,9 @@ class RGF extends _transform.default {
250
289
  this.error(new Error("Error deleting function declaration: " + parents.map(x => x.type).join(",")));
251
290
  }
252
291
  } else {
253
- this.replace(object, newFunction);
292
+ // The wrapper function passes the reference array around
293
+ var wrapperFunction = (0, _gen.FunctionExpression)([], [(0, _gen.ReturnStatement)((0, _gen.CallExpression)((0, _gen.MemberExpression)(newFunction, (0, _gen.Literal)("call"), true), [(0, _gen.Identifier)("undefined"), (0, _gen.Identifier)(referenceArray), (0, _gen.SpreadElement)((0, _gen.Identifier)("arguments"))]))]);
294
+ this.replace(object, applySignature(wrapperFunction));
254
295
  }
255
296
  });
256
297
  };
@@ -45,28 +45,8 @@ class StringSplitting extends _transform.default {
45
45
  this.vars = [];
46
46
  }
47
47
 
48
- apply(tree) {
49
- super.apply(tree);
50
-
51
- if (this.vars.length) {
52
- (0, _random.shuffle)(this.adders);
53
- (0, _random.shuffle)(this.vars);
54
- var body = tree.body;
55
- this.adders.forEach(nodes => {
56
- nodes.forEach(x => body.unshift(x));
57
- });
58
- var variableDeclaration = {
59
- type: "VariableDeclaration",
60
- declarations: [],
61
- kind: "var"
62
- };
63
- this.vars.forEach(node => variableDeclaration.declarations.push(node));
64
- body.unshift(variableDeclaration);
65
- }
66
- }
67
-
68
48
  match(object, parents) {
69
- return object.type == "Literal" && typeof object.value === "string" && !(0, _stringConcealing.isModuleSource)(object, parents) && !(0, _compare.isDirective)(object, parents);
49
+ return object.type == "Literal" && typeof object.value === "string" && object.value.length >= 8 && !(0, _stringConcealing.isModuleSource)(object, parents) && !(0, _compare.isDirective)(object, parents);
70
50
  }
71
51
 
72
52
  transform(object, parents) {
@@ -92,7 +72,6 @@ class StringSplitting extends _transform.default {
92
72
  var last = chunks.pop();
93
73
  chunks.forEach((chunk, i) => {
94
74
  if (i == 0) {
95
- (0, _assert.ok)(i == 0);
96
75
  parent = binaryExpression = (0, _gen.BinaryExpression)("+", (0, _gen.Literal)(chunk), (0, _gen.Literal)(""));
97
76
  } else {
98
77
  binaryExpression.left = (0, _gen.BinaryExpression)("+", (0, _insert.clone)(binaryExpression.left), (0, _gen.Literal)(chunk));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "js-confuser",
3
- "version": "1.5.5",
3
+ "version": "1.5.6",
4
4
  "description": "JavaScript Obfuscation Tool.",
5
5
  "main": "dist/index.js",
6
6
  "types": "index.d.ts",
@@ -45,7 +45,7 @@
45
45
  "bugs": {
46
46
  "url": "https://github.com/MichaelXF/js-confuser/issues"
47
47
  },
48
- "homepage": "https://jsconfuser.com",
48
+ "homepage": "https://master--hungry-shannon-c1ce6b.netlify.app/",
49
49
  "jest": {
50
50
  "coverageReporters": [
51
51
  "html"
@@ -328,8 +328,8 @@ export default class Flatten extends Transform {
328
328
  var pop = CallExpression(
329
329
  MemberExpression(
330
330
  MemberExpression(Identifier(resultName), Identifier(propName), false),
331
- Identifier("pop"),
332
- false
331
+ Literal("pop"),
332
+ true
333
333
  ),
334
334
  []
335
335
  );
@@ -93,10 +93,10 @@ export default class VariableAnalysis extends Transform {
93
93
  ok(definingContexts.length);
94
94
 
95
95
  definingContexts.forEach((definingContext) => {
96
- ok(
97
- isContext(definingContext),
98
- `${definingContext.type} is not a context`
99
- );
96
+ // ok(
97
+ // isContext(definingContext),
98
+ // `${definingContext.type} is not a context`
99
+ // );
100
100
 
101
101
  if (isDefined) {
102
102
  // Add to defined Map
@@ -61,9 +61,10 @@ export default class Minify extends Transform {
61
61
 
62
62
  body.forEach((stmt, i) => {
63
63
  if (
64
- stmt.type == "ReturnStatement" ||
65
- stmt.type == "BreakStatement" ||
66
- stmt.type == "ContinueStatement"
64
+ stmt.type === "ReturnStatement" ||
65
+ stmt.type === "BreakStatement" ||
66
+ stmt.type === "ContinueStatement" ||
67
+ stmt.type === "ThrowStatement"
67
68
  ) {
68
69
  if (earlyReturn > i + 1) {
69
70
  earlyReturn = i + 1;
@@ -156,7 +157,7 @@ export default class Minify extends Transform {
156
157
  ) {
157
158
  lastDec = x;
158
159
  } else {
159
- lastDec.declarations.push(...x.declarations);
160
+ lastDec.declarations.push(...clone(x.declarations));
160
161
  remove.unshift(i);
161
162
  }
162
163
  } else {
@@ -7,7 +7,10 @@ import Template from "../templates/template";
7
7
  import traverse, { walk } from "../traverse";
8
8
  import {
9
9
  ArrayExpression,
10
+ AssignmentExpression,
10
11
  CallExpression,
12
+ ConditionalExpression,
13
+ ExpressionStatement,
11
14
  FunctionExpression,
12
15
  Identifier,
13
16
  Literal,
@@ -28,6 +31,7 @@ import {
28
31
  isFunction,
29
32
  prepend,
30
33
  } from "../util/insert";
34
+ import { getRandomString } from "../util/random";
31
35
  import Transform from "./transform";
32
36
 
33
37
  /**
@@ -70,6 +74,8 @@ export default class RGF extends Transform {
70
74
  }[] = [];
71
75
  var queue: Location[] = [];
72
76
  var names = new Map<string, number>();
77
+ var referenceSignatures: { [name: string]: string } = {};
78
+
73
79
  var definingNodes = new Map<string, Node>();
74
80
 
75
81
  walk(contextObject, contextParents, (object, parents) => {
@@ -81,6 +87,25 @@ export default class RGF extends Transform {
81
87
  !object.generator &&
82
88
  getVarContext(parents[0], parents.slice(1)) === contextObject
83
89
  ) {
90
+ // Discard getter/setter methods
91
+ if (parents[0].type === "Property" && parents[0].value === object) {
92
+ if (
93
+ parents[0].method ||
94
+ parents[0].kind === "get" ||
95
+ parents[0].kind === "set"
96
+ ) {
97
+ return;
98
+ }
99
+ }
100
+
101
+ // Discard class methods
102
+ if (
103
+ parents[0].type === "MethodDefinition" &&
104
+ parents[0].value === object
105
+ ) {
106
+ return;
107
+ }
108
+
84
109
  var defined = new Set<string>(),
85
110
  referenced = new Set<string>();
86
111
 
@@ -177,6 +202,8 @@ export default class RGF extends Transform {
177
202
  var index = names.size;
178
203
 
179
204
  names.set(object.id.name, index);
205
+ referenceSignatures[index] = getRandomString(10);
206
+
180
207
  definingNodes.set(object.id.name, object.id);
181
208
  }
182
209
  }
@@ -186,7 +213,8 @@ export default class RGF extends Transform {
186
213
  return;
187
214
  }
188
215
 
189
- var referenceArray = this.generateIdentifier();
216
+ // An array containing all the function declarations
217
+ var referenceArray = "_" + getRandomString(10);
190
218
 
191
219
  walk(contextObject, contextParents, (o, p) => {
192
220
  if (o.type == "Identifier" && !reservedIdentifiers.has(o.name)) {
@@ -204,27 +232,50 @@ export default class RGF extends Transform {
204
232
  if (pointingTo == shouldBe) {
205
233
  this.log(o.name, "->", `${referenceArray}[${index}]`);
206
234
 
207
- this.replace(
208
- o,
209
- FunctionExpression(
210
- [],
211
- [
212
- ReturnStatement(
213
- CallExpression(
214
- MemberExpression(
215
- Identifier(referenceArray),
216
- Literal(index),
217
- true
218
- ),
219
- [
235
+ var memberExpression = MemberExpression(
236
+ Identifier(referenceArray),
237
+ Literal(index),
238
+ true
239
+ );
240
+
241
+ // Allow re-assignment to the RGF function
242
+ if (
243
+ p[0] &&
244
+ p[0].type === "AssignmentExpression" &&
245
+ p[0].left === o
246
+ ) {
247
+ // fn = ...
248
+
249
+ this.replace(o, memberExpression);
250
+ } else {
251
+ // fn()
252
+ // fn
253
+
254
+ // In most cases the identifier is being used like this (call expression, or referenced to be called later)
255
+ // Replace it with a simple wrapper function that will pass on the reference array
256
+
257
+ var conditionalExpression = ConditionalExpression(
258
+ Template(
259
+ `typeof ${referenceArray}[${index}] === "function" && ${referenceArray}[${index}]["${
260
+ referenceSignatures[index] || "_"
261
+ }"]`
262
+ ).single().expression,
263
+ FunctionExpression(
264
+ [],
265
+ [
266
+ ReturnStatement(
267
+ CallExpression(memberExpression, [
220
268
  Identifier(referenceArray),
221
269
  SpreadElement(Identifier("arguments")),
222
- ]
223
- )
224
- ),
225
- ]
226
- )
227
- );
270
+ ])
271
+ ),
272
+ ]
273
+ ),
274
+ memberExpression
275
+ );
276
+
277
+ this.replace(o, conditionalExpression);
278
+ }
228
279
  }
229
280
  }
230
281
  }
@@ -243,6 +294,7 @@ export default class RGF extends Transform {
243
294
  var name = object?.id?.name;
244
295
  var hasName = !!name;
245
296
  var params = object.params.map((x) => x.name) || [];
297
+ var signature = referenceSignatures[names.get(name)];
246
298
 
247
299
  var embeddedName = name || this.getPlaceholder();
248
300
 
@@ -280,11 +332,11 @@ export default class RGF extends Transform {
280
332
  CallExpression(
281
333
  MemberExpression(
282
334
  Identifier(embeddedName),
283
- Identifier("call"),
284
- false
335
+ Literal("call"),
336
+ true
285
337
  ),
286
338
  [
287
- ThisExpression(),
339
+ Identifier("undefined"),
288
340
  SpreadElement(
289
341
  Template(
290
342
  `Array.prototype.slice.call(arguments, 1)`
@@ -322,8 +374,38 @@ export default class RGF extends Transform {
322
374
  Literal(toString),
323
375
  ]);
324
376
 
325
- if (hasName) {
326
- arrayExpression.elements[names.get(name)] = newFunction;
377
+ function applySignature(fn) {
378
+ if (!signature) {
379
+ return fn;
380
+ }
381
+
382
+ // This code marks the function object with a unique property
383
+ return CallExpression(
384
+ FunctionExpression(
385
+ [],
386
+ [
387
+ VariableDeclaration(VariableDeclarator("fn", fn)),
388
+ ExpressionStatement(
389
+ AssignmentExpression(
390
+ "=",
391
+ MemberExpression(
392
+ Identifier("fn"),
393
+ Literal(signature),
394
+ true
395
+ ),
396
+ Literal(true)
397
+ )
398
+ ),
399
+ ReturnStatement(Identifier("fn")),
400
+ ]
401
+ ),
402
+ []
403
+ );
404
+ }
405
+
406
+ if (object.type === "FunctionDeclaration") {
407
+ arrayExpression.elements[names.get(name)] =
408
+ applySignature(newFunction);
327
409
 
328
410
  if (Array.isArray(parents[0])) {
329
411
  parents[0].splice(parents[0].indexOf(object), 1);
@@ -336,7 +418,24 @@ export default class RGF extends Transform {
336
418
  );
337
419
  }
338
420
  } else {
339
- this.replace(object, newFunction);
421
+ // The wrapper function passes the reference array around
422
+ var wrapperFunction = FunctionExpression(
423
+ [],
424
+ [
425
+ ReturnStatement(
426
+ CallExpression(
427
+ MemberExpression(newFunction, Literal("call"), true),
428
+ [
429
+ Identifier("undefined"),
430
+ Identifier(referenceArray),
431
+ SpreadElement(Identifier("arguments")),
432
+ ]
433
+ )
434
+ ),
435
+ ]
436
+ );
437
+
438
+ this.replace(object, applySignature(wrapperFunction));
340
439
  }
341
440
  });
342
441
  };
@@ -25,34 +25,11 @@ export default class StringSplitting extends Transform {
25
25
  this.vars = [];
26
26
  }
27
27
 
28
- apply(tree) {
29
- super.apply(tree);
30
-
31
- if (this.vars.length) {
32
- shuffle(this.adders);
33
- shuffle(this.vars);
34
-
35
- var body: Node[] = tree.body;
36
-
37
- this.adders.forEach((nodes) => {
38
- nodes.forEach((x) => body.unshift(x));
39
- });
40
-
41
- var variableDeclaration = {
42
- type: "VariableDeclaration",
43
- declarations: [],
44
- kind: "var",
45
- };
46
- this.vars.forEach((node) => variableDeclaration.declarations.push(node));
47
-
48
- body.unshift(variableDeclaration);
49
- }
50
- }
51
-
52
28
  match(object: Node, parents: Node[]) {
53
29
  return (
54
30
  object.type == "Literal" &&
55
31
  typeof object.value === "string" &&
32
+ object.value.length >= 8 &&
56
33
  !isModuleSource(object, parents) &&
57
34
  !isDirective(object, parents)
58
35
  );
@@ -87,7 +64,6 @@ export default class StringSplitting extends Transform {
87
64
  var last = chunks.pop();
88
65
  chunks.forEach((chunk, i) => {
89
66
  if (i == 0) {
90
- ok(i == 0);
91
67
  parent = binaryExpression = BinaryExpression(
92
68
  "+",
93
69
  Literal(chunk),
@@ -301,9 +301,9 @@ test("Variant #15: Removing implied 'return'", async () => {
301
301
  });
302
302
 
303
303
  // https://github.com/MichaelXF/js-confuser/issues/43
304
- test("Variant #16: Handle deconstructuring in for loop", async ()=>{
305
- // Valid
306
- var output = await JsConfuser(
304
+ test("Variant #16: Handle deconstructuring in for loop", async () => {
305
+ // Valid
306
+ var output = await JsConfuser(
307
307
  `
308
308
  for(const [a] of [[1]]) {
309
309
  input(a);
@@ -313,11 +313,56 @@ var output = await JsConfuser(
313
313
  );
314
314
 
315
315
  var value;
316
- function input(valueIn){
316
+ function input(valueIn) {
317
317
  value = valueIn;
318
318
  }
319
319
 
320
320
  eval(output);
321
321
 
322
322
  expect(value).toStrictEqual(1);
323
- })
323
+ });
324
+
325
+ test("Variant #17: Remove unreachable code following a return statement", async () => {
326
+ var output = await JsConfuser(
327
+ `
328
+ function myFunction(){
329
+ return;
330
+ unreachableStmt;
331
+ }
332
+ `,
333
+ { target: "node", minify: true }
334
+ );
335
+
336
+ expect(output).not.toContain("unreachableStmt");
337
+ });
338
+
339
+ test("Variant #18: Remove unreachable code following a continue or break statement", async () => {
340
+ var output = await JsConfuser(
341
+ `
342
+ for(var i =0; i < 10; i++){
343
+ continue;
344
+ unreachableStmt
345
+ }
346
+
347
+ while(true){
348
+ break;
349
+ unreachableStmt
350
+ }
351
+ `,
352
+ { target: "node", minify: true }
353
+ );
354
+
355
+ expect(output).not.toContain("unreachableStmt");
356
+ });
357
+
358
+ test("Variant #19: Remove unreachable code following a throw statement", async () => {
359
+ var output = await JsConfuser(
360
+ `
361
+ throw new Error("No more code to run");
362
+ unreachableStmt;
363
+ `,
364
+ { target: "node", minify: true }
365
+ );
366
+
367
+ expect(output).not.toContain("unreachableStmt");
368
+ });
@@ -154,6 +154,103 @@ input(console.log, result)
154
154
  eval(output);
155
155
  expect(value).toStrictEqual(60);
156
156
  });
157
+
158
+ // https://github.com/MichaelXF/js-confuser/issues/64
159
+ it("should work on Arrow Functions", async () => {
160
+ var output = await JsConfuser.obfuscate(
161
+ `
162
+ var double = (num)=>num*2;
163
+ TEST_VALUE = double(10);
164
+ `,
165
+ {
166
+ target: "node",
167
+ rgf: true,
168
+ }
169
+ );
170
+
171
+ expect(output).toContain("new Function");
172
+
173
+ var TEST_VALUE;
174
+
175
+ eval(output);
176
+ expect(TEST_VALUE).toStrictEqual(20);
177
+ });
178
+
179
+ it("should work on Function Expressions", async () => {
180
+ var output = await JsConfuser.obfuscate(
181
+ `
182
+ var double = function(num){
183
+ return num * 2
184
+ };
185
+ TEST_VALUE = double(10);
186
+ `,
187
+ {
188
+ target: "node",
189
+ rgf: true,
190
+ }
191
+ );
192
+
193
+ expect(output).toContain("new Function");
194
+
195
+ var TEST_VALUE;
196
+
197
+ eval(output);
198
+ expect(TEST_VALUE).toStrictEqual(20);
199
+ });
200
+
201
+ it("should work on re-assigned functions", async () => {
202
+ var output = await JsConfuser.obfuscate(
203
+ `
204
+ var fn1 = ()=>{
205
+ return "FN1";
206
+ }
207
+ var fn2 = ()=>{
208
+ fn1 = ()=>{
209
+ return "FN1 - Modified"
210
+ }
211
+ }
212
+ fn2();
213
+ TEST_VALUE = fn1();
214
+ `,
215
+ {
216
+ target: "node",
217
+ rgf: true,
218
+ }
219
+ );
220
+
221
+ expect(output).toContain("new Function");
222
+
223
+ var TEST_VALUE;
224
+
225
+ eval(output);
226
+ expect(TEST_VALUE).toStrictEqual("FN1 - Modified");
227
+ });
228
+
229
+ it("should work on re-assigned functions to non-function values", async () => {
230
+ var output = await JsConfuser.obfuscate(
231
+ `
232
+ var fn1 = ()=>{
233
+ return "FN1";
234
+ }
235
+ var fn2 = ()=>{
236
+ fn1 = undefined;
237
+ }
238
+ fn2();
239
+ TEST_VALUE = typeof fn1;
240
+ `,
241
+ {
242
+ target: "node",
243
+ rgf: true,
244
+ }
245
+ );
246
+
247
+ expect(output).toContain("new Function");
248
+
249
+ var TEST_VALUE;
250
+
251
+ eval(output);
252
+ expect(TEST_VALUE).toStrictEqual("undefined");
253
+ });
157
254
  });
158
255
 
159
256
  describe("RGF with the 'all' mode", () => {