@dacely/toildefender 0.1.0 → 0.1.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/LICENSE +661 -661
- package/NOTICE.md +16 -16
- package/README.md +467 -380
- package/cli.js +168 -168
- package/defendjs.js +6 -6
- package/docs/all-modes-output.demo.js +673 -673
- package/estest.js +49 -49
- package/esutils.js +107 -107
- package/logger.js +28 -28
- package/obfuscator.js +534 -534
- package/package.json +108 -108
- package/processors/deadCode.js +62 -62
- package/processors/flattener.js +808 -808
- package/processors/health.js +55 -55
- package/processors/identifiers.js +256 -256
- package/processors/literalObfuscator.js +40 -40
- package/processors/literals.js +233 -233
- package/processors/methods.js +332 -332
- package/processors/modules.js +231 -231
- package/processors/normalizer.js +490 -490
- package/processors/numericVm.js +950 -950
- package/processors/postprocessing.js +69 -69
- package/processors/preprocessing.js +248 -248
- package/processors/scopes.js +177 -177
- package/processors/uglifier.js +25 -25
- package/processors/variables.js +185 -185
- package/toildefender.js +7 -7
- package/traverser.js +115 -115
- package/utils.js +135 -135
package/README.md
CHANGED
|
@@ -1,380 +1,467 @@
|
|
|
1
|
-
<div align="center">
|
|
2
|
-
|
|
3
|
-
<img src="./images/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
### JavaScript code protection for the Toil stack.
|
|
7
|
-
|
|
8
|
-
<sub>Randomized control flow, literal protection, object packing, BigInt-backed VM bytecode, and hash-mesh bytecode unlock for browser and Node bundles.</sub>
|
|
9
|
-
|
|
10
|
-
<br/>
|
|
11
|
-
|
|
12
|
-
[](https://www.npmjs.com/package/@dacely/toildefender)
|
|
13
|
-
[](https://nodejs.org/)
|
|
14
|
-
[](#virtual-machine-protection)
|
|
15
|
-
[](./LICENSE)
|
|
16
|
-
|
|
17
|
-
</div>
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
ToilDefender is Dacely's maintained JavaScript protection layer for the Toil
|
|
22
|
-
technology stack. It started from the original `defendjs` project, but this
|
|
23
|
-
fork is now maintained as its own package: `@dacely/toildefender`.
|
|
24
|
-
|
|
25
|
-
The goal is not to make client-side JavaScript impossible to analyze. That is
|
|
26
|
-
not a real promise. The goal is to raise reverse-engineering cost by removing
|
|
27
|
-
source-level structure, splitting logic across generated helpers, packing
|
|
28
|
-
constants, and optionally compiling selected functions into randomized numeric
|
|
29
|
-
VM programs.
|
|
30
|
-
|
|
31
|
-
```bash
|
|
32
|
-
npm install @dacely/toildefender
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
```js
|
|
36
|
-
const toildefender = require("@dacely/toildefender");
|
|
37
|
-
|
|
38
|
-
const result = toildefender.do({
|
|
39
|
-
code: source,
|
|
40
|
-
modulesCode: {},
|
|
41
|
-
logLevel: "error",
|
|
42
|
-
features: {
|
|
43
|
-
dead_code: true,
|
|
44
|
-
scope: true,
|
|
45
|
-
control_flow: true,
|
|
46
|
-
identifiers: true,
|
|
47
|
-
numeric_vm: true,
|
|
48
|
-
object_packing: true,
|
|
49
|
-
literals: true,
|
|
50
|
-
mangle: true,
|
|
51
|
-
compress: true
|
|
52
|
-
},
|
|
53
|
-
protections: {
|
|
54
|
-
virtualMachine: {
|
|
55
|
-
enabled: true,
|
|
56
|
-
mode: "aggressive",
|
|
57
|
-
bigintBytecode: true,
|
|
58
|
-
randomizedOpcodes: true,
|
|
59
|
-
encodeConstants: true,
|
|
60
|
-
perFunctionDialect: true,
|
|
61
|
-
virtualize: "heuristic"
|
|
62
|
-
},
|
|
63
|
-
hashMesh: {
|
|
64
|
-
enabled: true,
|
|
65
|
-
mode: "aggressive",
|
|
66
|
-
unlock: "per-function",
|
|
67
|
-
deriveDialectFromMesh: true,
|
|
68
|
-
bindToVmState: true,
|
|
69
|
-
encodeChaff: true,
|
|
70
|
-
chaffRatio: 0.55
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
console.log(result.code);
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## What It Does
|
|
79
|
-
|
|
80
|
-
| Protection | Purpose |
|
|
81
|
-
| --- | --- |
|
|
82
|
-
| `control_flow` | Rewrites structured control flow into dispatcher-style execution. |
|
|
83
|
-
| `scope` | Flattens function/scope structure into generated runtime frames. |
|
|
84
|
-
| `identifiers` | Renames and rewrites identifiers, object references, and property access. |
|
|
85
|
-
| `object_packing` | Packs object literal keys into numeric schemas instead of readable key/value arrays. |
|
|
86
|
-
| `literals` | Encodes strings and numeric constants. |
|
|
87
|
-
| `dead_code` | Inserts unreachable or low-value code paths to add noise. |
|
|
88
|
-
| `mangle` | Shortens generated identifiers. |
|
|
89
|
-
| `compress` | Emits compact output. |
|
|
90
|
-
| `numeric_vm` | Virtualizes supported functions into BigInt-packed bytecode. |
|
|
91
|
-
|
|
92
|
-
## Virtual Machine Protection
|
|
93
|
-
|
|
94
|
-
Transform your JavaScript into randomized virtual-machine bytecode for maximum
|
|
95
|
-
resistance against reverse engineering.
|
|
96
|
-
|
|
97
|
-
ToilDefender compiles protected functions into a private instruction set, packs
|
|
98
|
-
the bytecode into encrypted BigInt streams, and executes it through a generated
|
|
99
|
-
runtime VM. Instead of exposing readable JavaScript logic, your code becomes
|
|
100
|
-
numeric program data consumed by a randomized virtual machine.
|
|
101
|
-
|
|
102
|
-
Original logic disappears from the output bundle. Attackers no longer reverse
|
|
103
|
-
plain JavaScript; they must recover the VM, decode the bytecode format,
|
|
104
|
-
reconstruct the instruction set, and emulate the protected program.
|
|
105
|
-
|
|
106
|
-
```js
|
|
107
|
-
toildefender.do({
|
|
108
|
-
code,
|
|
109
|
-
modulesCode: {},
|
|
110
|
-
features: {
|
|
111
|
-
numeric_vm: true
|
|
112
|
-
},
|
|
113
|
-
protections: {
|
|
114
|
-
virtualMachine: {
|
|
115
|
-
enabled: true,
|
|
116
|
-
mode: "aggressive",
|
|
117
|
-
bigintBytecode: true,
|
|
118
|
-
randomizedOpcodes: true,
|
|
119
|
-
encodeConstants: true,
|
|
120
|
-
perFunctionDialect: true,
|
|
121
|
-
virtualize: "marked",
|
|
122
|
-
minFunctionSize: 1,
|
|
123
|
-
maxFunctionSize: 120,
|
|
124
|
-
seed: "build-seed"
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
Selection modes:
|
|
131
|
-
|
|
132
|
-
| `virtualize` | Meaning |
|
|
133
|
-
| --- | --- |
|
|
134
|
-
| `marked` | Virtualize functions marked by supported annotations or compiler selection. |
|
|
135
|
-
| `all-supported` | Virtualize every function that fits the supported syntax subset. |
|
|
136
|
-
| `heuristic` | Virtualize functions selected by size and compiler suitability. |
|
|
137
|
-
|
|
138
|
-
Supported VM syntax currently targets practical protection work: literals,
|
|
139
|
-
locals, arguments, return, assignment, arithmetic, comparisons, logical
|
|
140
|
-
expressions, `if` / `else`, `while`, calls, member reads, arrays, and object
|
|
141
|
-
literals. Unsupported syntax remains native or is skipped by selection.
|
|
142
|
-
|
|
143
|
-
### All-Modes Output Demo
|
|
144
|
-
|
|
145
|
-
Input:
|
|
146
|
-
|
|
147
|
-
```js
|
|
148
|
-
function licenseGate(input) {
|
|
149
|
-
const total = input.length * 7;
|
|
150
|
-
return input.charCodeAt(0) === 86
|
|
151
|
-
? { ok: true, total: total + 13 }
|
|
152
|
-
: { ok: false, total: total - 5 };
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
globalThis.__result = licenseGate("Veilmark");
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
The demo artifact is generated with every major protection enabled:
|
|
159
|
-
|
|
160
|
-
```js
|
|
161
|
-
features: {
|
|
162
|
-
dead_code: true,
|
|
163
|
-
scope: true,
|
|
164
|
-
control_flow: true,
|
|
165
|
-
identifiers: true,
|
|
166
|
-
numeric_vm: true,
|
|
167
|
-
object_packing: true,
|
|
168
|
-
literals: true,
|
|
169
|
-
mangle: true,
|
|
170
|
-
compress: true
|
|
171
|
-
},
|
|
172
|
-
protections: {
|
|
173
|
-
virtualMachine: {
|
|
174
|
-
enabled: true,
|
|
175
|
-
mode: "aggressive",
|
|
176
|
-
bigintBytecode: true,
|
|
177
|
-
randomizedOpcodes: true,
|
|
178
|
-
encodeConstants: true,
|
|
179
|
-
perFunctionDialect: true,
|
|
180
|
-
virtualize: "all-supported",
|
|
181
|
-
seed: "readme-all-modes-demo"
|
|
182
|
-
},
|
|
183
|
-
hashMesh: {
|
|
184
|
-
enabled: true,
|
|
185
|
-
mode: "aggressive",
|
|
186
|
-
unlock: "per-function",
|
|
187
|
-
deriveDialectFromMesh: true,
|
|
188
|
-
bindToVmState: true,
|
|
189
|
-
encodeChaff: true,
|
|
190
|
-
chaffRatio: 0.55
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
The complete beautified generated output is committed at
|
|
196
|
-
[docs/all-modes-output.demo.js](./docs/all-modes-output.demo.js). It is a real
|
|
197
|
-
673-line artifact from the current generator and executes to:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
##
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<img src="./images/toildefender4.svg" alt="ToilDefender" width="600" />
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### JavaScript code protection for the Toil stack.
|
|
7
|
+
|
|
8
|
+
<sub>Randomized control flow, literal protection, object packing, BigInt-backed VM bytecode, and hash-mesh bytecode unlock for browser and Node bundles.</sub>
|
|
9
|
+
|
|
10
|
+
<br/>
|
|
11
|
+
|
|
12
|
+
[](https://www.npmjs.com/package/@dacely/toildefender)
|
|
13
|
+
[](https://nodejs.org/)
|
|
14
|
+
[](#virtual-machine-protection)
|
|
15
|
+
[](./LICENSE)
|
|
16
|
+
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
ToilDefender is Dacely's maintained JavaScript protection layer for the Toil
|
|
22
|
+
technology stack. It started from the original `defendjs` project, but this
|
|
23
|
+
fork is now maintained as its own package: `@dacely/toildefender`.
|
|
24
|
+
|
|
25
|
+
The goal is not to make client-side JavaScript impossible to analyze. That is
|
|
26
|
+
not a real promise. The goal is to raise reverse-engineering cost by removing
|
|
27
|
+
source-level structure, splitting logic across generated helpers, packing
|
|
28
|
+
constants, and optionally compiling selected functions into randomized numeric
|
|
29
|
+
VM programs.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @dacely/toildefender
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
const toildefender = require("@dacely/toildefender");
|
|
37
|
+
|
|
38
|
+
const result = toildefender.do({
|
|
39
|
+
code: source,
|
|
40
|
+
modulesCode: {},
|
|
41
|
+
logLevel: "error",
|
|
42
|
+
features: {
|
|
43
|
+
dead_code: true,
|
|
44
|
+
scope: true,
|
|
45
|
+
control_flow: true,
|
|
46
|
+
identifiers: true,
|
|
47
|
+
numeric_vm: true,
|
|
48
|
+
object_packing: true,
|
|
49
|
+
literals: true,
|
|
50
|
+
mangle: true,
|
|
51
|
+
compress: true
|
|
52
|
+
},
|
|
53
|
+
protections: {
|
|
54
|
+
virtualMachine: {
|
|
55
|
+
enabled: true,
|
|
56
|
+
mode: "aggressive",
|
|
57
|
+
bigintBytecode: true,
|
|
58
|
+
randomizedOpcodes: true,
|
|
59
|
+
encodeConstants: true,
|
|
60
|
+
perFunctionDialect: true,
|
|
61
|
+
virtualize: "heuristic"
|
|
62
|
+
},
|
|
63
|
+
hashMesh: {
|
|
64
|
+
enabled: true,
|
|
65
|
+
mode: "aggressive",
|
|
66
|
+
unlock: "per-function",
|
|
67
|
+
deriveDialectFromMesh: true,
|
|
68
|
+
bindToVmState: true,
|
|
69
|
+
encodeChaff: true,
|
|
70
|
+
chaffRatio: 0.55
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
console.log(result.code);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## What It Does
|
|
79
|
+
|
|
80
|
+
| Protection | Purpose |
|
|
81
|
+
| --- | --- |
|
|
82
|
+
| `control_flow` | Rewrites structured control flow into dispatcher-style execution. |
|
|
83
|
+
| `scope` | Flattens function/scope structure into generated runtime frames. |
|
|
84
|
+
| `identifiers` | Renames and rewrites identifiers, object references, and property access. |
|
|
85
|
+
| `object_packing` | Packs object literal keys into numeric schemas instead of readable key/value arrays. |
|
|
86
|
+
| `literals` | Encodes strings and numeric constants. |
|
|
87
|
+
| `dead_code` | Inserts unreachable or low-value code paths to add noise. |
|
|
88
|
+
| `mangle` | Shortens generated identifiers. |
|
|
89
|
+
| `compress` | Emits compact output. |
|
|
90
|
+
| `numeric_vm` | Virtualizes supported functions into BigInt-packed bytecode. |
|
|
91
|
+
|
|
92
|
+
## Virtual Machine Protection
|
|
93
|
+
|
|
94
|
+
Transform your JavaScript into randomized virtual-machine bytecode for maximum
|
|
95
|
+
resistance against reverse engineering.
|
|
96
|
+
|
|
97
|
+
ToilDefender compiles protected functions into a private instruction set, packs
|
|
98
|
+
the bytecode into encrypted BigInt streams, and executes it through a generated
|
|
99
|
+
runtime VM. Instead of exposing readable JavaScript logic, your code becomes
|
|
100
|
+
numeric program data consumed by a randomized virtual machine.
|
|
101
|
+
|
|
102
|
+
Original logic disappears from the output bundle. Attackers no longer reverse
|
|
103
|
+
plain JavaScript; they must recover the VM, decode the bytecode format,
|
|
104
|
+
reconstruct the instruction set, and emulate the protected program.
|
|
105
|
+
|
|
106
|
+
```js
|
|
107
|
+
toildefender.do({
|
|
108
|
+
code,
|
|
109
|
+
modulesCode: {},
|
|
110
|
+
features: {
|
|
111
|
+
numeric_vm: true
|
|
112
|
+
},
|
|
113
|
+
protections: {
|
|
114
|
+
virtualMachine: {
|
|
115
|
+
enabled: true,
|
|
116
|
+
mode: "aggressive",
|
|
117
|
+
bigintBytecode: true,
|
|
118
|
+
randomizedOpcodes: true,
|
|
119
|
+
encodeConstants: true,
|
|
120
|
+
perFunctionDialect: true,
|
|
121
|
+
virtualize: "marked",
|
|
122
|
+
minFunctionSize: 1,
|
|
123
|
+
maxFunctionSize: 120,
|
|
124
|
+
seed: "build-seed"
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Selection modes:
|
|
131
|
+
|
|
132
|
+
| `virtualize` | Meaning |
|
|
133
|
+
| --- | --- |
|
|
134
|
+
| `marked` | Virtualize functions marked by supported annotations or compiler selection. |
|
|
135
|
+
| `all-supported` | Virtualize every function that fits the supported syntax subset. |
|
|
136
|
+
| `heuristic` | Virtualize functions selected by size and compiler suitability. |
|
|
137
|
+
|
|
138
|
+
Supported VM syntax currently targets practical protection work: literals,
|
|
139
|
+
locals, arguments, return, assignment, arithmetic, comparisons, logical
|
|
140
|
+
expressions, `if` / `else`, `while`, calls, member reads, arrays, and object
|
|
141
|
+
literals. Unsupported syntax remains native or is skipped by selection.
|
|
142
|
+
|
|
143
|
+
### All-Modes Output Demo
|
|
144
|
+
|
|
145
|
+
Input:
|
|
146
|
+
|
|
147
|
+
```js
|
|
148
|
+
function licenseGate(input) {
|
|
149
|
+
const total = input.length * 7;
|
|
150
|
+
return input.charCodeAt(0) === 86
|
|
151
|
+
? { ok: true, total: total + 13 }
|
|
152
|
+
: { ok: false, total: total - 5 };
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
globalThis.__result = licenseGate("Veilmark");
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The demo artifact is generated with every major protection enabled:
|
|
159
|
+
|
|
160
|
+
```js
|
|
161
|
+
features: {
|
|
162
|
+
dead_code: true,
|
|
163
|
+
scope: true,
|
|
164
|
+
control_flow: true,
|
|
165
|
+
identifiers: true,
|
|
166
|
+
numeric_vm: true,
|
|
167
|
+
object_packing: true,
|
|
168
|
+
literals: true,
|
|
169
|
+
mangle: true,
|
|
170
|
+
compress: true
|
|
171
|
+
},
|
|
172
|
+
protections: {
|
|
173
|
+
virtualMachine: {
|
|
174
|
+
enabled: true,
|
|
175
|
+
mode: "aggressive",
|
|
176
|
+
bigintBytecode: true,
|
|
177
|
+
randomizedOpcodes: true,
|
|
178
|
+
encodeConstants: true,
|
|
179
|
+
perFunctionDialect: true,
|
|
180
|
+
virtualize: "all-supported",
|
|
181
|
+
seed: "readme-all-modes-demo"
|
|
182
|
+
},
|
|
183
|
+
hashMesh: {
|
|
184
|
+
enabled: true,
|
|
185
|
+
mode: "aggressive",
|
|
186
|
+
unlock: "per-function",
|
|
187
|
+
deriveDialectFromMesh: true,
|
|
188
|
+
bindToVmState: true,
|
|
189
|
+
encodeChaff: true,
|
|
190
|
+
chaffRatio: 0.55
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The complete beautified generated output is committed at
|
|
196
|
+
[docs/all-modes-output.demo.js](./docs/all-modes-output.demo.js). It is a real
|
|
197
|
+
673-line artifact from the current generator and executes to:
|
|
198
|
+
|
|
199
|
+
Output excerpt:
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
(function () {
|
|
203
|
+
function a(f, j) {
|
|
204
|
+
var b = new Array(96);
|
|
205
|
+
;
|
|
206
|
+
var c = arguments;
|
|
207
|
+
while (true) try {
|
|
208
|
+
switch (f) {
|
|
209
|
+
case 25271:
|
|
210
|
+
b[11] = c[11];
|
|
211
|
+
b[12] = c[10];
|
|
212
|
+
b[13] = c[9];
|
|
213
|
+
b[14] = c[8];
|
|
214
|
+
b[15] = c[7];
|
|
215
|
+
b[16] = c[6];
|
|
216
|
+
b[17] = c[5];
|
|
217
|
+
b[18] = c[4];
|
|
218
|
+
b[19] = c[3];
|
|
219
|
+
b[20] = c[2];
|
|
220
|
+
b[21] = e(a, 26903, b, c[1]);
|
|
221
|
+
b[22] = e(a, 13834, b, c[1]);
|
|
222
|
+
b[23] = e(a, 24871, b, c[1]);
|
|
223
|
+
b[24] = e(a, 3126, b, c[1]);
|
|
224
|
+
b[25] = e(a, 24897, b, c[1]);
|
|
225
|
+
b[26] = e(a, 32340, b, c[1]);
|
|
226
|
+
if (b[11]) {
|
|
227
|
+
b[20] = c[1][2](b[20], b[19], b[18], b[17], b[16], b[12], b[11]);
|
|
228
|
+
}
|
|
229
|
+
b[27] = [];
|
|
230
|
+
b[28] = b[17] >>> c[1][9][1];
|
|
231
|
+
b[29] = b[17] >>> c[1][9][1];
|
|
232
|
+
b[30] = BigInt(b[19]);
|
|
233
|
+
b[31] = c[1][9][1];
|
|
234
|
+
while (b[31] < b[18]) {
|
|
235
|
+
b[32] = Number(b[20] % b[30]);
|
|
236
|
+
b[20] = b[20] / b[30];
|
|
237
|
+
b[33] = c[1][9][5] + (b[28] >>> c[1][9][24]) % (b[19] - c[1][9][5]);
|
|
238
|
+
b[34] = b[28] % b[19];
|
|
239
|
+
b[35] = (b[32] - b[34] + b[19]) % b[19] * b[26](b[33], b[19]) % b[19];
|
|
240
|
+
b[27][c[1][9][29]](b[35]);
|
|
241
|
+
b[29] = b[25](b[29], b[32], b[31]);
|
|
242
|
+
b[28] = b[25](b[28], b[32], b[31]);
|
|
243
|
+
b[31] += c[1][9][5];
|
|
244
|
+
}
|
|
245
|
+
if (b[29] >>> c[1][9][1] !== b[16] >>> c[1][9][1]) throw new Error(c[1][9][30]);
|
|
246
|
+
b[36] = [];
|
|
247
|
+
b[37] = [];
|
|
248
|
+
b[38] = Array[c[1][9][31]][c[1][9][32]][c[1][9][33]](b[14]);
|
|
249
|
+
b[39] = c[1][9][1];
|
|
250
|
+
while (c[1][9][38]) {
|
|
251
|
+
b[40] = b[24]();
|
|
252
|
+
if (b[40] === b[12][c[1][9][1]]) continue;
|
|
253
|
+
if (b[40] === b[12][c[1][9][5]]) {
|
|
254
|
+
b[36][c[1][9][29]](undefined);
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (b[40] === b[12][c[1][9][28]]) {
|
|
258
|
+
b[36][c[1][9][29]](c[1][9][39]);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/* 600+ more generated lines:
|
|
263
|
+
dispatcher cases, encoded literals, VM bytecode decode,
|
|
264
|
+
BigInt program blobs, randomized opcode tables, and Hash-Mesh unlock */
|
|
265
|
+
|
|
266
|
+
case 11237:
|
|
267
|
+
b[1] = '';
|
|
268
|
+
b[1] += d(86, 101);
|
|
269
|
+
b[1] += d(105, 108, 109, 97);
|
|
270
|
+
b[1] += d(114);
|
|
271
|
+
b[1] += d(107);
|
|
272
|
+
return b[1];
|
|
273
|
+
}
|
|
274
|
+
} catch (a) {
|
|
275
|
+
veilmark$tobethrown = null;
|
|
276
|
+
switch (f) {
|
|
277
|
+
default:
|
|
278
|
+
throw a;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
a(4089, {});
|
|
283
|
+
})();
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
```json
|
|
287
|
+
{ "ok": true, "total": 69 }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
That output contains the full stacked mess: flattened dispatcher runtime,
|
|
291
|
+
identifier rewriting, packed literals, object packing, VM bytecode execution,
|
|
292
|
+
BigInt program blobs, randomized opcode tables, and Hash-Mesh unlock material.
|
|
293
|
+
|
|
294
|
+
## Hash-Mesh Unlock
|
|
295
|
+
|
|
296
|
+
Hash-Mesh Unlock derives VM bytecode keys from runtime integrity data. If
|
|
297
|
+
protected code, constants, VM helpers, or execution state are modified, the next
|
|
298
|
+
bytecode chunk decrypts incorrectly instead of exposing runnable logic.
|
|
299
|
+
|
|
300
|
+
This turns integrity checks into decryption requirements instead of patchable
|
|
301
|
+
boolean branches.
|
|
302
|
+
|
|
303
|
+
```js
|
|
304
|
+
toildefender.do({
|
|
305
|
+
code,
|
|
306
|
+
modulesCode: {},
|
|
307
|
+
features: {
|
|
308
|
+
numeric_vm: true
|
|
309
|
+
},
|
|
310
|
+
protections: {
|
|
311
|
+
virtualMachine: {
|
|
312
|
+
enabled: true,
|
|
313
|
+
mode: "aggressive",
|
|
314
|
+
virtualize: "all-supported"
|
|
315
|
+
},
|
|
316
|
+
hashMesh: {
|
|
317
|
+
enabled: true,
|
|
318
|
+
mode: "aggressive",
|
|
319
|
+
unlock: "per-function",
|
|
320
|
+
deriveDialectFromMesh: true,
|
|
321
|
+
bindToVmState: true,
|
|
322
|
+
encodeChaff: true,
|
|
323
|
+
chaffRatio: 0.55,
|
|
324
|
+
serverBound: false
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Hash-Mesh is an obfuscation and tamper-resistance layer. It is not a
|
|
331
|
+
cryptographic secrecy guarantee for code running on an attacker-controlled
|
|
332
|
+
machine.
|
|
333
|
+
|
|
334
|
+
## CLI
|
|
335
|
+
|
|
336
|
+
Install globally or run through `npx`:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
npm install -g @dacely/toildefender
|
|
340
|
+
toildefender --help
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
toildefender \
|
|
345
|
+
--input ./src \
|
|
346
|
+
--output ./dist-protected \
|
|
347
|
+
--features scope,control_flow,identifiers,literals,mangle,compress
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
For multi-entry projects, declare entry files in `package.json`:
|
|
351
|
+
|
|
352
|
+
```json
|
|
353
|
+
{
|
|
354
|
+
"toildefender": {
|
|
355
|
+
"mainFiles": ["index.js", "worker.js"]
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
The old `defendjs.mainFiles` field is still read as a compatibility fallback,
|
|
361
|
+
but new projects should use `toildefender`.
|
|
362
|
+
|
|
363
|
+
## API
|
|
364
|
+
|
|
365
|
+
```js
|
|
366
|
+
const toildefender = require("@dacely/toildefender");
|
|
367
|
+
|
|
368
|
+
const result = toildefender.do({
|
|
369
|
+
code: "function add(a,b){ return a + b } globalThis.x = add(1,2)",
|
|
370
|
+
modulesCode: {},
|
|
371
|
+
logLevel: "warn",
|
|
372
|
+
features: {
|
|
373
|
+
dead_code: false,
|
|
374
|
+
scope: true,
|
|
375
|
+
control_flow: true,
|
|
376
|
+
identifiers: true,
|
|
377
|
+
numeric_vm: false,
|
|
378
|
+
object_packing: true,
|
|
379
|
+
literals: true,
|
|
380
|
+
mangle: true,
|
|
381
|
+
compress: true
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
console.log(result.code);
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
Main options:
|
|
389
|
+
|
|
390
|
+
| Option | Meaning |
|
|
391
|
+
| --- | --- |
|
|
392
|
+
| `code` | Entry source code. |
|
|
393
|
+
| `modulesCode` | Map of dependency filename to source code. |
|
|
394
|
+
| `features` | Feature switches for the classic pipeline. |
|
|
395
|
+
| `protections.virtualMachine` | User-facing VM bytecode backend configuration. |
|
|
396
|
+
| `protections.hashMesh` | User-facing hash-mesh unlock configuration. |
|
|
397
|
+
| `numericVm` | Lower-level numeric VM configuration retained for internal callers. |
|
|
398
|
+
| `preprocessorVariables` | Compile-time preprocessor constants. |
|
|
399
|
+
| `logLevel` | `error`, `warn`, `info`, `debug`, or `log`. |
|
|
400
|
+
|
|
401
|
+
## Toil Integration
|
|
402
|
+
|
|
403
|
+
ToilDefender is intended to sit behind Toil build tooling. Framework packages
|
|
404
|
+
can call the API directly, then run normal syntax validation and browser tests
|
|
405
|
+
against the protected artifact.
|
|
406
|
+
|
|
407
|
+
Recommended Toil stack pattern:
|
|
408
|
+
|
|
409
|
+
```txt
|
|
410
|
+
source bundle
|
|
411
|
+
-> Vite / framework build
|
|
412
|
+
-> ToilDefender pre-obfuscation and protection
|
|
413
|
+
-> syntax validation
|
|
414
|
+
-> browser smoke tests
|
|
415
|
+
-> publish/deploy artifact
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
For security-sensitive browser code, pair this with server-side validation.
|
|
419
|
+
Client-side protection raises cost; it does not replace server authority.
|
|
420
|
+
|
|
421
|
+
## Security Boundary
|
|
422
|
+
|
|
423
|
+
ToilDefender is code protection, not magic.
|
|
424
|
+
|
|
425
|
+
It helps against:
|
|
426
|
+
|
|
427
|
+
- quick static reading of shipped JavaScript
|
|
428
|
+
- simple string/signature extraction
|
|
429
|
+
- source-level control-flow recovery
|
|
430
|
+
- direct patching of obvious boolean integrity checks
|
|
431
|
+
- automated diffing across builds when seeds and dialects rotate
|
|
432
|
+
|
|
433
|
+
It does not guarantee:
|
|
434
|
+
|
|
435
|
+
- secrets stay secret in client-side code
|
|
436
|
+
- runtime tracing is impossible
|
|
437
|
+
- browser-controlled attackers cannot eventually emulate behavior
|
|
438
|
+
- server authorization can be moved into the browser
|
|
439
|
+
|
|
440
|
+
Put real authorization and durable decisions on the server.
|
|
441
|
+
|
|
442
|
+
## Development
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
npm test
|
|
446
|
+
npm run test:firefox
|
|
447
|
+
npm run pack:dry
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
The regression suite covers modern syntax handling, object packing, VM bytecode
|
|
451
|
+
execution, Hash-Mesh unlock, and tamper failure behavior.
|
|
452
|
+
|
|
453
|
+
## Credit
|
|
454
|
+
|
|
455
|
+
ToilDefender began as a fork of
|
|
456
|
+
[defendjs](https://github.com/alexhorn/defendjs), originally created by
|
|
457
|
+
Alexander Horn and released under the GNU Affero General Public License v3.0.
|
|
458
|
+
|
|
459
|
+
Dacely maintains this fork for the Toil stack and has added the modern parser
|
|
460
|
+
surface, VM bytecode backend, Hash-Mesh unlock layer, object key packing,
|
|
461
|
+
branding cleanup, and current regression coverage.
|
|
462
|
+
|
|
463
|
+
See [NOTICE.md](./NOTICE.md) for attribution details.
|
|
464
|
+
|
|
465
|
+
## License
|
|
466
|
+
|
|
467
|
+
AGPL-3.0. See [LICENSE](./LICENSE).
|