Compare the DOM of two browser windows by using graph comparison. JavaScript port of the corresponding AutoLeak functionality.
A web-based toolkit for capturing and diffing full JavaScript/DOM object graphs between two controlled page loads. It helps you see exactly what changed when a resource is included via different methods (for example iframe, script, img, object, CSP variants) or when serving different file types. No backend required, everything runs in the browser.
Attribution: Ported from Python 3 to JavaScript by Din Asotic in the context of the CASA project BOWSER. Graph comparison algorithm and XS-Leak test cases based on the AutoLeak CCS'23 Paper by Dominik Trevor Noß, Lukas Knittel, Christian Mainka, Marcus Niemietz, Jörg Schwenk (https://autoleak.org/paper.pdf).
- Run controlled experiments: Pick an inclusion method and file type, and run two pop-ups (State 0 / State 1) to load and crawl the target. The graphs are captured and returned to the main UI automatically. You can download JSON graph files upon their creation and inspect them manually.
- Deep-diff the captured states: The UI computes and displays path-level differences (nodes/edges/props), with search, filters, tags, and CSV export.
- Sandbox real sites: Open two regular windows, navigate to any URLs, paste the provided “Vendor crawler” snippets, and diff the results. This allows users to investigate live behavior.
- Open the web link to the platform (link/placeholder).
- In Controls, pick:
- Inclusion method
- File type
- Difference
- Under "Advanced" you can choose between opening pop-ups or new tabs (regular windows).
- Click Run. Allow pop-ups if prompted. Two small pop-ups will open (State 0 and State 1).
- Wait a moment. When both graphs arrive, the Results section shows the diff. You can:
- Search/filter results
- Toggle blocklist
- Export CSV
- Download each state’s JSON graph
Tip: Use “Copy test link” to share a pre-configured run.
You can diff any two real pages with the sandbox mode.
- In Advanced -> Sandbox, click Open two sandbox windows.
- In each window, navigate to your target URL(s).
- Get both pages into the states you wish to get difference of.
- Open DevTools and Console in each window, and paste the corresponding Vendor crawler snippet (State 0 / State 1) from the main UI.
- The graphs are sent back to the UI and diffed automatically.
NOTE If messaging is blocked, use “Paste JSON State …” in the UI and paste the full message the snippet copied to your clipboard.
The sandbox snippets set CRAWLER_CONFIG and load the vendor crawler script. They post back a { type:'CRAWL_RESULT', sid, state, graph } message to the main UI.
cd existing_repo
git remote add origin https://git.noc.ruhr-uni-bochum.de/NDS/code/graphdiff.js.git
git branch -M main
git push -uf origin main
- Main UI (
index.html) controls runs, renders results, and manages filters/tags/exports. It loads difference presets from/testconfigs/*.json. - App logic (
app.js) opens two pop-ups, tracks jobs, normalizes/sanitizes graphs, and kicks off the diff worker. It also registers a Service Worker (for diff configuration and perf). - Runner (
popup/runner.html) lives in each pop-up. It loads the selected inclusion method and the vendor crawler, intercepts the crawler’s upload call, captures the full graph, and posts it back to the opener. If same-origin, it severswindow.openerto avoid traversal noise and falls back toBroadcastChannel.
- A modern browser with pop-ups allowed for the site.
- No server is required as this is a static web app.
- Service Worker runs on secure contexts (
HTTPSorhttp://localhost). The app works without it, but SW enables extra conveniences (among them, diff config passing).
Although you can use the hosted link, you can also run locally:
#from the project root
# Option A: Python
python3 -m http.server 8080
# Option B: Node
npx serve -l 8080
Then open http://localhost:8080/ (or any other chosen port) in your browser.
The app automatically registers
./sw.jswith scope./(secure contexts only). If you don’t see diff presets, ensure the site was served from a static server (notfile://).
Best practice is to host the analysis view and runner on different origins (different port/domain/protocol). The runner targets postMessage to the analysis origin (inferred from document.referrer or an explicit analysisOrigin query param).
It is also possible, if you keep everything on one origin, the runner severs window.opener and uses BroadcastChannel to deliver results, mitigating traversal noise. This is automatic so no config is needed.
the UI currently opens
./popup/runner.html?...for each pop-up. If you want to hostpopup/on a separate origin in production, point that URL to the absolute runner location (for example by adjusting the base URL used inapp.jswhen opening the popups). The runner already honors ananalysisOriginparameter for strictpostMessagetargeting.