demo · v150

Security Audit Tool

Before Chrome 150, a data: URL worker inherited the page's origin — letting it join BroadcastChannel, read localStorage, and access IndexedDB as if it were part of the page. This was a security gap: a data: URL should be isolated. This tool audits the security implications by spinning up a real data: URL worker and probing exactly which cross-origin channels it can reach.

Pre-Chrome 150 — security gap
self.origin returns the page origin ("https://…"), not null. VULN
BroadcastChannel('shared-bus') joins the page's channel — worker can eavesdrop on all messages. VULN
localStorage.getItem(key) reads first-party storage from inside an untrusted data: blob. VULN
indexedDB.open('mydb') succeeds — can read and write the page's IDB database. VULN
Chrome 150 — opaque origin fix
self.origin returns "null" — opaque origin, no real origin string. FIXED
BroadcastChannel is scoped to the null origin — cannot communicate with the page. FIXED
localStorage access throws SecurityError — null origin has no storage bucket. FIXED
indexedDB.open() succeeds but opens an isolated database for the null origin — cannot reach the page's IDB. FIXED
postMessage still works — same-thread communication is unaffected. UNCHANGED

Live audit results

The attack pattern the fix prevents

// Attacker injects malicious code via a data: URL worker.
// Pre-Chrome-150: the worker inherits the page's origin.

// Malicious worker code (inside data: URL):
self.onmessage = () => {
  // COULD read all localStorage keys (pre-150):
  const secrets = JSON.stringify(localStorage); // VULN pre-150

  // COULD join origin BroadcastChannel:
  const bc = new BroadcastChannel('auth-events');  // VULN pre-150
  bc.onmessage = e => exfiltrate(e.data);

  // COULD read IndexedDB:
  const req = indexedDB.open('user-data');         // VULN pre-150
  req.onsuccess = e => exfiltrate(e.target.result);
};

// Chrome 150: all of the above fail or are isolated.
// self.origin is "null". BroadcastChannel is scoped to null origin.
// localStorage throws SecurityError. IDB opens null-origin database.
// Migration: replace data: URL workers with Blob URL workers:
const blob = new Blob([workerCode], { type: 'text/javascript' });
const worker = new Worker(URL.createObjectURL(blob));

see also

implementation reference

Need the exact API surface, compatibility boundaries, errors, lifecycle, and source links? Read the matching gendn reference ↗