DepWall

Home / Blog

  • ai agents
  • supply chain
  • verdicts

A browser extension cannot block an install, and should not imply it can

We shipped a DepWall extension for npm and PyPI pages. It reports; it does not block — the badge says would, the manifest description says does not block, and a test asserts both.

DepWall now has a browser extension. It puts a verdict card on npm and PyPI package pages, checks anything you right-click, and — if you turn it on for a site — underlines npm install … lines in READMEs and answers with the verdict beside them.

It does not block anything, and it is written so that it cannot start pretending to.

The distinction is the product#

DepWall's claim is that it gates the install at the shell, through PATH shims, where an agent cannot route around it. That claim is worth something precisely because of where the gate sits. A browser extension sits beside a page a human is reading. It has no position in the install path, so there is nothing for it to stop.

An extension that implied the same guarantee from a browser tab would be selling the claim without the mechanism. So the badge says what the gate would return, the manifest description says it reports; it does not block, and a test asserts the description still contains that phrase. Copy that carries a promise is copy worth testing.

Three things and only three things leave the browser#

An ecosystem, a package name, and a version. That is the entire payload.

No page content, no URLs, no query strings, no fragments, no browsing history. No cookies — the lookup is credentials: "omit". Permissions are storage, contextMenus, activeTab, scripting, and one host: our own verdict endpoint. Not tabs, which would be standing access to every URL in every window.

Those are assertions in the test suite rather than promises in a README, because the CLI's privacy claim is that only public package coordinates reach the cloud. An extension that widened that would make the claim false for every DepWall user, not just the ones who installed the extension.

Reading a page is a different permission from checking a package#

The page scanner is the feature people ask for first: notice npm install left-pad anywhere on the web and put the verdict next to it. It is also the feature that requires reading the page, which is a categorically different thing from checking a package you are already looking at.

So it is off by default, granted per site, and the wildcard host lives in optional_host_permissions rather than host_permissions. A test asserts it never moves — if it did, installing the extension would silently grant read access to the entire web.

Two details that took a while to get right:

  • Detection only looks inside code. <pre>, <code>, and the highlighted blocks GitHub and friends render. Prose is where false positives live — "you should install express" is a sentence, not a command, and underlining every mention of a popular package would make the page unreadable and the extension the first thing anyone turns off.
  • ALLOW hits are unwrapped. A mark on every install command meaning "checked, fine" is noise that makes the real ones harder to see. Only ASK and BLOCK are ever marked.

An unreachable service is not a clean result#

If the lookup times out or fails, the badge says so in those words. It never shows anything that could be read as "fine".

This is the same fail-mode rule the CLI holds — unknown plus unreachable resolves to ASK, never to a silent ALLOW — applied to a surface that cannot enforce anything. The extension has no power to stop you, so the least it can do is not tell you a comforting thing it does not know.

Where the content script does not fetch#

The content script holds no fetch, on purpose. It runs in the page's world, where a request would be visible to the page and usable by it as a proxy to an origin it could not reach itself. The network call lives in the background worker, which is also the only place package coordinates are validated.

Package names and finding messages are attacker-chosen text rendered onto a page an attacker may also control, so the badge is built with textContent and createElement only. There is no innerHTML anywhere in the extension, and a test greps for it — after stripping comments, because the first version of that test matched its own explanatory comment.

What it is good for#

The moment a human is deciding whether to add a dependency: reading a package page, about to copy an install command out of a blog post or an answer from a model. That is a real moment and nothing was watching it.

It is just not the moment where anything gets stopped. The command line is still what enforces.