Title: deadCodeInjection intermittently drops a positional argument across a rest/spread forward (non-deterministic, minified input) · Issue #1423 · javascript-obfuscator/javascript-obfuscator · GitHub
Open Graph Title: deadCodeInjection intermittently drops a positional argument across a rest/spread forward (non-deterministic, minified input) · Issue #1423 · javascript-obfuscator/javascript-obfuscator
X Title: deadCodeInjection intermittently drops a positional argument across a rest/spread forward (non-deterministic, minified input) · Issue #1423 · javascript-obfuscator/javascript-obfuscator
Description: Summary With deadCodeInjection: true on already-minified input (bundler output where locals are 1-char identifiers, i.e. effectively the same as identifierNamesGenerator: 'mangled'), a positional argument is intermittently lost across a ...
Open Graph Description: Summary With deadCodeInjection: true on already-minified input (bundler output where locals are 1-char identifiers, i.e. effectively the same as identifierNamesGenerator: 'mangled'), a positional a...
X Description: Summary With deadCodeInjection: true on already-minified input (bundler output where locals are 1-char identifiers, i.e. effectively the same as identifierNamesGenerator: 'mangled'), a posi...
Opengraph URL: https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1423
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"deadCodeInjection intermittently drops a positional argument across a rest/spread forward (non-deterministic, minified input)","articleBody":"## Summary\n\nWith `deadCodeInjection: true` on **already-minified input** (bundler output where locals are 1-char identifiers, i.e. effectively the same as `identifierNamesGenerator: 'mangled'`), a **positional argument is intermittently lost across a rest/spread forward** (`(...args) =\u003e fn(...args)`): the callee receives one argument as `undefined` even though the caller passed a defined value.\n\nThe corruption is **non-deterministic across builds from identical source**, because the default `seed: 0` re-randomises which AST fragments `deadCodeInjection` clones each run. The same source \"works\" in some builds and breaks in others.\n\nThis looks related to the historically-fixed scope/clone issues (#708, and the `NodeUtils.cloneRecursive` / `range` scope-resolution fix in v5.2.0), and to #262 (non-deterministic `undefined` / \"is not a function\" with `deadCodeInjection` + `mangled` + `seed: 0`), but I could not find an open issue with this exact signature (positional arg silently `undefined` through a spread forward).\n\n## Version\n\n- `javascript-obfuscator`: **5.4.3** (latest on npm)\n- Node: v24.15.0\n- Input: ESM chunk emitted by a bundler (rolldown/esbuild), already minified\n\n## Options\n\n```js\n{\n compact: true,\n controlFlowFlattening: true,\n controlFlowFlatteningThreshold: 0.75,\n deadCodeInjection: true,\n deadCodeInjectionThreshold: 0.4,\n stringArray: true,\n stringArrayEncoding: ['base64'],\n stringArrayThreshold: 0.75,\n identifierNamesGenerator: 'mangled',\n selfDefending: false,\n debugProtection: false,\n // seed: 0 (default) — non-deterministic\n}\n```\n\n## What we observed\n\nOur app dispatches IPC calls through a generic rest/spread forwarder. Source:\n\n```js\n// for each registered method:\nhandleIpc(state, `${group}:${method}`, (e, ...args) =\u003e\n runWithRoute(routeFor(e.sender.id), searchParamsFor(e.sender.id), () =\u003e\n methods[method](...args), // \u003c-- args forwarded via spread\n ),\n);\n```\n\nMinified input handed to the obfuscator (unbroken, semantically correct):\n\n```js\n(v,...b)=\u003eqi(o(v.sender.id),a(v.sender.id),()=\u003ep[h](...b))\n```\n\nObfuscated output from a **broken** build (`deadCodeInjection: true`):\n\n```js\n(H,...I)=\u003eqi(w(H[auH(0x3932)]['id']),x(H[auH(0x3932)]['id']),()=\u003eF[G](...I))\n```\n\nThe structure looks fine, yet at runtime a call made with **two** defined arguments arrives at `F[G]` (`p[h]`) with the **second argument `undefined`**.\n\nConcretely, we verified the boundary on both sides in the *same* broken build:\n\n- Caller side (before the boundary), logged args: `[\"/path/to/repo\", \"5c09f59b-…\"]` — both defined.\n- Callee side (after the spread forward): `arg0 = \"/path/to/repo\"`, `arg1 = undefined`.\n\nFlipping `deadCodeInjection: false` (everything else unchanged) makes the problem disappear across every build we tried; pinning a non-zero `seed` makes the outcome reproducible per seed.\n\n## Why we can't attach a minimal standalone repro (yet)\n\nWe tried hard to reduce it. Obfuscating the *isolated* chunk that contains the forwarder — byte-for-byte the real bundler output — and executing it across 200 seeds produced **zero** corruptions. The failure only appears when the **whole large chunk** (~950 KB, our Electron main-process bundle) is obfuscated as one unit. That strongly suggests the corruption is an **emergent property of `deadCodeInjection` cloning fragments from elsewhere in the same chunk** (it \"steals a part of code\" per #708), so a small extracted snippet has nothing to steal and never triggers it. The full bundle isn't runnable standalone (native `require`s), which is what has blocked a clean minimal repro.\n\nWe understand a non-reproducible report is hard to action. We're filing it because the signature (silent `undefined` through a spread forward, non-deterministic by seed, only with `deadCodeInjection` + mangled/minified input) seems like a real miscompile and may ring a bell given #262/#708/v5.2.0.\n\n## Offer to help\n\nWe can run **any instrumentation / logging / debug build you suggest** against our real bundle and report back, and we're happy to iterate to help you pin it down (e.g. bisecting seeds, dumping the pre/post AST for a specific node, trying a patched build). If a private channel would be easier for sharing a larger reproduction artifact, we can arrange that too. Just let us know what would be most useful.\n\n## Workaround (for anyone hitting this)\n\nDisable `deadCodeInjection` (it adds little real reverse-engineering resistance) and/or pin a fixed non-zero `seed` so builds are reproducible and a latent miscompile can't surface only in some releases.\n","author":{"url":"https://github.com/julianmesa-gitkraken","@type":"Person","name":"julianmesa-gitkraken"},"datePublished":"2026-07-07T11:57:05.000Z","interactionStatistic":{"@type":"InteractionCounter","interactionType":"https://schema.org/CommentAction","userInteractionCount":7},"url":"https://github.com/1423/javascript-obfuscator/issues/1423"}
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:38a03c89-edb5-45f4-0b5d-9abb4cd258d5 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | 976A:B3B5C:11AB123:1836484:6A62DEDA |
| html-safe-nonce | 88cd3c3051639652a3b916fc67db21a51b57ee92d0044f5555de027750d0b7fd |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiI5NzZBOkIzQjVDOjExQUIxMjM6MTgzNjQ4NDo2QTYyREVEQSIsInZpc2l0b3JfaWQiOiIzMDc4Mjk3MTI2OTA2ODE0MTcwIiwicmVnaW9uX2VkZ2UiOiJpYWQiLCJyZWdpb25fcmVuZGVyIjoiaWFkIn0= |
| visitor-hmac | 2ba26a00a3250f57a209c091b8ac3296c86d100494c3067e3d10a9cca7ec1a76 |
| hovercard-subject-tag | issue:4828248752 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/javascript-obfuscator/javascript-obfuscator/1423/issue_layout |
| twitter:image | https://opengraph.githubassets.com/ae22ab3733e934adcbe6611cdf7a10f2e6680972141b60309f695f7b60cf9cc1/javascript-obfuscator/javascript-obfuscator/issues/1423 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/ae22ab3733e934adcbe6611cdf7a10f2e6680972141b60309f695f7b60cf9cc1/javascript-obfuscator/javascript-obfuscator/issues/1423 |
| og:image:alt | Summary With deadCodeInjection: true on already-minified input (bundler output where locals are 1-char identifiers, i.e. effectively the same as identifierNamesGenerator: 'mangled'), a positional a... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | julianmesa-gitkraken |
| hostname | github.com |
| expected-hostname | github.com |
| None | df33b1b61ee7b9a0af988199bfc3503c9c1acafb1f1d40e1f140ea7c84f890dd |
| turbo-cache-control | no-preview |
| go-import | github.com/javascript-obfuscator/javascript-obfuscator git https://github.com/javascript-obfuscator/javascript-obfuscator.git |
| octolytics-dimension-user_id | 23015672 |
| octolytics-dimension-user_login | javascript-obfuscator |
| octolytics-dimension-repository_id | 58360147 |
| octolytics-dimension-repository_nwo | javascript-obfuscator/javascript-obfuscator |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 58360147 |
| octolytics-dimension-repository_network_root_nwo | javascript-obfuscator/javascript-obfuscator |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | d41cd1bdb290013455c0ac430fa755621733f5eb |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width