javascript-obfuscator 5.5.0 → 5.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/CLAUDE.md +19 -0
- package/README.md +269 -24
- package/dist/index.browser.js +2 -2
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cli.js +1 -1
- package/dist/index.cli.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
- package/typings/src/code-transformers/preparing-transformers/HashbangOperatorTransformer.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
Change Log
|
|
2
2
|
|
|
3
|
+
v5.6.0
|
|
4
|
+
---
|
|
5
|
+
* Pro API: `obfuscatePro` now fall back to the basic local obfuscation API when no Pro feature (`vmObfuscation` or `parseHtml`) is enabled, instead of throwing an `ApiError`
|
|
6
|
+
* Improved `stringArrayEncoding` runtime performance
|
|
7
|
+
|
|
8
|
+
v5.5.1
|
|
9
|
+
---
|
|
10
|
+
* Fixed leading whitespace of the source code being stripped without being reflected in the source map, shifting all `sourcesContent` and original position mappings. Fixes https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1437
|
|
11
|
+
|
|
3
12
|
v5.5.0
|
|
4
13
|
---
|
|
5
14
|
* Pro API: reworked large file uploads — fixed `413 Content Too Large` for ~4.4–4.6MB request bodies, and Blob uploads now send the raw source (`blobFormat: 'raw'`) instead of the JSON request body, so uploads always fit the plan's file size cap
|
package/CLAUDE.md
CHANGED
|
@@ -475,15 +475,23 @@ The project includes a browser build at `dist/index.browser.js` that can be used
|
|
|
475
475
|
```bash
|
|
476
476
|
# Production build
|
|
477
477
|
npm run build
|
|
478
|
+
# or
|
|
479
|
+
yarn run build
|
|
478
480
|
|
|
479
481
|
# Development watch mode
|
|
480
482
|
npm run watch
|
|
483
|
+
# or
|
|
484
|
+
yarn run watch
|
|
481
485
|
|
|
482
486
|
# Build TypeScript typings
|
|
483
487
|
npm run build:typings
|
|
488
|
+
# or
|
|
489
|
+
yarn run build:typings
|
|
484
490
|
|
|
485
491
|
# Linting
|
|
486
492
|
npm run eslint
|
|
493
|
+
# or
|
|
494
|
+
yarn run eslint
|
|
487
495
|
```
|
|
488
496
|
|
|
489
497
|
## Testing
|
|
@@ -1184,24 +1192,32 @@ The project uses **Husky** for git hooks:
|
|
|
1184
1192
|
1. **Use watch mode during development**:
|
|
1185
1193
|
```bash
|
|
1186
1194
|
npm run watch
|
|
1195
|
+
# or
|
|
1196
|
+
yarn run watch
|
|
1187
1197
|
```
|
|
1188
1198
|
This rebuilds automatically when you save files.
|
|
1189
1199
|
|
|
1190
1200
|
2. **Run specific tests during development**:
|
|
1191
1201
|
```bash
|
|
1192
1202
|
npm run test:dev
|
|
1203
|
+
# or
|
|
1204
|
+
yarn run test:dev
|
|
1193
1205
|
```
|
|
1194
1206
|
Faster than full test suite.
|
|
1195
1207
|
|
|
1196
1208
|
3. **Check linting before committing**:
|
|
1197
1209
|
```bash
|
|
1198
1210
|
npm run eslint
|
|
1211
|
+
# or
|
|
1212
|
+
yarn run eslint
|
|
1199
1213
|
```
|
|
1200
1214
|
Fix issues before the pre-commit hook runs.
|
|
1201
1215
|
|
|
1202
1216
|
4. **Test memory usage**:
|
|
1203
1217
|
```bash
|
|
1204
1218
|
npm run test:mocha-memory-performance
|
|
1219
|
+
# or
|
|
1220
|
+
yarn run test:mocha-memory-performance
|
|
1205
1221
|
```
|
|
1206
1222
|
Ensure your changes don't cause memory issues.
|
|
1207
1223
|
|
|
@@ -1209,6 +1225,9 @@ The project uses **Husky** for git hooks:
|
|
|
1209
1225
|
```bash
|
|
1210
1226
|
npm run test:mocha-coverage
|
|
1211
1227
|
npm run test:mocha-coverage:report
|
|
1228
|
+
# or
|
|
1229
|
+
yarn run test:mocha-coverage
|
|
1230
|
+
yarn run test:mocha-coverage:report
|
|
1212
1231
|
```
|
|
1213
1232
|
Check test coverage in the generated `coverage/` directory.
|
|
1214
1233
|
|
package/README.md
CHANGED
|
@@ -10,30 +10,10 @@
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
### Do you use JavaScript Obfuscator at your company?
|
|
14
|
-
|
|
15
|
-
JavaScript Obfuscator has reached over **1 million npm downloads per week**. I am currently preparing an **EB-1 immigration case** and collecting independent evidence of the project’s real-world professional usage and impact.
|
|
16
|
-
|
|
17
|
-
If you use JavaScript Obfuscator in a company project — especially at a well-known company, large organization, or widely used product — I would be very grateful if you could contact me.
|
|
18
|
-
|
|
19
|
-
Helpful evidence may include a brief confirmation or, ideally, a 1–2 page reference letter describing:
|
|
20
|
-
|
|
21
|
-
- how your team or company used JavaScript Obfuscator;
|
|
22
|
-
- why you chose it;
|
|
23
|
-
- what problem it helped solve;
|
|
24
|
-
- whether it was used in production or an important internal workflow;
|
|
25
|
-
- your role and how you are familiar with the usage.
|
|
26
|
-
|
|
27
|
-
I can provide a simple draft/template to make this easy.
|
|
28
|
-
|
|
29
|
-
Please contact me at: **referenceletter@obfuscator.io**
|
|
30
|
-
|
|
31
|
-
Thank you for supporting the project.
|
|
32
|
-
|
|
33
|
-
---
|
|
34
|
-
|
|
35
13
|
### :rocket: Obfuscator.io with VM Obfuscation
|
|
36
14
|
|
|
15
|
+
[](https://obfuscator.io)
|
|
16
|
+
|
|
37
17
|
**Obfuscator.io** adds **VM-based bytecode obfuscation** to this package - your JavaScript functions are compiled to custom bytecode that runs on an embedded virtual machine. Each build produces unique opcodes and VM structure, making reverse engineering and automated deobfuscation dramatically harder.
|
|
38
18
|
|
|
39
19
|
| Protection goal | Free (this package) | [obfuscator.io](https://obfuscator.io) |
|
|
@@ -54,6 +34,28 @@ This package provides access to Obfuscator.io API via CLI and Node.js API.
|
|
|
54
34
|
|
|
55
35
|
---
|
|
56
36
|
|
|
37
|
+
### Do you use JavaScript Obfuscator at your company?
|
|
38
|
+
|
|
39
|
+
JavaScript Obfuscator has reached over **1 million npm downloads per week**. I am currently preparing an **EB-1 immigration case** and collecting independent evidence of the project’s real-world professional usage and impact.
|
|
40
|
+
|
|
41
|
+
If you use JavaScript Obfuscator in a company project — especially at a well-known company, large organization, or widely used product — I would be very grateful if you could contact me.
|
|
42
|
+
|
|
43
|
+
Helpful evidence may include a brief confirmation or, ideally, a 1–2 page reference letter describing:
|
|
44
|
+
|
|
45
|
+
- how your team or company used JavaScript Obfuscator;
|
|
46
|
+
- why you chose it;
|
|
47
|
+
- what problem it helped solve;
|
|
48
|
+
- whether it was used in production or an important internal workflow;
|
|
49
|
+
- your role and how you are familiar with the usage.
|
|
50
|
+
|
|
51
|
+
I can provide a simple draft/template to make this easy.
|
|
52
|
+
|
|
53
|
+
Please contact me at: **referenceletter@obfuscator.io**
|
|
54
|
+
|
|
55
|
+
Thank you for supporting the project.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
57
59
|
JavaScript Obfuscator is a powerful free obfuscator for JavaScript, containing a variety of features which provide protection for your source code.
|
|
58
60
|
|
|
59
61
|
**Key features:**
|
|
@@ -346,7 +348,7 @@ console.log(result.getObfuscatedCode());
|
|
|
346
348
|
**Parameters:**
|
|
347
349
|
|
|
348
350
|
* `sourceCode` (`string`) – source code to obfuscate
|
|
349
|
-
* `options` (`Object`) – obfuscation options.
|
|
351
|
+
* `options` (`Object`) – obfuscation options. To use the Pro API, include at least one Pro feature: `vmObfuscation: true` or `parseHtml: true`. When no Pro feature is enabled, `obfuscatePro` falls back to the basic (local) obfuscation API.
|
|
350
352
|
* `apiConfig` (`Object`) – Pro API configuration:
|
|
351
353
|
* `apiToken` (`string`, required) – your API token from obfuscator.io
|
|
352
354
|
* `timeout` (`number`, optional) – request timeout in ms (default: `300000` - 5 minutes)
|
|
@@ -355,8 +357,9 @@ console.log(result.getObfuscatedCode());
|
|
|
355
357
|
|
|
356
358
|
**Returns:** `Promise<ObfuscationResult>`
|
|
357
359
|
|
|
360
|
+
> **Note:** When no Pro feature (`vmObfuscation` or `parseHtml`) is enabled, the Pro API is not needed, so `obfuscatePro` falls back to the basic (local) obfuscation API and no API request is made.
|
|
361
|
+
|
|
358
362
|
**Throws:** `ApiError` if:
|
|
359
|
-
- No Pro features (`vmObfuscation` or `parseHtml`) are enabled in options
|
|
360
363
|
- API token is invalid or expired
|
|
361
364
|
- API request fails
|
|
362
365
|
|
|
@@ -632,6 +635,7 @@ Following options are available for the JS Obfuscator:
|
|
|
632
635
|
--identifier-names-generator <string> [dictionary, hexadecimal, mangled, mangled-shuffled]
|
|
633
636
|
--identifiers-dictionary '<list>' (comma separated)
|
|
634
637
|
--identifiers-prefix <string>
|
|
638
|
+
--random-identifiers-prefix <boolean>
|
|
635
639
|
--ignore-imports <boolean>
|
|
636
640
|
--log <boolean>
|
|
637
641
|
--numbers-to-expressions <boolean>
|
|
@@ -665,6 +669,7 @@ Following options are available for the JS Obfuscator:
|
|
|
665
669
|
--string-array-wrappers-type <string> [variable, function]
|
|
666
670
|
--string-array-threshold <number>
|
|
667
671
|
--target <string> [browser, browser-no-eval, node]
|
|
672
|
+
--browser-environment <string> [http, https]
|
|
668
673
|
--transform-object-keys <boolean>
|
|
669
674
|
--unicode-escape-sequence <boolean>
|
|
670
675
|
--pro-api-token <string>
|
|
@@ -676,6 +681,7 @@ Following options are available for the JS Obfuscator:
|
|
|
676
681
|
--vm-target-functions '<list>' (comma separated)
|
|
677
682
|
--vm-exclude-functions '<list>' (comma separated)
|
|
678
683
|
--vm-target-functions-mode <string> [root, comment]
|
|
684
|
+
--vm-force-compile-dynamic-code <boolean>
|
|
679
685
|
--vm-wrap-top-level-initializers <boolean>
|
|
680
686
|
--vm-opcode-shuffle <boolean>
|
|
681
687
|
--vm-bytecode-encoding <boolean>
|
|
@@ -689,12 +695,19 @@ Following options are available for the JS Obfuscator:
|
|
|
689
695
|
--vm-split-dispatcher <boolean>
|
|
690
696
|
--vm-macro-ops <boolean>
|
|
691
697
|
--vm-debug-protection <boolean>
|
|
698
|
+
--vm-self-defending <boolean>
|
|
692
699
|
--vm-runtime-opcode-derivation <boolean>
|
|
693
700
|
--vm-stateful-opcodes <boolean>
|
|
701
|
+
--vm-async-executor <boolean>
|
|
702
|
+
--vm-call-context-opcodes <boolean>
|
|
694
703
|
--vm-stack-encoding <boolean>
|
|
695
704
|
--vm-randomize-keys <boolean>
|
|
696
705
|
--vm-indirect-dispatch <boolean>
|
|
697
706
|
--vm-compact-dispatcher <boolean>
|
|
707
|
+
--vm-register-based <boolean>
|
|
708
|
+
--vm-string-array-bytecode-only <boolean>
|
|
709
|
+
--vm-domain-lock '<list>' (comma separated)
|
|
710
|
+
--vm-domain-lock-redirect-url <string>
|
|
698
711
|
--vm-bytecode-format <string> [binary, json]
|
|
699
712
|
--parse-html <boolean>
|
|
700
713
|
--strict-mode <boolean>
|
|
@@ -1966,6 +1979,35 @@ function outer() {
|
|
|
1966
1979
|
|
|
1967
1980
|
**When to use:** When you need surgical control over exactly which functions get VM protection, especially nested functions that contain sensitive logic. Unlike `vmTargetFunctions` which only works with root-level named functions, comment mode lets you protect any function anywhere in your code.
|
|
1968
1981
|
|
|
1982
|
+
### `vmForceCompileDynamicCode`
|
|
1983
|
+
Type: `boolean` Default: `false`
|
|
1984
|
+
|
|
1985
|
+
Controls what VM obfuscation does with a function that contains a direct `eval`, `new Function(...)`, or `Function(...)` call.
|
|
1986
|
+
|
|
1987
|
+
By default, such a function (and every function defined inside it) is skipped from VM bytecoding and a `VMDynamicCodeSkipped` warning is reported on `result.getWarnings()`. This is because the runtime-built source may reference identifiers from the surrounding scope chain — identifiers that the obfuscator has renamed.
|
|
1988
|
+
|
|
1989
|
+
When set to `true`, the function is bytecoded anyway and the `VMDynamicCodeSkipped` warning is no longer emitted.
|
|
1990
|
+
|
|
1991
|
+
The separate `DynamicCodeRenameRisk` warning continues to fire regardless of this option, because the rename risk it describes is independent of the VM skip — turning this option on does not make the underlying pattern any safer.
|
|
1992
|
+
|
|
1993
|
+
```javascript
|
|
1994
|
+
// Source code
|
|
1995
|
+
function loadConfig(src) {
|
|
1996
|
+
return eval(src);
|
|
1997
|
+
}
|
|
1998
|
+
loadConfig('1 + 2');
|
|
1999
|
+
```
|
|
2000
|
+
|
|
2001
|
+
```javascript
|
|
2002
|
+
// Options
|
|
2003
|
+
{
|
|
2004
|
+
vmObfuscation: true,
|
|
2005
|
+
vmForceCompileDynamicCode: true
|
|
2006
|
+
}
|
|
2007
|
+
```
|
|
2008
|
+
|
|
2009
|
+
With the option off (default), `loadConfig` is left as plain JavaScript. With the option on, `loadConfig` is compiled to VM bytecode like any other function. Use this when you have audited the call site and know the runtime-built code does not depend on closure-renamed identifiers.
|
|
2010
|
+
|
|
1969
2011
|
### `vmWrapTopLevelInitializers`
|
|
1970
2012
|
Type: `boolean` Default: `false`
|
|
1971
2013
|
|
|
@@ -2063,6 +2105,44 @@ JavaScriptObfuscator.obfuscate(code, {
|
|
|
2063
2105
|
window.__VM_KEY__ = 'mySecretKey123';
|
|
2064
2106
|
```
|
|
2065
2107
|
|
|
2108
|
+
### `vmAsyncExecutor`
|
|
2109
|
+
Type: `boolean` Default: `false`
|
|
2110
|
+
|
|
2111
|
+
Enables the asynchronous VM executor, which lets [`vmBytecodeArrayEncodingKeyGetter`](#vmbytecodearrayencodingkeygetter) return a `Promise` (an **asynchronous** key getter) — so the decryption key can be fetched at runtime (network request, IndexedDB, etc.) instead of having to be available synchronously when the code loads.
|
|
2112
|
+
|
|
2113
|
+
**Heavily recommended for fully async codebases.** In this mode only `async` functions are virtualized — a synchronous function can't be made async without turning its return value into a `Promise` and breaking its callers — so code that is `async` throughout gets the most coverage. It still works when the root is synchronous (e.g. a sync IIFE / UMD wrapper): the outermost `async` functions inside are protected, and the sync parts are left as-is.
|
|
2114
|
+
|
|
2115
|
+
**What gets transformed:** every **outermost** `async` function, wherever it appears (including nested inside sync wrappers). The outermost async in each chain is the protected unit — everything inside it, sync and async, is compiled in. Synchronous functions and plain generators are left unobfuscated.
|
|
2116
|
+
```js
|
|
2117
|
+
function foo() { // sync — left as-is
|
|
2118
|
+
function bar() {} // sync — left as-is
|
|
2119
|
+
|
|
2120
|
+
async function baz() { // transformed
|
|
2121
|
+
// any code here, including calls to other async or sync functions
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
async function bark() { // transformed
|
|
2125
|
+
// any code here, including calls to other async or sync functions
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
```
|
|
2129
|
+
|
|
2130
|
+
**Skips and warnings.** Async generators are also left unobfuscated when an asynchronous key getter is active (an async generator must return its iterator synchronously and can't wait for the key). In the default `vmTargetFunctionsMode: 'root'` skips are silent (selection is automatic); in `comment` [mode](#vmtargetfunctionsmode) a warning is emitted through `ObfuscationResult.getWarnings()` whenever a function you explicitly marked can't be virtualized — it turned out synchronous, or it's an async generator under an async key getter.
|
|
2131
|
+
|
|
2132
|
+
The asynchronous key getter additionally requires `vmBytecodeArrayEncoding` with a `vmBytecodeArrayEncodingKeyGetter`.
|
|
2133
|
+
|
|
2134
|
+
**Usage example:**
|
|
2135
|
+
```ts
|
|
2136
|
+
JavaScriptObfuscator.obfuscate(code, {
|
|
2137
|
+
vmObfuscation: true,
|
|
2138
|
+
vmAsyncExecutor: true,
|
|
2139
|
+
vmBytecodeArrayEncoding: true,
|
|
2140
|
+
vmBytecodeArrayEncodingKey: 'mySecretKey123',
|
|
2141
|
+
// the key getter may now return a Promise
|
|
2142
|
+
vmBytecodeArrayEncodingKeyGetter: 'fetch("/vm-key").then((res) => res.text())'
|
|
2143
|
+
});
|
|
2144
|
+
```
|
|
2145
|
+
|
|
2066
2146
|
### `vmJumpsEncoding`
|
|
2067
2147
|
Type: `boolean` Default: `false`
|
|
2068
2148
|
|
|
@@ -2097,11 +2177,137 @@ Adds multi-layered tamper detection, anti-hooking, and anti-reverse-engineering
|
|
|
2097
2177
|
|
|
2098
2178
|
Strongly recommended to use together with [`vmDebugProtection`](#vmDebugProtection), [`vmBytecodeArrayEncodingKey`](#vmbytecodeArrayEncodingKey), and [`vmBytecodeArrayEncodingKeyGetter`](#vmbytecodeArrayEncodingKeyGetter).
|
|
2099
2179
|
|
|
2180
|
+
### `vmDefenseHook`
|
|
2181
|
+
Type: `{ name: string, aliases?: object }` Default: `''`
|
|
2182
|
+
|
|
2183
|
+
`vmDefenseHook` takes an object with two keys: **`name`** (required) and **`aliases`** (optional).
|
|
2184
|
+
|
|
2185
|
+
`name` is a **global function your host page defines** that a VM defense (`vmDebugProtection` / `vmSelfDefending`) calls with a signal object when it detects a hostile signal — a debugger or inspector, a headless / automation browser, an AI-coding-agent process, a disallowed domain, and so on. Use it to report the event to your backend (e.g. `navigator.sendBeacon`). The hook is a **pure telemetry sink**: its return value is ignored, and a missing or throwing hook is a silent no-op that can never disable a defense. To change what a defense *does* on detection, use [`vmDefenseReaction`](#vmdefensereaction).
|
|
2186
|
+
|
|
2187
|
+
`aliases` optionally renames the fields of that signal object — covered under *Renaming the signal fields* below.
|
|
2188
|
+
|
|
2189
|
+
**The signal object.** The hook receives a single `signal`:
|
|
2190
|
+
|
|
2191
|
+
- `source` — the specific detector that fired (see the table).
|
|
2192
|
+
- `category` — the group it reports under: `automation` (non-human browsers), `debugger` (a debugger/inspector is active), `sandbox` (instrumented/fake host), `domain` (domain-lock violation), `tamper` (built-ins patched at runtime), or `integrity` (the VM's own code was altered).
|
|
2193
|
+
- `score` / `threshold` — how strongly the detector fired and the value it had to reach; the hook fires only once `score >= threshold`. Most checks are all-or-nothing (a single decisive signal); `headless` sums several browser-shape signals, so its `score` is typically higher than its `threshold`.
|
|
2194
|
+
|
|
2195
|
+
| `source` | detects | `category` |
|
|
2196
|
+
|---|----------------------------------------------------------------------------------|---|
|
|
2197
|
+
| `integrity` | the obfuscated VM code itself has been modified | `integrity` |
|
|
2198
|
+
| `node` | browser-targeted code running under Node.js | `debugger` |
|
|
2199
|
+
| `debugger` | an attached or active debugger or inspector session, or a debug environment | `debugger` |
|
|
2200
|
+
| `headless` | a headless browser is used to run the code | `automation` |
|
|
2201
|
+
| `agent` | an AI coding-agent running the code | `automation` |
|
|
2202
|
+
| `timing` | an execution pause suggesting a breakpoint or a stepping debugger | `debugger` |
|
|
2203
|
+
| `sandbox` | the code runs in a sandbox or a faked host environment | `sandbox` |
|
|
2204
|
+
| `domain` | the page origin is not in the [`vmDomainLock`](#vmdomainlock) allow-list | `domain` |
|
|
2205
|
+
| `nativeHook` | a native built-in function has been replaced or hooked | `tamper` |
|
|
2206
|
+
|
|
2207
|
+
**Registering the hook.** Define it as a plain global **before** the obfuscated bundle loads — the VM runtime and its defenses run before your (protected) program, so many detections fire during startup:
|
|
2208
|
+
|
|
2209
|
+
```js
|
|
2210
|
+
// in your page, before the obfuscated script:
|
|
2211
|
+
window.__vmDetection = function (signal) { navigator.sendBeacon('/vm-defense', JSON.stringify(signal)); };
|
|
2212
|
+
```
|
|
2213
|
+
```js
|
|
2214
|
+
// obfuscation option:
|
|
2215
|
+
vmDefenseHook: { name: '__vmDetection' }
|
|
2216
|
+
```
|
|
2217
|
+
|
|
2218
|
+
> A hook defined *inside* the obfuscated source is registered too late to catch startup-time detections, and if it gets VM-compiled it can't be reached until your program runs. It's kept safe either way (a missing hook no-ops, and a re-entrancy guard prevents any runaway), but for complete coverage register it up front. To still protect your reporting logic, keep the registered hook a one-line buffer (`(window.__vmDet = window.__vmDet || []).push(signal)`) and read/send that buffer from your obfuscated code.
|
|
2219
|
+
|
|
2220
|
+
**Renaming the signal fields (`aliases`).** The default `source`/`category` values are descriptive names, so anyone instrumenting the callback (or reading the output) can recognise the protection and which detector fired. `aliases` renames signal fields to opaque tokens of your choice, applied inside the VM *before* the signal is emitted, so those names never appear in the output or reach the callback. Your app knows its own mapping and forwards the tokens to your backend.
|
|
2221
|
+
|
|
2222
|
+
Aliases are per field, keeping key and value renames separate: each field takes a `key` (the property name the callback receives); the string name-fields `source` and `category` also take a `values` map, while `score`/`threshold` are numbers and take only a `key`. The names you can map (anything else is rejected at build time):
|
|
2223
|
+
|
|
2224
|
+
- **field keys** — `source`, `category`, `score`, `threshold`
|
|
2225
|
+
- **`source` values** — `headless`, `agent`, `node`, `debugger`, `timing`, `sandbox`, `domain`, `nativeHook`, `integrity`
|
|
2226
|
+
- **`category` values** — `automation`, `debugger`, `sandbox`, `domain`, `tamper`, `integrity`
|
|
2227
|
+
|
|
2228
|
+
```js
|
|
2229
|
+
vmDefenseHook: {
|
|
2230
|
+
name: '__vmDetection',
|
|
2231
|
+
aliases: {
|
|
2232
|
+
source: { key: 'a8Qm', values: { headless: 'xP4m9Q' } },
|
|
2233
|
+
category: { key: 'p3Tx', values: { automation: 'bQ7s1M' } },
|
|
2234
|
+
score: { key: 's1' },
|
|
2235
|
+
threshold: { key: 't1' }
|
|
2236
|
+
}
|
|
2237
|
+
// the callback now receives e.g. { a8Qm: 'xP4m9Q', p3Tx: 'bQ7s1M', s1: <score>, t1: <threshold> }
|
|
2238
|
+
}
|
|
2239
|
+
```
|
|
2240
|
+
|
|
2241
|
+
This is fingerprint avoidance, not secrecy — the mapping can still be inferred by repeated testing — so its only benefit is not exposing stable, self-explanatory names. Unset entries keep their default names.
|
|
2242
|
+
|
|
2243
|
+
> A bare string (`vmDefenseHook: '__vmDetection'`) is accepted as shorthand for `{ name: '__vmDetection' }` but is **deprecated** — prefer the object form.
|
|
2244
|
+
|
|
2245
|
+
### `vmDefenseReaction`
|
|
2246
|
+
Type: `object` Default: `{ automation: 'break', debugger: 'decoy', sandbox: 'decoy', domain: 'break', tamper: 'break', integrity: 'break' }`
|
|
2247
|
+
|
|
2248
|
+
Configures how each detection category reacts. It does **not** enable anything — the defenses themselves are turned on by [`vmSelfDefending`](#vmselfdefending), [`vmDebugProtection`](#vmdebugprotection), and [`vmDomainLock`](#vmdomainlock); this option only selects *how* an enabled defense reacts. The **category is the unit of control** — every detector in a category enacts that category's reaction.
|
|
2249
|
+
|
|
2250
|
+
Each **category** groups the detectors that watch for one kind of hostile condition. A category only reacts when the option that emits its detectors is enabled:
|
|
2251
|
+
|
|
2252
|
+
| Category | Enabled by | Reacts when |
|
|
2253
|
+
|---|---|---|
|
|
2254
|
+
| `automation` | `vmSelfDefending` or `vmDebugProtection` | The code is being driven by software instead of a person: a headless or automated browser, a scraping / testing framework, or an AI coding-agent stepping the page. |
|
|
2255
|
+
| `debugger` | `vmDebugProtection` or `vmSelfDefending` | Someone has a debugger or the browser's developer-tools inspector open and is stepping through the running code to understand it. |
|
|
2256
|
+
| `sandbox` | `vmDebugProtection` | The code is not running in a real browser at all — it has been lifted into an emulated or scripted JavaScript environment to be executed and studied offline. |
|
|
2257
|
+
| `domain` | `vmDomainLock` | The code is running on a site you did not authorize: a host not in your [`vmDomainLock`](#vmdomainlock) allow-list (for example, your bundle copied onto someone else's domain). |
|
|
2258
|
+
| `tamper` | `vmSelfDefending` | The JavaScript environment around the VM has been modified to watch or hijack it, such as native browser built-ins swapped out for instrumented versions. |
|
|
2259
|
+
| `integrity` | `vmSelfDefending` | The protected bundle's own code has been edited or patched since you generated it. |
|
|
2260
|
+
|
|
2261
|
+
Every category maps to one or more of [`vmSelfDefending`](#vmselfdefending), [`vmDebugProtection`](#vmdebugprotection), and [`vmDomainLock`](#vmdomainlock); there is no category outside those three options, and a reaction set for a category whose option is off simply has no effect.
|
|
2262
|
+
|
|
2263
|
+
Keys are these six category names, or `default` (a fallback for unspecified categories). Values are:
|
|
2264
|
+
|
|
2265
|
+
- `break` — break immediately
|
|
2266
|
+
- `decoy` — keep running on poisoned state, silently producing wrong results
|
|
2267
|
+
- `none` — do nothing locally (telemetry only)
|
|
2268
|
+
|
|
2269
|
+
The per-category defaults are shown above; a category you don't set (or set to its default value) uses that default. `default` reaches **every** category, including the correct-by-construction ones (`integrity`, `tamper`), so `{ default: 'none' }` is a genuinely non-breaking, telemetry-only build:
|
|
2270
|
+
|
|
2271
|
+
```js
|
|
2272
|
+
vmDefenseReaction: { default: 'none' } // never break — pair with vmDefenseHook
|
|
2273
|
+
vmDefenseReaction: { automation: 'none', domain: 'break' } // tolerate automation FPs, still break on a bad domain
|
|
2274
|
+
```
|
|
2275
|
+
|
|
2276
|
+
### `browserEnvironment`
|
|
2277
|
+
Type: `object` Default: `{}`
|
|
2278
|
+
|
|
2279
|
+
Declares facts about the environment your production build is served in, so the protected code can bind itself to them. Only takes effect together with [`vmSelfDefending`](#vmselfdefending), and only for `browser` / `browser-no-eval` / `service-worker` targets — it is rejected for `node`, `userscript`, and `bytenode`.
|
|
2280
|
+
|
|
2281
|
+
Currently one field:
|
|
2282
|
+
|
|
2283
|
+
- **`transport`** — the scheme your production serves the bundle over: `'http'` or `'https'`. With `'https'`, the build ties its integrity to being served over HTTPS, so a copy an analyst lifts and serves over plain HTTP (a common local reverse-engineering setup) will not run correctly. `'http'` or an unset field adds no binding.
|
|
2284
|
+
|
|
2285
|
+
```js
|
|
2286
|
+
browserEnvironment: { transport: 'https' }
|
|
2287
|
+
```
|
|
2288
|
+
|
|
2289
|
+
> :warning: A build declared `transport: 'https'` runs correctly **only** where it is actually served over `https:`. Every other scheme corrupts it, so declare it only when every context that loads your production build is HTTPS. That excludes: plain `http://` (including `http://localhost` in development), `file://` (Electron / Cordova / packaged apps), and `blob:` / `about:` embeddings (a bundle running inside an `about:blank` or `srcdoc` iframe). A client-side HTTP→HTTPS redirect still renders the HTTP page first, so the bundle must not run before the redirect completes.
|
|
2290
|
+
|
|
2100
2291
|
### `vmStatefulOpcodes`
|
|
2101
2292
|
Type: `boolean` Default: `false`
|
|
2102
2293
|
|
|
2103
2294
|
Makes opcode meanings depend on position in the bytecode. Each position has a different opcode-to-handler mapping derived from a seed, so the same opcode number performs different operations at different positions.
|
|
2104
2295
|
|
|
2296
|
+
### `vmCallContextOpcodes`
|
|
2297
|
+
Type: `boolean` Default: `false`
|
|
2298
|
+
|
|
2299
|
+
Makes a protected function depend on where it's called from, so it can't be lifted out of the code and run or analyzed on its own — it only behaves correctly when invoked through its real call sites in the program. This option affects runtime performance.
|
|
2300
|
+
|
|
2301
|
+
Currently only the following constructions are supported:
|
|
2302
|
+
|
|
2303
|
+
- function declarations (`function f() {}`);
|
|
2304
|
+
- function expressions and arrow functions assigned to a variable (`const f = () => {}`);
|
|
2305
|
+
- instance private methods (`this.#m()`).
|
|
2306
|
+
|
|
2307
|
+
In every case the function must always be reached through a direct call (`f()`, `this.#m()`). If it is stored in another variable, passed as an argument, or otherwise used as a value, it is left unprotected. Async functions are supported; generators are not.
|
|
2308
|
+
|
|
2309
|
+
This option is experimental and may break your code, so test the output thoroughly before using it.
|
|
2310
|
+
|
|
2105
2311
|
### `vmStackEncoding`
|
|
2106
2312
|
Type: `boolean` Default: `false`
|
|
2107
2313
|
|
|
@@ -2117,6 +2323,17 @@ Uses a single VM executor instead of dual executors (sync + generator). Reduces
|
|
|
2117
2323
|
- `false` (default): dual executors — optimal performance, larger output
|
|
2118
2324
|
- `true`: single executor — smaller output, slightly slower
|
|
2119
2325
|
|
|
2326
|
+
### `vmRegisterBased`
|
|
2327
|
+
Type: `boolean` Default: `false`
|
|
2328
|
+
|
|
2329
|
+
Switches the VM from the default stack-based bytecode to a register-based execution model, which improves VM runtime performance in some cases by ~15-20%, but slightly increases obfuscated code size.
|
|
2330
|
+
|
|
2331
|
+
Because it emits a structurally different bytecode and executor, it also gives the VM a more unique fingerprint than the default stack-based one — use it when you want to vary the VM's shape so it is less recognizable to generic analysis.
|
|
2332
|
+
|
|
2333
|
+
Under the hood this isn't a native register-based compiler — the regular stack-based compiler still generates the bytecode, which a separate transformation stage then rewrites into register-based form.
|
|
2334
|
+
|
|
2335
|
+
This option is experimental - test that your code runs well when `vmRegisterBased` is enabled.
|
|
2336
|
+
|
|
2120
2337
|
### `vmStringArrayBytecodeOnly`
|
|
2121
2338
|
Type: `boolean` Default: `false`
|
|
2122
2339
|
|
|
@@ -2129,6 +2346,25 @@ When enabled, the string array will **only** extract strings from bytecode data
|
|
|
2129
2346
|
- `stringArrayThreshold` still controls what percentage of those bytecode strings are extracted
|
|
2130
2347
|
|
|
2131
2348
|
|
|
2349
|
+
### `vmDomainLock`
|
|
2350
|
+
Type: `string[]` Default: `[]`
|
|
2351
|
+
|
|
2352
|
+
##### :warning: This option does not work with `target: 'node'`, `target: 'service-worker'` or `target: 'bytenode'`
|
|
2353
|
+
|
|
2354
|
+
Restricts the obfuscated code to specific domains and/or sub-domains, and is much harder to locate and strip than `domainLock`.
|
|
2355
|
+
|
|
2356
|
+
If the source code isn't run on the domains specified by this option, the browser will be redirected to the URL passed to [`vmDomainLockRedirectUrl`](#vmdomainlockredirecturl), and further protected calls will return incorrect results even if the redirect is suppressed.
|
|
2357
|
+
|
|
2358
|
+
##### Multiple domains and sub-domains
|
|
2359
|
+
It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`. To make it work on the root domain including any sub-domains (`example.com`, `sub.example.com`), use `.example.com`.
|
|
2360
|
+
|
|
2361
|
+
### `vmDomainLockRedirectUrl`
|
|
2362
|
+
Type: `string` Default: `about:blank`
|
|
2363
|
+
|
|
2364
|
+
##### :warning: This option does not work with `target: 'node'`, `target: 'service-worker'` or `target: 'bytenode'`
|
|
2365
|
+
|
|
2366
|
+
Allows the browser to be redirected to a passed URL if the source code isn't run on the domains specified by [`vmDomainLock`](#vmdomainlock).
|
|
2367
|
+
|
|
2132
2368
|
### `strictMode`
|
|
2133
2369
|
Type: `boolean | null` Default: `null`
|
|
2134
2370
|
|
|
@@ -2195,6 +2431,15 @@ JavaScriptObfuscator.obfuscate(html, {
|
|
|
2195
2431
|
// output: HTML with only the marked script obfuscated
|
|
2196
2432
|
```
|
|
2197
2433
|
|
|
2434
|
+
### `randomIdentifiersPrefix`
|
|
2435
|
+
Type: `boolean` Default: `false`
|
|
2436
|
+
|
|
2437
|
+
Appends a seeded random prefix (6 alphanumeric characters) to all global identifiers. Use this option to avoid collisions between separately obfuscated bundles that are loaded into the same global scope — it removes the need to pick a unique `identifiersPrefix` per bundle manually.
|
|
2438
|
+
|
|
2439
|
+
- The random value is derived from the `seed` option and the source code hash, so reproducible builds with the same seed produce the same prefix.
|
|
2440
|
+
- When combined with `identifiersPrefix`, the random characters are appended to the user-provided prefix (e.g. `myApp` + random `aBc123` → `myAppaBc123`).
|
|
2441
|
+
- When combined with `vmObfuscation`, the random value replaces the default `vm` prefix — randomness already guarantees uniqueness.
|
|
2442
|
+
|
|
2198
2443
|
## Frequently Asked Questions
|
|
2199
2444
|
|
|
2200
2445
|
### What javascript versions are supported?
|