Most Electron apps ship their JavaScript, HTML and assets in a single file called app.asar, found in the app’s resources folder. ASAR is a simple archive format: a JSON header that describes a directory tree, followed by the file contents stored back to back, uncompressed. Inside are the three parts that define an Electron app’s security model: the main process code, which has full Node.js access; the preload scripts, which decide what the web page may reach; and the renderer pages. They talk to each other over IPC channels.
This tool opens an app.asar in your browser, lists every file, lets you download or preview any packed file, and builds a table of the IPC channels that the code registers and calls. The archive is never uploaded.
What it does
Input. An ASAR archive, usually resources/app.asar. Only the 16-byte prefix and the JSON header are read up front; file contents are read on demand, so a large archive costs little more than its header. Limits: a header of up to 32 MiB, up to 1,000,000 entries, directory nesting up to 128 levels, and a two-minute time limit.
Archive. You get:
- File tree: every file, directory and symbolic link as a collapsible, searchable tree with sizes. The JSON export adds each file’s offset, the executable flag and the per-file
integrityrecord (algorithm and hash) when the archive has one. - Statistics: counts of files, directories, links and unpacked files, total size, a breakdown by file extension, and the 20 largest files.
- Download and preview: any file that is stored inside the archive can be downloaded on its own or shown as text, without extracting the rest. Links are followed.
app.asar.unpackedentries: files marked as unpacked are listed, but their bytes are not in the.asarfile; Electron keeps them in theapp.asar.unpacked/folder next to it. Native modules are typically stored there.
Electron analysis. The report adds:
- package.json:
name,versionandmain. The main entry is resolved the way Node does it, withindex.jsas the default. - Preload scripts: string values assigned to a
preloadproperty, as inwebPreferences: { preload: path.join(__dirname, 'preload.js') }, resolved to files in the archive, with the file and line where each one is configured. - Renderer entries:
<script src>tags in the archive’s HTML files, resolved to archive paths. Remote URLs are ignored. - IPC channel table: every call to
ipcMain.handle,ipcMain.on,ipcMain.once,ipcRenderer.invoke,ipcRenderer.send,ipcRenderer.sendSync,ipcRenderer.onandwebContents.send(including forms such aswin.webContents.send). Each row shows the channel, the API, the direction (renderer to main, or main to renderer), the process, and thefile:line. When the channel is not a plain string literal, the row is marked dynamic and shows the expression instead. - contextBridge exposures: each
contextBridge.exposeInMainWorld(name, { ... })with the exposed name and the keys of the API object. This is the exact surface the web page can call. - Native modules:
.nodefiles (Node add-ons compiled to Mach-O, ELF or PE), with their size and whether they are unpacked. - Source maps:
.mapfiles in the archive and the number of scanned JavaScript files that end with asourceMappingURLcomment. - Warnings: entries with suspicious names (
.., slashes, absolute paths) or links that point outside the archive, malformed entries, files that extend past the end of the archive, and JavaScript files that were only partly scanned.
How it works
The archive starts with two Chromium “pickle” records: the first gives the size of the header, the second holds the header as a JSON string. The page checks that these sizes agree, parses the JSON, and walks its files tree. A packed file is located at the start of the data area plus its offset, which ASAR stores as a decimal string. The format handling was checked against @electron/asar 4.3.1, and the prefix checks follow REA, the open-source project by morluto (MIT).
For the IPC table, every .js, .cjs and .mjs file in the archive is scanned with a lightweight JavaScript tokenizer, application files first and node_modules last. The tokenizer skips comments, string contents and regular expressions, so text such as "ipcMain.handle('x')" inside a string is not reported. It then matches member calls such as ipcRenderer.invoke( and reads the first argument. A channel counts as literal only when that argument is a single string or a template without substitutions.
To decide which process a file belongs to, the page starts from the main entry, the resolved preload scripts and the renderer scripts, and follows relative require and import paths. A file that is not reached this way gets its process from the API it uses: ipcMain and webContents exist only in the main process, and a file that calls contextBridge is treated as a preload. Otherwise the process is shown as unknown.
How your archive stays private
The archive is not uploaded:
- Parsing, scanning and each file download run in a Web Worker on your device. The worker reads only the header and the slices it needs through the browser’s file APIs.
- Analytics events are coarse: success or an error code, a coarse range for the number of files found (such as
11-100), a file-size bucket and a duration bucket. They never contain the file name, file paths, channel names or any other content. The full list is on the privacy page. - The tool page shows no ads.
How to verify this yourself
- Open your browser’s developer tools and switch to the Network tab before you choose a file. Analyze an archive and download a file from it. The only requests are page assets and the worker script
/_astro/asar-worker-*.js; none carries your archive. - Or load the page, go offline (turn off Wi-Fi, or tick Offline in the Network tab) and analyze a file. It still works.
- The worker runs under the Content Security Policy
default-src 'none'; script-src 'self'; connect-src 'none', so the browser does not let it make any network request.
Limitations
- Tokenizer, not a full parser. Calls are recognized by the literal names
ipcMain,ipcRendererandwebContentsfollowed by a method. Aliases are not followed:const { ipcRenderer: r } = require('electron'); r.send(...)orconst r = ipcRendereris missed, and so is a computed call such asipc[name](...)oripcMain['handle'](...). Minified bundles that rename these objects lose most rows. - Only the listed APIs. Methods such as
ipcRenderer.once,ipcRenderer.postMessage,ipcRenderer.sendToHost,ipcMain.removeHandlerand MessagePort-based channels are not in the table. - Dynamic channels stay dynamic. A channel built from a variable is shown with its expression, not resolved to a value.
- Scan budget. Only the first 4 MiB of each JavaScript file are scanned, and at most 128 MiB in total. Files beyond the budget, usually in
node_modules, are listed in a warning. - What is not scanned. Unpacked JavaScript (its bytes are outside the archive), inline
<script>blocks in HTML, TypeScript sources and package.json or HTML files larger than 1 MiB. - Integrity is shown, not verified. The JSON export carries each file’s integrity hash from the header, but the page does not recompute it, and it does not check Electron’s ASAR integrity fuse or the hash stored in the app bundle.
- Process attribution is heuristic. Bare package imports are not followed, so code reached only through a package may show as unknown.
- Native code is out of scope.
.nodefiles are listed; analyze them with a native tool such as Ghidra.
FAQ
Can I extract the whole archive?
The page downloads files one at a time. For a full extraction, use npx @electron/asar extract app.asar out/ on your own machine.
Why can’t I download a file marked unpacked?
Its bytes are not inside the .asar file. Look in the app.asar.unpacked/ folder next to it.
Why is a channel shown as dynamic?
The first argument is not a single string literal, for example `user:${id}` or a constant defined elsewhere. Search the file at the given line to see what it resolves to.
The IPC table is empty, but the app clearly uses IPC.
The code is probably minified with renamed objects, or the IPC calls live in an unpacked or oversized file. Check the warnings and the scan statistics.
Does it find security problems?
It maps the attack surface: exposed APIs, channels and their handlers. Whether a handler validates its input is for you to read in the code.
Compliance
Analyze apps you built, apps you are authorized to assess, or open-source apps. Respect the licenses of the software you inspect, including any terms that restrict reverse engineering, and the laws that apply to you. The tool gives no advice on bypassing licensing or other protections. See the disclaimer.
Related guides
- Reverse Engineer an Electron App with REA: ASAR, Preload, IPC: the full workflow from
app.asarto the IPC handlers, with an AST-based analysis from the command line. - Trace Website API Calls with REA: Browser, HAR and Bundles: many Electron apps call web APIs; capture them and summarize them with the HAR analyzer.
- WASM to WAT and WebAssembly analyzer: for
.wasmmodules you find inside the archive. - Unpack an ASAR File: Extract and Inspect an Electron App: where to find
app.asaron each platform, full extraction withnpx @electron/asar, and what to read first.