Is Smoothwall Cloud Filter safe?

High risk

Smoothwall Cloud Filter is high risk. On every page, Smoothwall Cloud Filter copies the full HTML (up to 256KB), incl. iframes, to its background script every 5 seconds. It backs content-filtering but fires unconditionally, reading email previews into worker memory.

Smoothwallv3.2.5Chrome Web Store
72Risk
Who publishes it

Smoothwall Ltd - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
Smoothwall
Declared legal entity
Smoothwall Ltd
Registered address
2nd Floor, 2 Whitehall Quay, Leeds LS1 4HR, GB
Registered contact
Francois Nitenberg

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Full DOM of Every Page Captured Even When Unprovisioned

On every page, Smoothwall Cloud Filter copies the full HTML (up to 256KB), incl. iframes, to its background script every 5 seconds.

It backs content-filtering but fires unconditionally, reading email previews into worker memory.

Severity
High unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

You load any web page, a webmail thread, an intranet wiki, a Google Doc, a banking dashboard, in a tab or iframe.

The capture also fires inside every embedded iframe on the page, not just the top-level document.

The extension did this

Smoothwall's content script reads the page's rendered HTML and forwards it to its service worker, then re-sends the live DOM every 5 seconds while the tab is open.

It does this even when the extension has no Smoothwall tenant configured; there is no policy gate, no opt-in, and no on-screen indicator that DOM capture is happening.

What every 'page-content' message carries from your tab to the Smoothwall service worker
  • The URL of the page
    https://mail.example.org/mail/u/0/#inbox/FMfcgzGwJVNzPxRBLstqNGZQVnxZRkbV

    The exact address of the page or iframe you are looking at, including any query parameters and fragments.

  • The full HTML of the page
    <html lang="en"><head><title>Inbox (3) - alice@example.org</title>...</head><body><div class="thread"><div class="msg" data-from="hr@example.org">Subject: Q3 compensation review – please review attached salary band changes before Friday...</div></div></body></html>

    Up to 256,000 chars of the live DOM: visible text, form values, hidden fields, draft emails, wiki content, previews, anything rendered.

  • The page title
    Q3 Compensation Review – Confidential – Google Docs

    The browser tab title, often more revealing than the URL (e.g. 'Inbox (3), alice@example.org').

When this fires

Every 5 seconds

Once a page finishes loading, the content script immediately sends one DOM snapshot, then keeps sending a fresh snapshot every 5 seconds until the tab is closed or navigates away. Single-page apps (Gmail, Notion, Office 365) therefore stream a continuous timeline of their evolving DOM state.

The code that does this

The DOM-capture loop, exactly as shipped in v3.2.3.

Readable version

5-second capture interval

// Once readyState is non-'loading', start the periodic capture.// First snapshot is sent immediately; further snapshots fire every 5s.startRepeatedlySendingContentToExtension(intervalMs = 5000) {  this.stopRepeatedlySendingContentToExtension();  this.sendContentToExtension();                       // initial snapshot  this._contentSendInterval = setInterval(() => {    this.sendContentToExtension();                     // every 5 seconds  }, intervalMs);}

DOM read + intra-extension send

// Reads up to 256KB of live HTML and posts it to the service worker.// No allow-list / deny-list / tenant check — runs on every page,// in every frame, whether or not the extension has any policy.sendContentToExtension(overrideHtml = null) {  try {    const html =      overrideHtml !== null        ? overrideHtml        : document.querySelector('html')?.outerHTML.substring(0, 256_000) ?? '';    chrome.runtime.sendMessage(      chrome.runtime.id,      new PageContentMessage(        window.location.href,   // exact URL (path + query + fragment)        html,                   // up to 256KB of rendered DOM        document.title          // tab title      ),      this.handleMessageFromExtension    );  } catch (e) {    console.error('Failed to send content to extension');    console.error(e);  }}

PageContentMessage payload shape

// The exact object that crosses the content-script → service-worker boundary.class PageContentMessage extends Message {  constructor(url = '', content = '', title = '') {    super(PageContentMessage.type);    this.url     = url;       // window.location.href    this.content = content;   // outerHTML[0..256000]    this.title   = title;     // document.title  }}PageContentMessage.type = 'page-content';

Manifest content_scripts entry

// Injects content-script.js into EVERY frame on EVERY origin// at the earliest possible moment (document_start)."content_scripts": [{  "matches":    ["*://*/*"],   // every http and https URL  "js":         ["./content-script.js"],  "run_at":     "document_start",  "all_frames": true            // also runs inside every iframe}]
Confirmed: capture fires even with no Smoothwall tenant configured

We loaded the extension into a fresh Chrome profile with **no managed-storage Serial / TenantId** and navigated to five third-party sites (google.com, amazon.com, wikipedia.org, reddit.com, youtube.com).

- The service worker logged `Serial ID is missing from policy` and `No provisioning info found in cache`. - Despite that, **20 `Page content received from tab X, frame N` log entries** were emitted by `FilterController._onPageContentMessageReceived` across the five navigations. - Frames `0, 7, 8, 9, 11, 12, 13, 16` all participated, confirming `all_frames: true` is honoured. - `Re-scanning URL for tab X` entries interleaved, confirming the captured outerHTML was being driven into the URL re-scan pipeline.

In other words: the DOM capture is **not** gated on the extension being provisioned for an organisation. Every installed user — including individuals who may have installed the extension by mistake or evaluators looking at the listing — has their full page DOM read out and shipped to the service worker on every page, in every frame, every 5 seconds.

Reproduce it yourself

Run this in the Smoothwall service-worker DevTools console to log every 'page-content' message Smoothwall receives, including the originating tab, frame, URL, title, and a length + first-200-character preview of the captured HTML. Lets you verify on your own machine that the DOM is being read on every page, in every frame, every 5 seconds, even when no Smoothwall policy is configured.

Requires
  • Chrome 110+ with Developer mode enabled in chrome://extensions
  • Smoothwall Cloud Filter installed (any version 3.2.x)
  • Permission to open the extension's service-worker DevTools
smoothwall-page-content-tap.js · js
// smoothwall-page-content-tap.js// Paste into the DevTools console of Smoothwall's service worker://   chrome://extensions → enable Developer mode → find//   'Smoothwall Cloud Filter' → click 'service worker' link.//// It wraps chrome.runtime.onMessage so any 'page-content' message// the extension receives is logged with sender + payload preview.(function () {  const tag = '[smoothwall-tap]';  if (globalThis.__smoothwallTapInstalled) {    console.warn(tag, 'already installed');    return;  }  globalThis.__smoothwallTapInstalled = true;  let count = 0;  chrome.runtime.onMessage.addListener((msg, sender) => {    if (!msg || msg.type !== 'page-content') return; // ignore non-DOM messages    count += 1;    const html = typeof msg.content === 'string' ? msg.content : '';    console.log(      tag,      `#${count}`,      `tab=${sender.tab && sender.tab.id}`,      `frame=${sender.frameId}`,      `url=${msg.url}`,      `title=${JSON.stringify(msg.title)}`,      `htmlBytes=${html.length}`,      `preview=${JSON.stringify(html.slice(0, 200))}`    );    // Returning nothing — we are observing, not blocking.  });  console.log(tag, 'installed. Open or reload any tab and watch this console.');})();
How to run it
  1. 1Open chrome://extensions, enable Developer mode.
  2. 2Open 'service worker' DevTools for Smoothwall.
  3. 3Paste script into Console.
  4. 4Visit sites.
  5. 5See one entry per load, one per 5s.
  6. 6Repeat unenrolled; captures still appear.
Where the captured DOM goes
    • (stays inside the extension)

    The DOM payload goes to Smoothwall's worker via sendMessage, for filtering. Not exfiltrated on its face, but worker code can read it; a bug could expose every page DOM rendered.

Updated 30 September 2026jbldkhfglmgeihlcaeliadhipokhocnm