javascript-obfuscator 5.6.0 → 5.8.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  Change Log
2
2
 
3
+ v5.8.0
4
+ ---
5
+ * Pro API: added support for custom presets. `optionsPreset` accepts the alias of a custom preset saved in the obfuscator.io dashboard; `obfuscatePro()` and the CLI (`--pro-api-token`) fetch it and merge its options
6
+ * CLI: `--options-preset` with a VM preset name (e.g. `vm-default`) no longer fails locally when `--pro-api-token` is set; the name is passed to the Pro API
7
+
8
+ v5.7.0
9
+ ---
10
+ * **New option:** `advertisement` allows to control the display of the JavaScript Obfuscator Pro advertisement message in the console. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1448
11
+
3
12
  v5.6.0
4
13
  ---
5
14
  * 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`
package/README.md CHANGED
@@ -91,7 +91,6 @@ The example of obfuscated code: [github.com](https://github.com/javascript-obfus
91
91
  [![Coverage Status](https://coveralls.io/repos/github/javascript-obfuscator/javascript-obfuscator/badge.svg)](https://coveralls.io/github/javascript-obfuscator/javascript-obfuscator)
92
92
  [![Backers on Open Collective](https://opencollective.com/javascript-obfuscator/backers/badge.svg)](#backers)
93
93
  [![Sponsors on Open Collective](https://opencollective.com/javascript-obfuscator/sponsors/badge.svg)](#sponsors)
94
- [![xscode](https://img.shields.io/badge/Available%20on-xs%3Acode-blue?style=?style=plastic&logo=appveyor&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAAlUlEQVR42uzXSwqAMAwE0Mn9L+3Ggtgkk35QwcnSJo9S+yGwM9DCooCbgn4YrJ4CIPUcQF7/XSBbx2TEz4sAZ2q1RAECBAiYBlCtvwN+KiYAlG7UDGj59MViT9hOwEqAhYCtAsUZvL6I6W8c2wcbd+LIWSCHSTeSAAECngN4xxIDSK9f4B9t377Wd7H5Nt7/Xz8eAgwAvesLRjYYPuUAAAAASUVORK5CYII=)](https://xscode.com/sanex3339/javascript-obfuscator)
95
94
 
96
95
  #### *NOTE! the README on the master branch might not match that of the latest stable release!*
97
96
 
@@ -421,6 +420,29 @@ if (ProApiClient.hasProFeatures(options)) {
421
420
  Pro features include:
422
421
  - `vmObfuscation: true` – VM-based bytecode obfuscation
423
422
  - `parseHtml: true` – HTML parsing with inline JavaScript obfuscation
423
+ - `optionsPreset` set to a VM preset (`vm-default`, ...) or to a [custom preset](#custom-presets) alias – only the Pro API can resolve these
424
+
425
+ ### Custom Presets :new:
426
+
427
+ Save a configuration in the [obfuscator.io](https://obfuscator.io) dashboard as a custom preset and give it an **API alias** in the save dialog. Set `optionsPreset` to that alias and `obfuscatePro()` fetches the preset's options before obfuscating, so the configuration lives in the dashboard and changes there apply to the next build:
428
+
429
+ ```javascript
430
+ const result = await JavaScriptObfuscator.obfuscatePro(
431
+ sourceCode,
432
+ { optionsPreset: 'production' },
433
+ { apiToken: 'YOUR_API_TOKEN' }
434
+ );
435
+ ```
436
+
437
+ The preset's options are the base and any other option you pass overrides them, the same way built-in presets are merged. A custom preset with no Pro feature enabled is obfuscated locally with its options. A team service key resolves the team owner's presets; a member's personal key resolves their own presets plus the ones shared with the team.
438
+
439
+ An alias that does not exist for your account throws an `ApiError` with `statusCode` 404. Without an API token (`obfuscate()`), only the built-in presets are available.
440
+
441
+ The same works from the CLI:
442
+
443
+ ```sh
444
+ javascript-obfuscator input.js --pro-api-token YOUR_API_TOKEN --options-preset production -o output.js
445
+ ```
424
446
 
425
447
  ### Error Handling
426
448
 
@@ -456,7 +478,7 @@ javascript-obfuscator input.js --pro-api-token YOUR_API_TOKEN --pro-api-version
456
478
  - `--pro-api-token <string>` – Your API token from [obfuscator.io](https://obfuscator.io)
457
479
  - `--pro-api-version <string>` – Obfuscator.io version to use (optional, defaults to latest)
458
480
 
459
- The CLI automatically detects when Pro features (`vmObfuscation` or `parseHtml`) are enabled and routes the request through the Pro API.
481
+ The CLI automatically detects when Pro features (`vmObfuscation`, `parseHtml`, or an `optionsPreset` naming a VM preset or a [custom preset](#custom-presets)) are enabled and routes the request through the Pro API.
460
482
 
461
483
  ### Large File Uploads
462
484
 
@@ -555,6 +577,7 @@ Following options are available for the JS Obfuscator:
555
577
 
556
578
  ```javascript
557
579
  {
580
+ advertisement: true,
558
581
  compact: true,
559
582
  controlFlowFlattening: false,
560
583
  controlFlowFlatteningThreshold: 0.75,
@@ -618,6 +641,7 @@ Following options are available for the JS Obfuscator:
618
641
 
619
642
  -o, --output
620
643
 
644
+ --advertisement <boolean>
621
645
  --compact <boolean>
622
646
  --config <string>
623
647
  --control-flow-flattening <boolean>
@@ -715,6 +739,13 @@ Following options are available for the JS Obfuscator:
715
739
 
716
740
  <!-- ##options-start## -->
717
741
 
742
+ ### `advertisement`
743
+ Type: `boolean` Default: `true`
744
+
745
+ Allows to control the display of the JavaScript Obfuscator Pro advertisement message in the console.
746
+
747
+ The message is only shown when using the Node.js CLI in an interactive (TTY) terminal, is never shown in CI environments or in the browser, and is limited to a few displays. Set this option to `false` to disable the advertisement message completely.
748
+
718
749
  ### `compact`
719
750
  Type: `boolean` Default: `true`
720
751
 
@@ -1133,6 +1164,10 @@ Available values:
1133
1164
  * `medium-obfuscation`;
1134
1165
  * `high-obfuscation`.
1135
1166
 
1167
+ With `obfuscatePro()` or `--pro-api-token` the following are also accepted and resolved by the Pro API:
1168
+ * the VM presets: `vm-low-obfuscation`, `vm-default`, `vm-medium-obfuscation`, `vm-high-obfuscation`, `vm-ultra-high-obfuscation`, `vm-anti-llm`;
1169
+ * the alias of a [custom preset](#custom-presets) saved in the [obfuscator.io](https://obfuscator.io) dashboard.
1170
+
1136
1171
  All addition options will be merged with selected options preset.
1137
1172
 
1138
1173
  ### `renameGlobals`
@@ -1916,8 +1951,8 @@ Specify exactly which root-level functions should get VM protection by name.
1916
1951
  **Example:**
1917
1952
  ```javascript
1918
1953
  {
1919
- vmObfuscation: true,
1920
- vmTargetFunctions: ['someFunctionName']
1954
+ vmObfuscation: true,
1955
+ vmTargetFunctions: ['someFunctionName']
1921
1956
  }
1922
1957
  ```
1923
1958
 
@@ -1931,8 +1966,8 @@ Specify root-level functions that should never get VM protection. Takes preceden
1931
1966
  **Example:**
1932
1967
  ```javascript
1933
1968
  {
1934
- vmObfuscation: true,
1935
- vmExcludeFunctions: ['someFunctionName']
1969
+ vmObfuscation: true,
1970
+ vmExcludeFunctions: ['someFunctionName']
1936
1971
  }
1937
1972
  ```
1938
1973
 
@@ -1952,28 +1987,28 @@ Controls how functions/methods are selected for VM obfuscation.
1952
1987
  ```javascript
1953
1988
  // Source code
1954
1989
  function regularFunction() {
1955
- return 'not virtualized';
1990
+ return 'not virtualized';
1956
1991
  }
1957
1992
 
1958
1993
  /* javascript-obfuscator:vm */
1959
1994
  function sensitiveFunction() {
1960
- return 'this will be VM-protected';
1995
+ return 'this will be VM-protected';
1961
1996
  }
1962
1997
 
1963
1998
  function outer() {
1964
- /* javascript-obfuscator:vm */
1965
- function nestedSensitive() {
1966
- return 'nested but still VM-protected';
1967
- }
1968
- return nestedSensitive();
1999
+ /* javascript-obfuscator:vm */
2000
+ function nestedSensitive() {
2001
+ return 'nested but still VM-protected';
2002
+ }
2003
+ return nestedSensitive();
1969
2004
  }
1970
2005
  ```
1971
2006
 
1972
2007
  ```javascript
1973
2008
  // Obfuscator options
1974
2009
  {
1975
- vmObfuscation: true,
1976
- vmTargetFunctionsMode: 'comment'
2010
+ vmObfuscation: true,
2011
+ vmTargetFunctionsMode: 'comment'
1977
2012
  }
1978
2013
  ```
1979
2014
 
@@ -2034,6 +2069,8 @@ const MY_STRING = (() => { return /* VM bytecode call */ })(); // String hidden
2034
2069
 
2035
2070
  **Note:** This option only works when `vmTargetFunctionsMode` is `'root'` (the default).
2036
2071
 
2072
+ **Warnings:** Whenever a top-level initializer ends up in plain JavaScript under VM obfuscation, a `VMTopLevelInitializerNotVirtualized` warning listing the affected variable names is reported. That covers: this option being disabled, initializers this option had to skip (each with the reason — e.g. the initializer references a sibling declarator or contains top-level await), and `vmAsyncExecutor` mode where the synchronous wrappers can't be virtualized at all.
2073
+
2037
2074
  ### `vmDynamicOpcodes`
2038
2075
  Type: `boolean` Default: `false`
2039
2076
 
@@ -2048,7 +2085,7 @@ As the result - smaller output and each build looks different.
2048
2085
  ### `vmBytecodeEncoding`
2049
2086
  Type: `boolean` Default: `false`
2050
2087
 
2051
- Encodes each bytecode instruction. Instructions are decoded one at a time during execution.
2088
+ Encodes each bytecode instruction (decoded one at a time during execution) and masks the string constants stored in the bytecode pool, so plaintext strings do not sit in the compiled bytecode.
2052
2089
 
2053
2090
  ### `vmBytecodeArrayEncoding`
2054
2091
  Type: `boolean` Default: `false`
@@ -2067,11 +2104,15 @@ This option externalizes the encryption key - it's not embedded in the obfuscate
2067
2104
  ### `vmBytecodeArrayEncodingKeyGetter`
2068
2105
  Type: `string` Default: `''`
2069
2106
 
2070
- **Synchronous** JavaScript expression that **returns** the encryption key at runtime. This expression is evaluated when the obfuscated code loads, and must return the same key that was provided in `vmBytecodeArrayEncodingKey`.
2107
+ **Synchronous** JavaScript expression that **returns** the encryption key at runtime. This expression is evaluated when the obfuscated code loads, and must return the same key that was provided in `vmBytecodeArrayEncodingKey`. To resolve the key **asynchronously** (a `Promise`), enable [`vmAsyncExecutor`](#vmasyncexecutor).
2108
+
2109
+ > **Note:** a Promise-returning getter requires `vmAsyncExecutor`. This can't be checked at build time, so a Promise getter with `vmAsyncExecutor` **off** fails at runtime — the decoder receives the Promise instead of the key.
2110
+
2111
+ **The obfuscated code will only work when the key getter returns exactly the same key that was used during obfuscation.** If the keys don't match — or the getter returns `undefined`, `null`, or an empty string — decryption produces a wrong keystream and the code fails at runtime with garbage output or an ordinary runtime error. There is deliberately no distinct, key-specific error message, so a failed key is indistinguishable from any other runtime fault.
2071
2112
 
2072
- **The obfuscated code will only work when the key getter returns exactly the same key that was used during obfuscation.** If the keys don't match, decryption will fail and the code will produce garbage or errors. If the key getter returns `undefined`, `null`, or an empty string, the code will throw an error: "VM decryption key not available".
2113
+ **Important:** Keep the key out of the same file/script as the obfuscated code inlining it there lets even a purely **static** scan of the bundle recover it. Store it in a separate source instead: server-set cookies, `localStorage` populated by another script, a server-injected HTML meta tag, a global set by a different script, or (with [`vmAsyncExecutor`](#vmasyncexecutor)) fetched from your backend at runtime.
2073
2114
 
2074
- **Important:** The key should NOT be defined in the same JavaScript file/script as the obfuscated code. Doing so defeats the purpose of key externalization, as static analysis could still find the key. Store the key in a separate source: server-set cookies, localStorage populated by another script, server-injected HTML meta tags, or a global variable set by a different script that loads before the obfuscated code.
2115
+ When the key is fetched from your backend (via [`vmAsyncExecutor`](#vmasyncexecutor)), add session- or origin-based checks on that endpoint: return the correct key to real users (valid session, expected `Origin`/`Referer`) and a garbage key to suspicious requests (e.g. a `localhost`/unexpected origin, no session). Real users run normally; a copy running outside your environment gets a key that decrypts to nothing. The exact logic depends on your site.
2075
2116
 
2076
2117
  Examples:
2077
2118
  ```ts
@@ -2089,16 +2130,19 @@ vmBytecodeArrayEncodingKeyGetter: "document.querySelector('meta[name=\"vm-key\"]
2089
2130
 
2090
2131
  // From nested object
2091
2132
  vmBytecodeArrayEncodingKeyGetter: "window.config.encryption.key"
2133
+
2134
+ // From backend, async (requires vmAsyncExecutor)
2135
+ vmBytecodeArrayEncodingKeyGetter: 'fetch("/vm-key").then((res) => res.text())'
2092
2136
  ```
2093
2137
 
2094
2138
  **Usage example:**
2095
2139
  ```ts
2096
2140
  // Build time
2097
2141
  JavaScriptObfuscator.obfuscate(code, {
2098
- vmObfuscation: true,
2099
- vmBytecodeArrayEncoding: true,
2100
- vmBytecodeArrayEncodingKey: 'mySecretKey123',
2101
- vmBytecodeArrayEncodingKeyGetter: 'window.__VM_KEY__'
2142
+ vmObfuscation: true,
2143
+ vmBytecodeArrayEncoding: true,
2144
+ vmBytecodeArrayEncodingKey: 'mySecretKey123',
2145
+ vmBytecodeArrayEncodingKeyGetter: 'window.__VM_KEY__'
2102
2146
  });
2103
2147
 
2104
2148
  // Runtime - key must be set before obfuscated code runs
@@ -2148,25 +2192,36 @@ Type: `boolean` Default: `false`
2148
2192
 
2149
2193
  Encodes jump targets in the bytecode. Jump offsets are calculated at runtime, hiding the control flow structure (`if`/`else`, loops, etc.) from static analysis.
2150
2194
 
2151
- ### `vmDecoyOpcodes`
2195
+ ### `vmMacroOps`
2152
2196
  Type: `boolean` Default: `false`
2153
2197
 
2154
- Adds fake opcode handlers to the VM dispatcher that are never called. For example, if the VM uses 20 real opcodes, this might add 30 fake handlers, making the interpreter appear more complex than it really is.
2198
+ Combines common instruction sequences into single "macro" opcodes. For example, `LOAD_ARG + PUSH_CONST + SUB` can become `MACRO_SUB_ARG_CONST`, reducing interpreter dispatches. This works with both the default stack VM and `vmRegisterBased: true`; enable `vmMacroOps: true` explicitly in either mode.
2155
2199
 
2156
- ### `vmDeadCodeInjection`
2157
- Type: `boolean` Default: `false`
2200
+ ### `vmDebugProtection`
2201
+ Type: `boolean | object` Default: `false`
2158
2202
 
2159
- Injects fake bytecode sequences that are never executed. These look like real instructions but are skipped during runtime, confusing analysis tools that process them.
2203
+ Adds multi-layered anti-debugging, anti-analysis, and anti-LLM defenses to the VM runtime. Works best with `browser`/`browser-no-eval` targets.
2160
2204
 
2161
- ### `vmMacroOps`
2162
- Type: `boolean` Default: `false`
2205
+ Pass `true` to enable it, or `false` to disable it. Pass an object to enable it while turning off a specific defense:
2163
2206
 
2164
- Combines common instruction sequences into single "macro" opcodes. For example, `LOAD + ADD + STORE` might become a single `MACRO_ADD_TO_VAR` instruction. This breaks pattern recognition and can improve performance.
2207
+ ```js
2208
+ {
2209
+ vmDebugProtection: {
2210
+ // most defenses are always on; but CDP/devtools detection is disabled
2211
+ inspectorDetection: false
2212
+ }
2213
+ }
2214
+ ```
2165
2215
 
2166
- ### `vmDebugProtection`
2167
- Type: `boolean` Default: `false`
2216
+ > :warning: **The object is not a menu of defenses to switch on.** When debug protection is enabled, the great majority of its defenses are **always active and cannot be turned off**. The sub-options below expose only the small number of defenses that some consumers may deliberately need to relax (for example, because a false positive would break a legitimate workflow) — every other defense stays on regardless.
2217
+
2218
+ | Sub-option | Type | Default | Description |
2219
+ | --- | --- | --- | --- |
2220
+ | `inspectorDetection` | `boolean` | `true` | Detect and react to an attached CDP (Chrome DevTools Protocol) inspector — both the CDP `Runtime` domain being enabled (`Runtime.enable`) and an active debugger (the CDP `Debugger` domain, e.g. breakpoints or the developer-tools Sources panel). **Opening the browser's developer tools enables those domains, so an open inspector is detected and reacted to.** Keeping it enabled is recommended; set it to `false` only if your users legitimately open developer tools. |
2221
+
2222
+ > :bulb: The object form is available through the API
2168
2223
 
2169
- Adds multi-layered anti-debugging, anti-analysis, and anti-LLM defenses to the VM runtime. For best results, allow `unsafe-eval` in your Content Security Policy. Works best with `browser`/`browser-no-eval` targets.
2224
+ > :warning: **Automation frameworks.** With `inspectorDetection` on (the default), driving the protected page with a CDP-based tool (Puppeteer, Playwright, Selenium/ChromeDriver) is detected as an attached inspector. If you run automated tests against protected code, build those with `vmDebugProtection: { inspectorDetection: false }`.
2170
2225
 
2171
2226
  ### `vmSelfDefending`
2172
2227
  Type: `boolean` Default: `false`
@@ -2175,12 +2230,21 @@ Adds multi-layered tamper detection, anti-hooking, and anti-reverse-engineering
2175
2230
 
2176
2231
  > :warning: This option force-enables [`vmBytecodeArrayEncoding`](#vmbytecodeArrayEncoding).
2177
2232
 
2233
+ > :warning: **Sensitive environment detection.** This option binds the obfuscated code to its target runtime environment and uses advanced browser fingerprinting to detect automation tools. Code protected with this option **will intentionally break** when run in:
2234
+ > - Headless browsers (headless Chrome/Chromium, PhantomJS)
2235
+ > - Browser automation tools (Puppeteer, Playwright, Cypress, Selenium/ChromeDriver, Nightmare)
2236
+ > - Node.js (when `target` is set to `browser`)
2237
+ > - jsdom or similar server-side DOM emulations
2238
+ > - Environments where native browser builtins have been hooked or replaced
2239
+ >
2240
+ > The code **will work correctly** in regular browsers (Chrome, Firefox, Safari, Edge), including when loaded inside iframes, browser extensions (content scripts), and Web Workers. If you need to run automated tests against protected code, disable `vmSelfDefending` for test builds — this option is designed to prevent automated analysis and **cannot be safely used with any automation framework**.
2241
+
2178
2242
  Strongly recommended to use together with [`vmDebugProtection`](#vmDebugProtection), [`vmBytecodeArrayEncodingKey`](#vmbytecodeArrayEncodingKey), and [`vmBytecodeArrayEncodingKeyGetter`](#vmbytecodeArrayEncodingKeyGetter).
2179
2243
 
2180
2244
  ### `vmDefenseHook`
2181
- Type: `{ name: string, aliases?: object }` Default: `''`
2245
+ Type: `{ name: string, aliases?: object } | null` Default: `null`
2182
2246
 
2183
- `vmDefenseHook` takes an object with two keys: **`name`** (required) and **`aliases`** (optional).
2247
+ `vmDefenseHook` is `null` (disabled) or an object with two keys: **`name`** (required) and **`aliases`** (optional).
2184
2248
 
2185
2249
  `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
2250
 
@@ -2240,8 +2304,6 @@ vmDefenseHook: {
2240
2304
 
2241
2305
  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
2306
 
2243
- > A bare string (`vmDefenseHook: '__vmDetection'`) is accepted as shorthand for `{ name: '__vmDetection' }` but is **deprecated** — prefer the object form.
2244
-
2245
2307
  ### `vmDefenseReaction`
2246
2308
  Type: `object` Default: `{ automation: 'break', debugger: 'decoy', sandbox: 'decoy', domain: 'break', tamper: 'break', integrity: 'break' }`
2247
2309
 
@@ -2276,17 +2338,33 @@ vmDefenseReaction: { automation: 'none', domain: 'break' } // tolerate automat
2276
2338
  ### `browserEnvironment`
2277
2339
  Type: `object` Default: `{}`
2278
2340
 
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`.
2341
+ Declares facts about the environment your production build runs in, so the protected code can bind to, react to, or tolerate them. Available only for `browser` / `browser-no-eval` / `service-worker` targets — it is rejected for `node`, `userscript`, and `bytenode`. Each field takes effect together with a specific protection, noted below.
2280
2342
 
2281
- Currently one field:
2343
+ Fields:
2282
2344
 
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.
2345
+ - **`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. Takes effect with [`vmSelfDefending`](#vmselfdefending).
2284
2346
 
2285
2347
  ```js
2286
2348
  browserEnvironment: { transport: 'https' }
2287
2349
  ```
2288
2350
 
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.
2351
+ - **`hosting`** where your production bundle is served from: `'remote'` or `'local'`. With `'remote'`, the build ties its integrity to being served from a remote host, so a copy an analyst lifts and runs in their own local setup is treated as a runtime-environment mismatch and the automation defenses react (see [`vmDebugProtection`](#vmdebugprotection) and [`vmDefenseReaction`](#vmdefensereaction)). `'local'` or an unset field adds no binding. Takes effect with `vmDebugProtection`, on `browser` / `browser-no-eval` only.
2352
+
2353
+ ```js
2354
+ browserEnvironment: { transport: 'https', hosting: 'remote' }
2355
+ ```
2356
+
2357
+ - **`hookedBuiltins`** — set to `true` to declare that the runtime your production build runs in legitimately replaces native builtins with JavaScript wrappers: the app's own anti-tamper, the host page, or other browser extensions sharing the same realm. [`vmSelfDefending`](#vmselfdefending) normally treats a replaced native builtin as tampering and stops the build from running; with this set, it tolerates such an environment and the code runs. `false` or an unset field keeps the strict behavior. Takes effect with [`vmSelfDefending`](#vmselfdefending).
2358
+
2359
+ ```js
2360
+ browserEnvironment: { hookedBuiltins: true }
2361
+ ```
2362
+
2363
+ This option relaxes nativity checks only; clean-realm validation and required builtin behavior remain enforced.
2364
+
2365
+ > :warning: `hookedBuiltins` deliberately relaxes tamper detection: once it is set, an analyst who wraps those same builtins to inspect your code is no longer stopped either. The VM virtualization, anti-debugging, and integrity protections are unaffected. Enable it only when your production runtime is known to hook builtins and that weaker guarantee is acceptable.
2366
+
2367
+ > :warning: The `transport` and `hosting` fields bind the protected build to the environment you declare. The same build loaded in any environment that does not match — including transiently, before it reaches its final one — will not run correctly, by design. Declare a field only when every context that loads your production build matches it, and keep these declarations off the builds you use for local development, testing, and CI.
2290
2368
 
2291
2369
  ### `vmStatefulOpcodes`
2292
2370
  Type: `boolean` Default: `false`
@@ -2345,7 +2423,6 @@ When enabled, the string array will **only** extract strings from bytecode data
2345
2423
  - When `vmBytecodeArrayEncoding: true` — top-level base64 encoded bytecode strings are extracted
2346
2424
  - `stringArrayThreshold` still controls what percentage of those bytecode strings are extracted
2347
2425
 
2348
-
2349
2426
  ### `vmDomainLock`
2350
2427
  Type: `string[]` Default: `[]`
2351
2428
 
@@ -2365,81 +2442,6 @@ Type: `string` Default: `about:blank`
2365
2442
 
2366
2443
  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
2444
 
2368
- ### `strictMode`
2369
- Type: `boolean | null` Default: `null`
2370
-
2371
- Allows to specify how the obfuscator should treat code regarding JavaScript strict mode.
2372
-
2373
- Available values:
2374
- * `null` (default) - auto-detect strict mode from the code. If the code has explicit `'use strict'` directive, ES module syntax, or class methods, it's treated as strict mode. Otherwise, sloppy mode is assumed.
2375
- * `true` - force strict mode treatment for all code, even without explicit `'use strict'` directive. Use this when your code will run in strict mode context (e.g., in ES modules, bundlers, or modern frameworks).
2376
- * `false` - only explicit strict mode indicators (`'use strict'`, ES modules, class methods) are treated as strict. Parent scope inheritance still applies per JS spec.
2377
-
2378
- ### `parseHtml`
2379
- Type: `boolean` Default: `false`
2380
-
2381
- Enables obfuscation of JavaScript within HTML `<script>` tags.
2382
-
2383
- When enabled, the obfuscator will:
2384
- - Auto-detect if input is HTML (by checking for `<!DOCTYPE`, `<html>`, `<head>`, `<body>`, or `<script>` tags)
2385
- - Extract JavaScript from `<script>` tags marked with the `data-javascript-obfuscator` attribute
2386
- - Obfuscate each marked script individually while preserving the HTML structure
2387
- - Inject obfuscated code back into the original positions
2388
-
2389
- **Important:** Only scripts with the `data-javascript-obfuscator` attribute are obfuscated. Each marked script is obfuscated individually and independently. This means:
2390
- - Code inside marked script tags **must be isolated** - it must NOT reference variables, functions, or classes defined in other marked script tags
2391
- - Unmarked scripts can still access globals defined by marked scripts (via `var` declarations or explicit `globalThis` assignments)
2392
- - This gives you explicit control over which scripts to protect
2393
-
2394
- **Obfuscated (must have `data-javascript-obfuscator` attribute):**
2395
- - `<script data-javascript-obfuscator>` - regular scripts
2396
- - `<script type="text/javascript" data-javascript-obfuscator>` - explicitly typed scripts
2397
- - Scripts with any additional attributes (`id`, `class`, other `data-*`, etc.)
2398
-
2399
- **Skipped (left unchanged):**
2400
- - Scripts without `data-javascript-obfuscator` attribute
2401
- - `<script type="module">` - ES modules (even with the attribute)
2402
- - `<script src="...">` - external scripts (even with the attribute)
2403
- - Empty script tags
2404
-
2405
- **Note:** Source maps are not generated when `parseHtml` is enabled, as they would not map correctly to the HTML output.
2406
-
2407
- Example:
2408
- ```ts
2409
- // input
2410
- const html = `<!DOCTYPE html>
2411
- <html>
2412
- <body>
2413
- <!-- This script will NOT be obfuscated -->
2414
- <script>
2415
- var helper = 'utility';
2416
- </script>
2417
-
2418
- <!-- This script WILL be obfuscated -->
2419
- <script data-javascript-obfuscator>
2420
- var greeting = 'Hello World';
2421
- console.log(greeting);
2422
- </script>
2423
- </body>
2424
- </html>`;
2425
-
2426
- JavaScriptObfuscator.obfuscate(html, {
2427
- parseHtml: true,
2428
- stringArray: true
2429
- });
2430
-
2431
- // output: HTML with only the marked script obfuscated
2432
- ```
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
-
2443
2445
  ## Frequently Asked Questions
2444
2446
 
2445
2447
  ### What javascript versions are supported?