expose-kit 0.10.0 → 0.10.2

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/README.md CHANGED
@@ -1,230 +1,233 @@
1
- # Expose Kit
2
- ![release workflow](https://github.com/evex-dev/linejs/actions/workflows/release.yml/badge.svg)
3
- [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
4
-
5
- > A universal toolkit for JavaScript deobfuscation
6
-
7
- ---
8
-
9
- ## What is this?
10
-
11
- JavaScript deobfuscation tools are everywhere.
12
- But many of them are **too aggressive**, rewriting code until it breaks.
13
-
14
- Expose Kit takes a **different approach**.
15
-
16
- - No brute force
17
- - Step-by-step, verifiable transforms
18
- - Designed to *not* break your code silently
19
-
20
- Each transformation is meant to be **checked and validated**, so you always know *when* something goes wrong.
21
-
22
- Alongside deobfuscation, Expose Kit also provides a set of **practical utilities** for working with obfuscated JavaScript.
23
-
24
- ---
25
-
26
- ## Installation
27
-
28
- Just one step:
29
-
30
- ```bash
31
- npm i -g expose-kit
32
- # or
33
- bun i -g expose-kit
34
- ```
35
-
36
- ```bash
37
- expose --help
38
- expose parsable sample.js
39
- ```
40
-
41
- ---
42
-
43
- ## Usage Notes
44
-
45
- ### Default arguments
46
-
47
- - The first argument is the input file
48
- (`--file` / `--input` can also be used)
49
- - If required options are missing, Expose Kit will **prompt you**
50
- - A timeout is enabled by default to avoid hangs
51
- Use `--unlimited` for long-running execution
52
-
53
- ---
54
-
55
- ## Recommended Workflow
56
-
57
- First, an important premise:
58
-
59
- > It is **impossible** to create a static deobfuscation tool that *never* breaks.
60
-
61
- Reasons include:
62
- - Unpredictable execution (`eval`, dynamic code)
63
- - Bugs or edge cases in AST manipulation
64
-
65
- Because of this, you should **verify the code at every step**.
66
-
67
- ### 1. Always verify with `parsable`
68
-
69
- After each transformation, run:
70
-
71
- ```bash
72
- expose parsable file.js
73
- ```
74
-
75
- This ensures the syntax is still valid.
76
-
77
- ---
78
-
79
- ### 2. Make scopes safe first
80
-
81
- One of the most common causes of breakage is **variable name confusion**.
82
-
83
- If you try to write your own deobfuscation logic (e.g. in Python), you’ll quickly realize how painful it is to track scopes correctly.
84
-
85
- That’s why you should **always start with**:
86
-
87
- ```bash
88
- expose safe-scope input.js
89
- ```
90
-
91
- This renames bindings per scope, producing code like:
92
-
93
- ```js
94
- Before: var x = 810;((x) => console.log(x))(114514);
95
- After: var x = 810;((_x) => console.log(_x))(114514);
96
- ```
97
-
98
- With this alone:
99
- - The code becomes far more resistant to breakage
100
- - Writing custom deobfuscation logic becomes much easier
101
- - You no longer need to worry about scope collisions
102
-
103
- ---
104
-
105
- ### 3. Apply transforms step by step
106
-
107
- After `safe-scope`, combine common techniques like:
108
- - `expand-array`, `expand-object` and more
109
- - legacy obfuscator-specific commands
110
-
111
- After **each step**, run `parsable` again.
112
-
113
- Expose Kit will also clearly indicate whether a **diff** exists, making inspection easy.
114
-
115
- Repeat this process, and the original code will gradually reveal itself.
116
-
117
- ---
118
-
119
- ## Commands
120
-
121
- ### `expose parsable`
122
-
123
- Check whether a file is syntactically valid.
124
-
125
- ```js
126
- parsable: const x = 810;
127
- not parsable: const ;x; == 810;
128
- ```
129
-
130
- ```bash
131
- expose parsable path/to/file.js
132
- ```
133
-
134
- Args:
135
- - Default args only
136
-
137
- ---
138
-
139
- ### `expose safe-scope`
140
-
141
- Rename bindings per scope for safer transformations.
142
-
143
- ```bash
144
- expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
145
- ```
146
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/safe-scope/mocks).
147
-
148
- Args:
149
- - `--o, --output <file>`
150
- Output file path
151
- - No extension → `file.safe-scope.js`
152
- - With extension → `file.safe-scope.<ext>`
153
-
154
- ---
155
-
156
- ### `expose pre-evaluate`
157
-
158
- Pre-evaluate const numeric/string expressions. (Safe evaluate)
159
-
160
- ```js
161
- const a = 1 + 2 * 3; // => 7
162
- const c = "aaaa";
163
- const b = "a" + c; // => "aaaaa"
164
- ```
165
-
166
- ```bash
167
- expose pre-evaluate path/to/file.js --output path/to/file.pre-evaluate.js
168
- ```
169
-
170
- Args:
171
- - `--o, --output <file>`
172
- Output file path
173
- - No extension → `file.pre-evaluate.js`
174
- - With extension → `file.pre-evaluate.<ext>`
175
-
176
- ---
177
-
178
- ### `expose expand-array`
179
-
180
- Expand array index access for primitive values.
181
- ```js
182
- var a = [1, 1, 4, 5, 1, 4];
183
- // before
184
- console.log(a[0], a[2], a[3]);
185
- // after
186
- console.log(1, 4, 5);
187
- ```
188
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-array/mocks).
189
-
190
- ```bash
191
- expose expand-array path/to/file.js --target arrayName --output path/to/file.expand-array.js
192
- ```
193
-
194
- Args:
195
- - `--target <name>`
196
- Target array variable name
197
- - `--o, --output <file>`
198
- Output file path
199
-
200
- Notes:
201
- - Each replacement is validated by reparsing; unsafe replacements are skipped.
202
- (This array is intended to be immutable, so caution is required)
203
-
204
- ---
205
-
1
+ # Expose Kit
2
+ ![release workflow](https://github.com/evex-dev/linejs/actions/workflows/release.yml/badge.svg)
3
+ [![](https://dcbadge.limes.pink/api/server/evex)](https://discord.gg/evex)
4
+
5
+ > A universal toolkit for JavaScript deobfuscation
6
+
7
+ ---
8
+
9
+ ## What is this?
10
+
11
+ JavaScript deobfuscation tools are everywhere.
12
+ But many of them are **too aggressive**, rewriting code until it breaks.
13
+
14
+ Expose Kit takes a **different approach**.
15
+
16
+ - No brute force
17
+ - Step-by-step, verifiable transforms
18
+ - Designed to *not* break your code silently
19
+
20
+ Each transformation is meant to be **checked and validated**, so you always know *when* something goes wrong.
21
+
22
+ Alongside deobfuscation, Expose Kit also provides a set of **practical utilities** for working with obfuscated JavaScript.
23
+
24
+ ---
25
+
26
+ ## Installation
27
+
28
+ Just one step:
29
+
30
+ ```bash
31
+ npm i -g expose-kit
32
+ # or
33
+ bun i -g expose-kit
34
+ ```
35
+
36
+ ```bash
37
+ expose --help
38
+ expose parsable sample.js
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Usage Notes
44
+
45
+ ### Default arguments
46
+
47
+ - The first argument is the input file
48
+ (`--file` / `--input` can also be used)
49
+ - If required options are missing, Expose Kit will **prompt you**
50
+ - A timeout is enabled by default to avoid hangs
51
+ Use `--unlimited` for long-running execution
52
+
53
+ ---
54
+
55
+ ## Recommended Workflow
56
+
57
+ First, an important premise:
58
+
59
+ > It is **impossible** to create a static deobfuscation tool that *never* breaks.
60
+
61
+ Reasons include:
62
+ - Unpredictable execution (`eval`, dynamic code)
63
+ - Bugs or edge cases in AST manipulation
64
+
65
+ Because of this, you should **verify the code at every step**.
66
+
67
+ ### 1. Always verify with `parsable`
68
+
69
+ After each transformation, run:
70
+
71
+ ```bash
72
+ expose parsable file.js
73
+ ```
74
+
75
+ This ensures the syntax is still valid.
76
+
77
+ ---
78
+
79
+ ### 2. Make scopes safe first
80
+
81
+ One of the most common causes of breakage is **variable name confusion**.
82
+
83
+ If you try to write your own deobfuscation logic (e.g. in Python), you’ll quickly realize how painful it is to track scopes correctly.
84
+
85
+ That’s why you should **always start with**:
86
+
87
+ ```bash
88
+ expose safe-scope input.js
89
+ ```
90
+
91
+ This renames bindings per scope, producing code like:
92
+
93
+ ```js
94
+ Before: var x = 810;((x) => console.log(x))(114514);
95
+ After: var x = 810;((_x) => console.log(_x))(114514);
96
+ ```
97
+
98
+ With this alone:
99
+ - The code becomes far more resistant to breakage
100
+ - Writing custom deobfuscation logic becomes much easier
101
+ - You no longer need to worry about scope collisions
102
+
103
+ ---
104
+
105
+ ### 3. Apply transforms step by step
106
+
107
+ After `safe-scope`, combine common techniques like:
108
+ - `expand-array`, `expand-object` and more
109
+
110
+ After **each step**, run `parsable` and `remove-unused` again.
111
+ (even if it's not frequent, we might overlook something broken)
112
+
113
+ Expose Kit will also clearly indicate whether a **diff** exists, making inspection easy.
114
+
115
+ Repeat this process, and the original code will gradually reveal itself.
116
+
117
+ ---
118
+
119
+ ## Commands
120
+
121
+ ### `expose parsable`
122
+
123
+ Check whether a file is syntactically valid.
124
+
125
+ ```js
126
+ parsable: const x = 810;
127
+ not parsable: const ;x; == 810;
128
+ ```
129
+
130
+ ```bash
131
+ expose parsable path/to/file.js
132
+ ```
133
+
134
+ Args:
135
+ - Default args only
136
+
137
+ ---
138
+
139
+ ### `expose safe-scope`
140
+
141
+ Rename bindings per scope for safer transformations.
142
+
143
+ ```bash
144
+ expose safe-scope path/to/file.js --output path/to/file.safe-scope.js
145
+ ```
146
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/safe-scope/mocks).
147
+
148
+ Args:
149
+ - `--o, --output <file>`
150
+ Output file path
151
+ - No extension → `file.safe-scope.js`
152
+ - With extension → `file.safe-scope.<ext>`
153
+
154
+ ---
155
+
156
+ ### `expose pre-evaluate`
157
+
158
+ Pre-evaluate const numeric/string expressions. (Safe evaluate)
159
+
160
+ ```js
161
+ const a = 1 + 2 * 3; // => 7
162
+ const c = "aaaa";
163
+ const b = "a" + c; // => "aaaaa"
164
+ ```
165
+
166
+ ```bash
167
+ expose pre-evaluate path/to/file.js --output path/to/file.pre-evaluate.js
168
+ ```
169
+
170
+ Args:
171
+ - `--o, --output <file>`
172
+ Output file path
173
+ - No extension → `file.pre-evaluate.js`
174
+ - With extension → `file.pre-evaluate.<ext>`
175
+
176
+ Notes:
177
+ - Inlines zero-arg functions that return safe array literals by hoisting them to a var.
178
+
179
+ ---
180
+
181
+ ### `expose expand-array`
182
+
183
+ Expand array index access for primitive values.
184
+ ```js
185
+ var a = [1, 1, 4, 5, 1, 4];
186
+ // before
187
+ console.log(a[0], a[2], a[3]);
188
+ // after
189
+ console.log(1, 4, 5);
190
+ ```
191
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-array/mocks).
192
+
193
+ ```bash
194
+ expose expand-array path/to/file.js --target arrayName --output path/to/file.expand-array.js
195
+ ```
196
+
197
+ Args:
198
+ - `--target <name>`
199
+ Target array variable name
200
+ - `--o, --output <file>`
201
+ Output file path
202
+
203
+ Notes:
204
+ - Each replacement is validated by reparsing; unsafe replacements are skipped.
205
+ (This array is intended to be immutable, so caution is required)
206
+
207
+ ---
208
+
206
209
  ### `expose expand-object`
207
-
208
- Expand object property access for primitive values.
209
- ```js
210
- const obj = { a: 1, b: 2 };
211
- // before
212
- console.log(obj.a, obj["b"]);
213
- // after
214
- console.log(1, 2);
215
- ```
216
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
217
-
218
- ```bash
219
- expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
220
- ```
221
-
222
- Args:
223
- - `--target <name>`
224
- Target object variable name
225
- - `--o, --output <file>`
226
- Output file path
227
-
210
+
211
+ Expand object property access for primitive values.
212
+ ```js
213
+ const obj = { a: 1, b: 2 };
214
+ // before
215
+ console.log(obj.a, obj["b"]);
216
+ // after
217
+ console.log(1, 2);
218
+ ```
219
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/expand-object/mocks).
220
+
221
+ ```bash
222
+ expose expand-object path/to/file.js --target objectName --output path/to/file.expand-object.js
223
+ ```
224
+
225
+ Args:
226
+ - `--target <name>`
227
+ Target object variable name
228
+ - `--o, --output <file>`
229
+ Output file path
230
+
228
231
  Notes:
229
232
  - Each replacement is validated by reparsing; unsafe replacements are skipped.
230
233
  (This object is intended to be immutable, so caution is required)
@@ -376,35 +379,35 @@ Notes:
376
379
  ### `expose remove-unused`
377
380
 
378
381
  Remove unused variabless.
379
- ```js
380
- // before
381
- var a = 0, b = 1;
382
- console.log(a);
383
- // after
384
- var a = 0;
385
- console.log(a);
386
- ```
387
- Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
388
-
389
- ```bash
390
- expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
391
- ```
392
-
393
- Args:
394
- - `--o, --output <file>`
395
- Output file path
396
-
397
-
398
- ## Community & Support
399
-
400
- - Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
401
- - Not sure which command to use? → Join our [Discord](https://evex.land)
402
-
403
- ---
404
-
405
- ## Author
406
-
407
- - [EdamAme-x](https://github.com/EdamAme-x)
408
-
409
- Built for research, not abuse.
410
- Want stronger obfuscation? Then build something this tool can’t reverse.
382
+ ```js
383
+ // before
384
+ var a = 0, b = 1;
385
+ console.log(a);
386
+ // after
387
+ var a = 0;
388
+ console.log(a);
389
+ ```
390
+ Example is [here](https://github.com/evex-dev/expose-kit/tree/main/commands/remove-unused/mocks).
391
+
392
+ ```bash
393
+ expose remove-unused path/to/file.js --output path/to/file.remove-unused.js
394
+ ```
395
+
396
+ Args:
397
+ - `--o, --output <file>`
398
+ Output file path
399
+
400
+
401
+ ## Community & Support
402
+
403
+ - Missing a feature? → [Create an issue](https://github.com/EdamAme-x/expose-kit/issues)
404
+ - Not sure which command to use? → Join our [Discord](https://evex.land)
405
+
406
+ ---
407
+
408
+ ## Author
409
+
410
+ - [EdamAme-x](https://github.com/EdamAme-x)
411
+
412
+ Built for research, not abuse.
413
+ Want stronger obfuscation? Then build something this tool can’t reverse.
package/dist/index.js CHANGED
@@ -8,8 +8,8 @@ import chalk4 from "chalk";
8
8
 
9
9
  // utils/cli/createCommand.ts
10
10
  var createCommand = (creator) => {
11
- return (program2) => {
12
- creator(program2);
11
+ return (program3) => {
12
+ creator(program3);
13
13
  };
14
14
  };
15
15
 
@@ -85,8 +85,8 @@ var showError = (message) => {
85
85
  };
86
86
 
87
87
  // commands/parsable/index.ts
88
- var parsable_default = createCommand((program2) => {
89
- program2.command("parsable").description("Check if the file is parsable").argument("[file]", "The file to check").option("--input, --file <file>", "The file to check").option("--unlimited", "Unlimited timeout").action(
88
+ var parsable_default = createCommand((program3) => {
89
+ program3.command("parsable").description("Check if the file is parsable").argument("[file]", "The file to check").option("--input, --file <file>", "The file to check").option("--unlimited", "Unlimited timeout").action(
90
90
  async (fileArgument, options) => {
91
91
  await timeout(
92
92
  async ({ finish }) => {
@@ -215,8 +215,8 @@ var renameBindingsByScope = (code, filename) => {
215
215
  });
216
216
  return patchDefault(generate)(ast).code;
217
217
  };
218
- var safe_scope_default = createCommand((program2) => {
219
- program2.command("safe-scope").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
218
+ var safe_scope_default = createCommand((program3) => {
219
+ program3.command("safe-scope").description("Rename bindings per scope for safer transforms").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
220
220
  async (fileArgument, options) => {
221
221
  await timeout(
222
222
  async ({ finish }) => {
@@ -450,8 +450,8 @@ var expandArrayAccess = async (code, filename, targetName) => {
450
450
  replacedCount
451
451
  };
452
452
  };
453
- var expand_array_default = createCommand((program2) => {
454
- program2.command("expand-array").description("Expand array index access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target array variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
453
+ var expand_array_default = createCommand((program3) => {
454
+ program3.command("expand-array").description("Expand array index access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target array variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
455
455
  async (fileArgument, options) => {
456
456
  await timeout(
457
457
  async ({ finish }) => {
@@ -721,8 +721,8 @@ var expandObjectAccess = async (code, filename, targetName) => {
721
721
  replacedCount
722
722
  };
723
723
  };
724
- var expand_object_default = createCommand((program2) => {
725
- program2.command("expand-object").description("Expand object property access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target object variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
724
+ var expand_object_default = createCommand((program3) => {
725
+ program3.command("expand-object").description("Expand object property access for primitive values").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--target <name>", "Target object variable name").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
726
726
  async (fileArgument, options) => {
727
727
  await timeout(
728
728
  async ({ finish }) => {
@@ -908,8 +908,8 @@ var packObjectProperties = (code, filename) => {
908
908
  removedStatements
909
909
  };
910
910
  };
911
- var object_packer_default = createCommand((program2) => {
912
- program2.command("object-packer").description("Pack consecutive object property assignments into literals").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
911
+ var object_packer_default = createCommand((program3) => {
912
+ program3.command("object-packer").description("Pack consecutive object property assignments into literals").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
913
913
  async (fileArgument, options) => {
914
914
  await timeout(
915
915
  async ({ finish }) => {
@@ -1105,6 +1105,108 @@ var shouldSkipReferencedIdentifier = (path) => {
1105
1105
  }
1106
1106
  return false;
1107
1107
  };
1108
+ var getArrayReturnExpression = (node) => {
1109
+ if (node.params.length !== 0) return null;
1110
+ if (t4.isBlockStatement(node.body)) {
1111
+ if (node.body.body.length !== 1) return null;
1112
+ const statement = node.body.body[0];
1113
+ if (!t4.isReturnStatement(statement) || !statement.argument) return null;
1114
+ return t4.isArrayExpression(statement.argument) ? statement.argument : null;
1115
+ }
1116
+ return t4.isArrayExpression(node.body) ? node.body : null;
1117
+ };
1118
+ var isSafeArrayElement = (node) => {
1119
+ if (!node) return true;
1120
+ if (t4.isNumericLiteral(node) || t4.isStringLiteral(node) || t4.isBooleanLiteral(node) || t4.isNullLiteral(node)) {
1121
+ return true;
1122
+ }
1123
+ if (t4.isUnaryExpression(node) && (node.operator === "-" || node.operator === "+") && t4.isNumericLiteral(node.argument)) {
1124
+ return true;
1125
+ }
1126
+ if (t4.isArrayExpression(node)) {
1127
+ return node.elements.every((element) => {
1128
+ if (element === null) return true;
1129
+ if (t4.isSpreadElement(element)) return false;
1130
+ return isSafeArrayElement(element);
1131
+ });
1132
+ }
1133
+ return false;
1134
+ };
1135
+ var isSafeArrayExpression = (node) => {
1136
+ return node.elements.every((element) => {
1137
+ if (element === null) return true;
1138
+ if (t4.isSpreadElement(element)) return false;
1139
+ return isSafeArrayElement(element);
1140
+ });
1141
+ };
1142
+ var collectArrayReturnFunctions = (ast) => {
1143
+ const results = [];
1144
+ let counter = 0;
1145
+ patchDefault(traverse5)(ast, {
1146
+ FunctionDeclaration(path) {
1147
+ if (!path.node.id) return;
1148
+ const arrayExpression = getArrayReturnExpression(path.node);
1149
+ if (!arrayExpression || !isSafeArrayExpression(arrayExpression)) return;
1150
+ const binding = path.scope.getBinding(path.node.id.name);
1151
+ if (!binding) return;
1152
+ const programScope = path.scope.getProgramParent();
1153
+ let replacementName = `${path.node.id.name}_${counter}`;
1154
+ counter += 1;
1155
+ while (programScope.hasBinding(replacementName)) {
1156
+ replacementName = `${path.node.id.name}_${counter}`;
1157
+ counter += 1;
1158
+ }
1159
+ results.push({
1160
+ binding,
1161
+ arrayNode: arrayExpression,
1162
+ replacementName,
1163
+ used: false
1164
+ });
1165
+ },
1166
+ VariableDeclarator(path) {
1167
+ if (!t4.isIdentifier(path.node.id)) return;
1168
+ const init = path.node.init;
1169
+ if (!init || !t4.isFunctionExpression(init) && !t4.isArrowFunctionExpression(init)) {
1170
+ return;
1171
+ }
1172
+ const arrayExpression = getArrayReturnExpression(init);
1173
+ if (!arrayExpression || !isSafeArrayExpression(arrayExpression)) return;
1174
+ const binding = path.scope.getBinding(path.node.id.name);
1175
+ if (!binding || !binding.constant || binding.kind !== "const") return;
1176
+ const programScope = path.scope.getProgramParent();
1177
+ let replacementName = `${path.node.id.name}_${counter}`;
1178
+ counter += 1;
1179
+ while (programScope.hasBinding(replacementName)) {
1180
+ replacementName = `${path.node.id.name}_${counter}`;
1181
+ counter += 1;
1182
+ }
1183
+ results.push({
1184
+ binding,
1185
+ arrayNode: arrayExpression,
1186
+ replacementName,
1187
+ used: false
1188
+ });
1189
+ }
1190
+ });
1191
+ return results;
1192
+ };
1193
+ var insertArrayReturnDeclarations = (ast, functions) => {
1194
+ const declarations = functions.filter((info) => info.used).map(
1195
+ (info) => t4.variableDeclaration("var", [
1196
+ t4.variableDeclarator(
1197
+ t4.identifier(info.replacementName),
1198
+ t4.cloneNode(info.arrayNode, true)
1199
+ )
1200
+ ])
1201
+ );
1202
+ if (declarations.length === 0) return;
1203
+ const body = ast.program.body;
1204
+ let insertIndex = 0;
1205
+ while (insertIndex < body.length && t4.isExpressionStatement(body[insertIndex]) && "directive" in (body[insertIndex] || {})) {
1206
+ insertIndex += 1;
1207
+ }
1208
+ body.splice(insertIndex, 0, ...declarations);
1209
+ };
1108
1210
  var preEvaluate = (code, filename) => {
1109
1211
  const ast = parse6(code, createParseOptions(filename));
1110
1212
  const state = {
@@ -1112,7 +1214,23 @@ var preEvaluate = (code, filename) => {
1112
1214
  bindingStack: /* @__PURE__ */ new Set()
1113
1215
  };
1114
1216
  let replacedCount = 0;
1217
+ const arrayReturnFunctions = collectArrayReturnFunctions(ast);
1218
+ const arrayReturnMap = /* @__PURE__ */ new Map();
1219
+ for (const info of arrayReturnFunctions) {
1220
+ arrayReturnMap.set(info.binding, info);
1221
+ }
1115
1222
  patchDefault(traverse5)(ast, {
1223
+ CallExpression(path) {
1224
+ if (!t4.isIdentifier(path.node.callee)) return;
1225
+ if (path.node.arguments.length !== 0) return;
1226
+ const binding = path.scope.getBinding(path.node.callee.name);
1227
+ if (!binding) return;
1228
+ const info = arrayReturnMap.get(binding);
1229
+ if (!info) return;
1230
+ path.replaceWith(t4.identifier(info.replacementName));
1231
+ info.used = true;
1232
+ replacedCount += 1;
1233
+ },
1116
1234
  ReferencedIdentifier(path) {
1117
1235
  if (shouldSkipReferencedIdentifier(path)) {
1118
1236
  return;
@@ -1163,13 +1281,14 @@ var preEvaluate = (code, filename) => {
1163
1281
  }
1164
1282
  }
1165
1283
  });
1284
+ insertArrayReturnDeclarations(ast, arrayReturnFunctions);
1166
1285
  return {
1167
1286
  code: patchDefault(generate5)(ast).code,
1168
1287
  replacedCount
1169
1288
  };
1170
1289
  };
1171
- var pre_evaluate_default = createCommand((program2) => {
1172
- program2.command("pre-evaluate").description("Pre-evaluate const numeric/string expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1290
+ var pre_evaluate_default = createCommand((program3) => {
1291
+ program3.command("pre-evaluate").description("Pre-evaluate const numeric/string expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1173
1292
  async (fileArgument, options) => {
1174
1293
  await timeout(
1175
1294
  async ({ finish }) => {
@@ -1262,8 +1381,8 @@ var removeUpdaters = (code, filename) => {
1262
1381
  replacedCount
1263
1382
  };
1264
1383
  };
1265
- var remove_updater_default = createCommand((program2) => {
1266
- program2.command("remove-updater").description("Replace safe update expressions with += or -=").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1384
+ var remove_updater_default = createCommand((program3) => {
1385
+ program3.command("remove-updater").description("Replace safe update expressions with += or -=").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1267
1386
  async (fileArgument, options) => {
1268
1387
  await timeout(
1269
1388
  async ({ finish }) => {
@@ -1505,8 +1624,8 @@ var removeReassign = (code, filename) => {
1505
1624
  wrapperReplacedCount
1506
1625
  };
1507
1626
  };
1508
- var remove_reassign_default = createCommand((program2) => {
1509
- program2.command("remove-reassign").description("Inline safe alias assignments and wrapper calls").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1627
+ var remove_reassign_default = createCommand((program3) => {
1628
+ program3.command("remove-reassign").description("Inline safe alias assignments and wrapper calls").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1510
1629
  async (fileArgument, options) => {
1511
1630
  await timeout(
1512
1631
  async ({ finish }) => {
@@ -1611,8 +1730,8 @@ var removeUnusedVariables = (code, filename) => {
1611
1730
  changed
1612
1731
  };
1613
1732
  };
1614
- var remove_unused_default = createCommand((program2) => {
1615
- program2.command("remove-unused").description("Remove unused variables and declarations").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1733
+ var remove_unused_default = createCommand((program3) => {
1734
+ program3.command("remove-unused").description("Remove unused variables and declarations").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1616
1735
  async (fileArgument, options) => {
1617
1736
  await timeout(
1618
1737
  async ({ finish }) => {
@@ -1682,11 +1801,15 @@ var isProxyFunctionExpression = (node) => {
1682
1801
  return t8.isFunction(node) && node.params.every((param) => t8.isIdentifier(param)) && (t8.isBlockStatement(node.body) && node.body.body.length === 1 && t8.isReturnStatement(node.body.body[0]) && (node.body.body[0].argument === void 0 || t8.isExpression(node.body.body[0].argument) && isProxyValue(node.body.body[0].argument)) || t8.isArrowFunctionExpression(node) && t8.isExpression(node.body) && isProxyValue(node.body));
1683
1802
  };
1684
1803
  var isProxyValue = (node) => {
1685
- if (t8.isFunction(node) || t8.isBlockStatement(node) || t8.isSequenceExpression(node)) {
1804
+ if (t8.isFunction(node) || t8.isBlockStatement(node) || t8.isSequenceExpression(node) || t8.isAssignmentExpression(node)) {
1686
1805
  return false;
1687
1806
  }
1688
1807
  let isValid = true;
1689
- walk(node, {
1808
+ if (!t8.isExpression(node)) {
1809
+ return false;
1810
+ }
1811
+ const wrapper = t8.file(t8.program([t8.expressionStatement(node)]));
1812
+ walk(wrapper, {
1690
1813
  "SequenceExpression|BlockStatement|Function|AssignmentExpression"(path) {
1691
1814
  isValid = false;
1692
1815
  path.stop();
@@ -1716,9 +1839,31 @@ var ProxyFunction = class {
1716
1839
  ])
1717
1840
  );
1718
1841
  const pathsToReplace = [];
1719
- walk(expression, {
1842
+ const shouldSkipIdentifier = (path) => {
1843
+ const parent = path.parentPath;
1844
+ if (!parent) return false;
1845
+ if (parent.isMemberExpression() || parent.isOptionalMemberExpression()) {
1846
+ if (path.key === "property" && !parent.node.computed) {
1847
+ return true;
1848
+ }
1849
+ }
1850
+ if (parent.isObjectProperty()) {
1851
+ if (parent.node.shorthand) return true;
1852
+ if (parent.get("key") === path && !parent.node.computed) {
1853
+ return true;
1854
+ }
1855
+ }
1856
+ if (parent.isObjectMethod()) {
1857
+ if (parent.get("key") === path && !parent.node.computed) {
1858
+ return true;
1859
+ }
1860
+ }
1861
+ return false;
1862
+ };
1863
+ const wrapper = t8.file(t8.program([t8.expressionStatement(expression)]));
1864
+ walk(wrapper, {
1720
1865
  enter(path) {
1721
- if (t8.isIdentifier(path.node) && !(path.parentPath?.isMemberExpression() && path.key === "property") && paramMap.has(path.node.name)) {
1866
+ if (t8.isIdentifier(path.node) && !shouldSkipIdentifier(path) && paramMap.has(path.node.name)) {
1722
1867
  const replacement = paramMap.get(path.node.name);
1723
1868
  pathsToReplace.push([path, replacement]);
1724
1869
  }
@@ -1794,8 +1939,8 @@ var inlineProxyFunctions = (code, filename) => {
1794
1939
  replacedCount
1795
1940
  };
1796
1941
  };
1797
- var fn_inliner_default = createCommand((program2) => {
1798
- program2.command("fn-inliner").description("Inline proxy function calls into expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1942
+ var fn_inliner_default = createCommand((program3) => {
1943
+ program3.command("fn-inliner").description("Inline proxy function calls into expressions").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1799
1944
  async (fileArgument, options) => {
1800
1945
  await timeout(
1801
1946
  async ({ finish }) => {
@@ -1977,8 +2122,8 @@ var sequenceSplit = (code, filename) => {
1977
2122
  changedCount
1978
2123
  };
1979
2124
  };
1980
- var sequence_split_default = createCommand((program2) => {
1981
- program2.command("sequence-split").description("Split sequence expressions into statements").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
2125
+ var sequence_split_default = createCommand((program3) => {
2126
+ program3.command("sequence-split").description("Split sequence expressions into statements").argument("[file]", "The file to transform").option("--input, --file <file>", "The file to transform").option("--o, --output <file>", "Output file path").option("--unlimited", "Unlimited timeout").action(
1982
2127
  async (fileArgument, options) => {
1983
2128
  await timeout(
1984
2129
  async ({ finish }) => {
@@ -2078,13 +2223,21 @@ ${calmGradienrain(`Expose Kit v${VERSION}`)}
2078
2223
 
2079
2224
  // index.ts
2080
2225
  import { readFileSync as readFileSync12 } from "fs";
2226
+ import updateNotifier from "update-notifier";
2081
2227
  var __filename = fileURLToPath(import.meta.url);
2082
2228
  var __dirname = dirname11(__filename);
2083
2229
  var pkg = JSON.parse(readFileSync12(join11(__dirname, "package.json"), "utf8"));
2230
+ var notifier = updateNotifier({
2231
+ pkg,
2232
+ updateCheckInterval: 0
2233
+ });
2234
+ if (notifier.update) {
2235
+ console.log(`${chalk4.yellow("Update available:")} ${notifier.update.latest}`);
2236
+ }
2084
2237
  console.log(showCredit(pkg.version));
2085
2238
  console.log();
2086
- var program = new Command();
2087
- program.name("expose").description("CLI for Deobfuscating").version(
2239
+ var program2 = new Command();
2240
+ program2.name("expose").description("CLI for Deobfuscating").version(
2088
2241
  chalk4.bold("It's written above, lol"),
2089
2242
  "-v, --version",
2090
2243
  "display version number"
@@ -2103,6 +2256,6 @@ var commands = [
2103
2256
  sequence_split_default
2104
2257
  ];
2105
2258
  for (const command of commands) {
2106
- command(program);
2259
+ command(program2);
2107
2260
  }
2108
- program.parse();
2261
+ program2.parse();
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",
@@ -42,9 +42,11 @@
42
42
  "@babel/types": "^7.28.5",
43
43
  "@types/babel__generator": "^7.27.0",
44
44
  "@types/babel__traverse": "^7.28.0",
45
+ "@types/update-notifier": "^6.0.8",
45
46
  "chalk": "^5.6.2",
46
47
  "commander": "^14.0.2",
47
- "loading-cli": "^1.1.2"
48
+ "loading-cli": "^1.1.2",
49
+ "update-notifier": "^7.3.1"
48
50
  },
49
51
  "bin": {
50
52
  "expose": "dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expose-kit",
3
- "version": "0.10.0",
3
+ "version": "0.10.2",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "author": "EdamAmex <edame8080@gmail.com> (https://github.com/EdamAme-x)",
@@ -42,9 +42,11 @@
42
42
  "@babel/types": "^7.28.5",
43
43
  "@types/babel__generator": "^7.27.0",
44
44
  "@types/babel__traverse": "^7.28.0",
45
+ "@types/update-notifier": "^6.0.8",
45
46
  "chalk": "^5.6.2",
46
47
  "commander": "^14.0.2",
47
- "loading-cli": "^1.1.2"
48
+ "loading-cli": "^1.1.2",
49
+ "update-notifier": "^7.3.1"
48
50
  },
49
51
  "bin": {
50
52
  "expose": "dist/index.js",