bundle-breaker is a CLI and JS API to make reverse-engineering bundled JavaScript applications easy and accessible. This can comprise of the separation, grouping, naming, pruning, deobfuscation and rebundling of production-built JavaScript applications.
- CLI & JS API - Exposes a robust JS API as well as a simple, user-friendly CLI for creating and modifying reverse-engineering projects
- Well-tested - Every change is tested against a suite of up-to-date generated bundle configs
- Multi-functional - Supports a wide range of operations beyond debundling to make your reverse-engineered bundle dramatically easier to understand (see below for details)
The core function of bundle-breaker is to debundle, or break up, a bundled JS application into individual files each containing a singular module. This is called debundling and serves as the entry point for all interfaces with bundle-breaker.
CLI:
npx bundle-breaker -c path/to/bundle ./outWhen used via the CLI, bundle-breaker will write your debundled application to specified directory. It will have the following structure:
out/
├─ modules/
│ ├─ module_1.js
│ ├─ module_2.js
│ ├─ module_3.js
├─ index.js
├─ chunk_1.js
├─ chunk_2.js
├─ ...extra metadata depending on options/input bundle (e.g. graph.gexf, module_mapping.js etc.)...
JS API:
import { debundle } from "bundle-breaker";
const files = { "index.js": "...", "chunk.js": "..." };
const deb = debundle(files);
// use debundle API as needed, for example; logging its unique identifier
console.log(deb.getId());bundle-breaker supports a handful of deobfuscation codemods you can apply to reverse common minification strategies and make your debundled code more readable. All of these codemods are safe and should never change the functionality of the code. Examples of the supported processors are; unminifying boolean literals, to flipping literals and identifiers in if statements, breaking sequence expressions into individual statements, and many more. See the full list of support techniques here.
JS API:
import { debundle } from "bundle-breaker";
const files = { "index.js": "...", "chunk.js": "..." };
const deb = debundle(files);
// opt-out of a given deobfuscator
const deobOpts = { flipLiterals: false };
deb.deobfuscate(deobOpts);CLI:
npx bundle-breaker -cd path/to/bundle ./outNote that the CLI only offers deobfuscation as an all or nothing deal. If you need more fine-grained control over which deobfuscation techniques will be applied you amy opt for the JS API.
bundle-breaker can traverse the modules produced in the debundling step to build a module graph.
CLI:
npx bundle-breaker -cg path/to/bundle ./outWhen using the CLI you can simply append the -g or --graph option to include a .gexf graph file in your output directory. This can be loaded into a variety of graph-analysis or visualization libraries usually with minimal transformation. However, one way to quickly visualize your module graph without writing any code is to load it into Gephi Lite.
JS API:
import { debundle } from "bundle-breaker";
const files = { "index.js": "...", "chunk.js": "..." };
const deb = debundle(files);
const graph = deb.graph();
console.log(graph.order, graph.size);When using the JavaScript API deb.graph() will return a graphology Graph object to interact with. You can use this as you would normally e.g. performing layouts, computing SNA metrics etc.
bundle-breaker supports renaming/remapping your file names to something more legible. If you have a set of known file name mappings to hand you can manually pass them to the application. This will also update any import/require statements your chosen bundler may use; making your debundled codebase easier to traverse manually.
CLI:
npx bundle-breaker -c -f path/to/file-name-map.json path/to/bundle ./outJS API:
import { debundle } from "bundle-breaker";
const files = { "index.js": "...", "chunk.js": "..." };
const fileRenames = { "abc123.js": "foo-bar.js", "...": "..." };
const deb = debundle(files);
deb.updateNames(fileRenames);TODO
TODO
TODO
The tool aims to work with to the majority bundler configurations and versions. Notably this includes:
- Webpack 4 and 5
- All
webpack.idsplugins - Bundles split across multiple bundles
- Runtime-only chunks
- ... and (hopefully) everything between!
If you find a config that doesn't work as you'd expect and the relevant option(s)/version(s) are not listed in the known limitations below please raise an issue.
| Bundler | Date added | Limitation |
|---|---|---|
| WP5 | 09/05/24 | Multi-chunk bundles with any output.chunkFormat besides 'array-push' |
-
Clone the repo
-
pnpm install(with thepnpmversion specified inpackage.json) -
Build all the examples with:
pnpm examples:build-all
Alternatively, to re-build individual examples (e.g.
webpack4_47-simple):pnpm examples:build webpack4_47-simple
-
Start the local dev build with:
pnpm dev
-
Test everything is working with a simple debundle by running:
npx . examples/webpack4_47-simple/out out