https://developer.chrome.com/docs/webstore/api (the older api_index path redirects there) — the Chrome Web Store API publishes and manages the developer's own item; there is no listing endpoint. Fetched 2026-09-02.This is an old revision of the document!
This page is about browser extensions as the object of a measurement: what they leak, what they can be made to do, whether a page can tell which ones you have installed, and how you get a corpus of them in the first place. It is not about uBlock Origin as a classifier — that is Requests and Filter lists — and it is not about the browser's own anti-tracking machinery, which is Browser protection.
The distinction matters because the corpus does not respect it. Of the 218 papers in the seven-venue corpus that a candidate sweep pulls out as possibly being about extensions, 92 turn out to be papers that built an extension to collect their data or to ship their defence, and 68 are papers that measured extensions. If you search for “browser extension” you will mostly find the first kind.
Four things about the Chrome Web Store, measured on 2026-09-02, that will cost you a crawl if you assume otherwise.
https://chromewebstore.google.com/sitemap, which on this date carried 42 shards (lastmod 2026-09-01) and 356,984 distinct item IDs. Those are items, not extensions: unpacking the CRX for a deterministic 200-item sample gives 146 extensions, 44 themes and 8 legacy Chrome apps, and a separate 40-item sample, taken from the same list on the same day, found 2 IDs with no live detail page./detail/<id> returns 200 for an ID that does not exist, because the store is a single-page app that renders a generic shell. The signal that does work is the <title>: a live item is titled “Name - Chrome Web Store”, a missing one is titled exactly “Chrome Web Store”. A status-code check admits every delisted extension in your seed list.cjpalhdlnbpafiamejdnhcphjbkeiagm) is no longer in the sitemap and its detail page is now a soft-404; uBlock Origin Lite (MV3) is still there. Any seed list, any replication, any “top extensions” sample older than that is measuring a store that no longer exists.clients2.google.com/service/update2/crx still serves packed extensions, but returns 204 No Content with an empty body when prodversion is not a plausible four-part Chrome version. Passing 120.0 — the form in most tutorials — produces an empty corpus that looks like a network fault.The script that establishes all four, with a self-test that fails if the existence check is stubbed out, is in Building a corpus of extensions.
The population is defined by a rule written before the first count, and by a hand verdict on every candidate; both are in scripts/ext_fold.mjs and the reasoning is on browser_extensions. A paper is in scope if it produces a reported finding about extensions — their code, their store listings, their permissions, their effect on a page, the set a user has installed, or the people who write and use them. A paper that only ships an extension is out.
| Role | Papers | Share of 218 candidates | In scope |
|---|---|---|---|
instrument — the extension is the paper's own tool or the vehicle for its defence | 92 | 42.2% | no |
object — extensions are the primary object of study | 47 | 21.6% | yes |
mention — cited or discussed only | 38 | 17.4% | no |
object-partial — extensions are one measured component among several | 21 | 9.6% | yes |
homograph — a different sense of “extension” (IPv6 extension headers, file extensions, WeChat mini-programs, Deno modules) | 13 | 6.0% | no |
attack-vector — a malicious extension is the assumed attacker, no real extensions measured | 5 | 2.3% | no |
corpus-source — extensions are a sample source for a study of something else (WebAssembly binaries) | 2 | 0.9% | no |
68 papers are in scope — 1.2% of the 5,859-paper corpus. In absolute terms the topic has not grown much (9 papers in 2010–2013, 14 in 2014–2017, 19 in 2018–2021, 15 in 2022–2024, 11 in the provisional 2025–2026 slice) and as a share of a corpus that quadrupled it has shrunk: 1.8%, 1.8%, 1.3%, 0.8%, 0.9%.
It is a USENIX Security topic: 24 of the 68 are there (35.3%) against USENIX's 24.1% share of the corpus as a whole, and 12 each at IEEE S&P and TheWebConf, 8 at CCS, 6 at NDSS, 5 at PoPETs and 1 at IMC — which is 1.5% of the in-scope papers against IMC's 10.9% of the corpus. If you are used to reading IMC for measurement papers, this is not where this literature is.
| Question | Papers | Share of 68 | 2010–13 | 2014–17 | 2018–21 | 2022–24 | 2025–26* |
|---|---|---|---|---|---|---|---|
| Is the extension itself vulnerable, and what does that give an attacker? | 18 | 26.5% | 5 | 4 | 4 | 3 | 2 |
| Can a page tell which extensions you have installed? | 11 | 16.2% | 0 | 2 | 5 | 2 | 2 |
| Does the protective extension actually protect? | 11 | 16.2% | 1 | 0 | 5 | 2 | 3 |
| What does the extension send, and to whom? | 8 | 11.8% | 0 | 1 | 1 | 4 | 2 |
| Is the extension deliberately abusive, and can a store catch it? | 8 | 11.8% | 0 | 5 | 2 | 1 | 0 |
| What do the users and the developers of extensions know and do? | 7 | 10.3% | 0 | 1 | 1 | 3 | 2 |
| Does the extension ask for more than it needs? | 4 | 5.9% | 3 | 0 | 1 | 0 | 0 |
| What does an installed extension do to your measurement? | 1 | 1.5% | 0 | 1 | 0 | 0 | 0 |
(* 2025–2026 is provisional: CCS and IMC 2026 have not been held and IEEE S&P and TheWebConf 2026 are incompletely selected. See corpus.)
Two rows are worth staring at. Over-privilege was a 2010–2013 question and stopped: three of its four papers are from before 2014 and the fourth, TKPERM, is from 2020. Nothing since asks whether extensions request more permission than they use. The last row is one paper — [7Snyder, Peter; Ansari, Lara; Taylor, Cynthia; Kanich, Chris (2016): "Browser Feature Usage on the Modern Web", in: Proceedings of the ACM Internet Measurement Conference. (DOI)], IMC 2016 — and it is the only one that treats an installed extension as a confounder of the measurement rather than as its subject. That is a gap, not a consensus.
| Store | Enumeration | Per-item metadata | Package download |
|---|---|---|---|
| Chrome Web Store | XML sitemap only — 42 shards, 356,984 item IDs on 2026-09-02. No search or list API1) | scraped from the detail page; install counts are rounded — “16,000,000 users” for AdGuard AdBlocker, “200,000 users” for Consent-O-Matic. No precise figure appears anywhere in either response | clients2.google.com/service/update2/crx, undocumented, works, four-part prodversion required |
| Firefox AMO | public paginated JSON API: addons.mozilla.org/api/v5/addons/search/?app=firefox&type=extension reported count 99,677 on 2026-09-022) | the same API, with exact average_daily_users (10,766,677 for uBlock Origin), the full permissions and host_permissions arrays, the file hash and size | file.url in the API response |
| Microsoft Edge Add-ons | none — the REST API covers your own submissions only3) | — | — |
| Opera Add-ons | none found: robots.txt and sitemap.xml both 404 on 2026-09-02 | — | — |
| Safari Extensions | none. Distribution is through the App Store4) | — | — |
The asymmetry is the practical point. Firefox gives you a research API and exact numbers; Chrome gives you a sitemap and rounded ones. A study that needs install counts as a weight has better data on the smaller store, and [3Agarwal, Shubham; Fass, Aurore; Stock, Ben (2024): "Peeking through the window: Fingerprinting Browser Extensions through Page-Visible Execution Traces and Interactions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] says so in its own text: the Chrome Web Store “provides only inaccurate numbers for popular extensions (e.g., 1,000+)”, which is why its user-reach figures are lower bounds.
Mozilla has since added something Chrome has no equivalent of: a data_collection_permissions manifest key, surfaced on the API as current_version.file.data_collection_permissions. It is a machine-readable declaration of what the add-on collects. Do not assume it is populated. Measured on 2026-09-02 over the 400 most-installed listed Firefox extensions: 78.5% declared nothing (an empty array), 15.8% declared [“none”], and 5.8% named at least one data category. It is a real signal and it is currently a signal about 5.8% of the catalogue.
#!/usr/bin/env python3 """Build a browser-extension corpus from the two stores that let you. Neither store gives you a research API. What they give you is: Chrome Web Store an XML sitemap of item detail pages, and an undocumented CRX endpoint that still serves the packed extension. Firefox AMO a real, paginated, public JSON API. Three things this gets right that a naive scraper does not, each of which silently corrupts a corpus: 1. ``/detail/<id>`` answers **HTTP 200 for an id that does not exist**. The store is a single-page app and renders a generic shell. Existence has to be read out of the <title>: a live item is "<name> - Chrome Web Store", a missing one is exactly "Chrome Web Store". A status-code check silently admits every delisted extension in your seed list. 2. The CRX endpoint returns **204 No Content**, not an error, when ``prodversion`` is not a plausible four-part Chrome version. Tutorials pass "120.0"; that produces an empty corpus that looks like a network problem. 3. The sitemap enumerates **items**, not extensions: themes are in it too, and so are ids whose detail page no longer resolves. Counting <loc> lines and calling the result "the number of Chrome extensions" is wrong by about a quarter, in the direction that flatters the number. Stdlib only. Rate-limited by default, because the point is a reproducible corpus, not a fast one. python3 ext_corpus.py --self-test python3 ext_corpus.py --sitemap-count python3 ext_corpus.py --exists ddkjiahejlhfcafbddmgiahcphecmpfh python3 ext_corpus.py --crx ddkjiahejlhfcafbddmgiahcphecmpfh -o /tmp/ubol.crx python3 ext_corpus.py --amo-count """ import argparse import gzip import io import json import re import sys import time import urllib.error import urllib.request UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/140.0.0.0 Safari/537.36") # A four-part version from a real Chrome release train. A two-part string makes # the CRX endpoint answer 204 with an empty body. PRODVERSION = "140.0.7339.80" SITEMAP = "https://chromewebstore.google.com/sitemap" CRX = ("https://clients2.google.com/service/update2/crx" "?response=redirect&os=linux&arch=x86-64&os_arch=x86_64&nacl_arch=x86-64" "&prod=chromiumcrx&prodchannel=unknown&prodversion={pv}" "&acceptformat=crx2,crx3&x=id%3D{id}%26uc") AMO = "https://addons.mozilla.org/api/v5/addons/search/?app=firefox&type=extension&page={p}" ID_RE = re.compile(r"^[a-p]{32}$") TITLE_RE = re.compile(rb"<title>([^<]*)</title>", re.I) def fetch(url, timeout=40): """Return (status, body). A 204 comes back as (204, b'') rather than raising.""" req = urllib.request.Request(url, headers={ "User-Agent": UA, "Accept-Encoding": "gzip", "Cookie": "CONSENT=YES+", }) try: with urllib.request.urlopen(req, timeout=timeout) as r: body = r.read() if r.headers.get("Content-Encoding") == "gzip": body = gzip.decompress(body) return r.status, body except urllib.error.HTTPError as e: return e.code, e.read() def sitemap_ids(pause=0.3): """Every item id the Chrome Web Store sitemap lists. Yields (shard, id).""" status, body = fetch(SITEMAP) if status != 200: raise SystemExit(f"sitemap index returned HTTP {status}") shards = re.findall(rb"<loc>([^<]+)</loc>", body) if not shards: raise SystemExit("sitemap index carried no <loc> entries — the format changed") for shard_url in shards: u = shard_url.decode() st, b = fetch(u) if st != 200: print(f" WARN shard {u} returned HTTP {st}", file=sys.stderr) continue for loc in re.findall(rb"<loc>([^<]+)</loc>", b): m = re.search(r"/detail/(?:[^/]+/)?([a-p]{32})$", loc.decode()) if m: yield u, m.group(1) else: print(f" WARN unparsed sitemap URL: {loc.decode()[:100]}", file=sys.stderr) time.sleep(pause) def item_exists(ext_id): """True iff the Chrome Web Store has a live detail page for this id. NOT a status-code check: /detail/<nonexistent> is HTTP 200. """ if not ID_RE.match(ext_id): raise ValueError(f"not a Chrome extension id: {ext_id!r}") status, body = fetch(f"https://chromewebstore.google.com/detail/{ext_id}") if status != 200: return False m = TITLE_RE.search(body) if not m: return False title = m.group(1).decode("utf8", "replace").strip() return title != "Chrome Web Store" and title.endswith("Chrome Web Store") def download_crx(ext_id, prodversion=PRODVERSION): """Bytes of the packed extension, or None. Verifies the Cr24 magic number.""" status, body = fetch(CRX.format(pv=prodversion, id=ext_id)) if status == 204: raise RuntimeError( f"{ext_id}: HTTP 204 with an empty body. This is what the endpoint " f"returns for an implausible prodversion ({prodversion!r}) as well as " f"for an id it will not serve — the two are indistinguishable here.") if status != 200: return None if body[:4] != b"Cr24": raise RuntimeError(f"{ext_id}: served {len(body)} bytes that are not a CRX " f"(magic {body[:4]!r})") return body def amo_count(): """(count, first page of results) from Firefox AMO's public API.""" status, body = fetch(AMO.format(p=1)) if status != 200: raise SystemExit(f"AMO API returned HTTP {status}") d = json.loads(body) return d["count"], d["results"] def self_test(): """Three cases from the real store. All three must hold or the guard is vacuous. A test that only checks the positive case passes even when item_exists() is replaced by `return True`. """ cases = [ ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", False, "a fabricated id"), ("cjpalhdlnbpafiamejdnhcphjbkeiagm", False, "uBlock Origin: a Manifest V2 extension, removed from the store 2026-08-31"), ("ddkjiahejlhfcafbddmgiahcphecmpfh", True, "uBlock Origin Lite (MV3)"), ] ok = True for ext_id, want, why in cases: got = item_exists(ext_id) flag = "ok " if got == want else "FAIL" if got != want: ok = False print(f"{flag} item_exists({ext_id[:8]}...) = {got}, expected {want} # {why}") blob = download_crx("ddkjiahejlhfcafbddmgiahcphecmpfh") print(f"ok download_crx(uBOL) -> {len(blob)} bytes, magic {blob[:4].decode()}") try: download_crx("ddkjiahejlhfcafbddmgiahcphecmpfh", prodversion="120.0") print("FAIL a two-part prodversion should have raised") ok = False except RuntimeError as e: print(f"ok a two-part prodversion raises: {str(e)[:60]}...") n, results = amo_count() print(f"ok AMO reports {n} listed Firefox extensions; first result " f"{results[0]['slug']!r} with {results[0]['average_daily_users']} daily users") print("SELF-TEST", "PASSED" if ok else "FAILED") return 0 if ok else 1 def main(): ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) ap.add_argument("--self-test", action="store_true") ap.add_argument("--sitemap-count", action="store_true") ap.add_argument("--sitemap-ids", metavar="FILE") ap.add_argument("--exists", metavar="ID") ap.add_argument("--crx", metavar="ID") ap.add_argument("-o", "--out", metavar="FILE") ap.add_argument("--amo-count", action="store_true") a = ap.parse_args() if a.self_test: return self_test() if a.sitemap_count or a.sitemap_ids: ids, per_shard = set(), {} for shard, ext_id in sitemap_ids(): ids.add(ext_id) per_shard[shard] = per_shard.get(shard, 0) + 1 print(f"shards: {len(per_shard)}") print(f"item ids, with duplicates: {sum(per_shard.values())}") print(f"distinct item ids: {len(ids)}") print("NOTE: these are STORE ITEMS. Themes are in here too, and so are " "ids with no live detail page. Sample with item_exists() before " "calling any of this a count of extensions.") if a.sitemap_ids: with open(a.sitemap_ids, "w") as f: f.write("\n".join(sorted(ids)) + "\n") return 0 if a.exists: print(item_exists(a.exists)) return 0 if a.crx: blob = download_crx(a.crx) if blob is None: print("not served", file=sys.stderr) return 1 if a.out: open(a.out, "wb").write(blob) print(f"{len(blob)} bytes -> {a.out}") else: print(f"{len(blob)} bytes, magic {blob[:4].decode()}") return 0 if a.amo_count: n, results = amo_count() print(f"AMO listed Firefox extensions: {n}") for r in results[:5]: f = r["current_version"]["file"] print(f" {r['slug']:<28} {r['average_daily_users']:>10} daily users " f"permissions={len(f['permissions'])} " f"data_collection={f.get('data_collection_permissions')}") return 0 ap.print_help() return 0 if __name__ == "__main__": sys.exit(main())
The self-test is the part to copy rather than the fetching. It asserts three cases, not one:
ok item_exists(aaaaaaaa...) = False, expected False # a fabricated id ok item_exists(cjpalhdl...) = False, expected False # uBlock Origin: a Manifest V2 extension, removed from the store 2026-08-31 ok item_exists(ddkjiahe...) = True, expected True # uBlock Origin Lite (MV3) ok download_crx(uBOL) -> 9665461 bytes, magic Cr24 ok a two-part prodversion raises: ddkjiahejlhfcafbddmgiahcphecmpfh: HTTP 204 with an empty bod... ok AMO reports 99677 listed Firefox extensions; first result 'ublock-origin' with 10766677 daily users SELF-TEST PASSED
Replacing the body of item_exists with return True makes the third line pass and the first two fail, so the test reports SELF-TEST FAILED. A one-case test of the form “the extension I know exists is found” passes on that mutant. The same applies to the prodversion case: without the negative case, a 204-swallowing bug is invisible.
Every in-scope paper that crawled a store states a size. Here is every Chrome-store extension corpus in the 68 with n ≥ 5,000, sorted by year. The rule is the size alone — the word the paper used for its own sampling is a column, not a filter, so nothing is quietly left out:
| Year | Venue | Extensions | Sampling, as the paper states it | Snapshot stated |
|---|---|---|---|---|
| 2012 | TheWebConf | 5,943 | exhaustive | not stated |
| 2014 | USENIX Sec | 47,940 | not stated | not stated |
| 2015 | IEEE S&P | 1,000,000 | exhaustive | 2011-03 to 2014-10 |
| 2015 | TheWebConf | 18,030 | exhaustive | March 2014 |
| 2017 | TheWebConf | 12,132 | exhaustive | July 2016 |
| 2017 | USENIX Sec | 10,620 | top-n | not stated |
| 2017 | TheWebConf | 10,000 | top-n | not stated |
| 2017 | IEEE S&P | 10,000 | top-n | not stated |
| 2018 | CCS | 178,893 | exhaustive | crawled between September 2016 and March 2018 |
| 2019 | IEEE S&P | 66,401 | exhaustive | not stated |
| 2019 | NDSS | 62,994 | exhaustive | 2018-02 |
| 2019 | USENIX Sec | 59,255 | exhaustive | November 2017 |
| 2019 | TheWebConf | 58,034 | exhaustive | October 2017 |
| 2020 | CCS | 152,341 | exhaustive | January 2014–April 2020 |
| 2020 | NDSS | 102,482 | exhaustive | not stated |
| 2021 | USENIX Sec | 501,349 | exhaustive | mid 2014 to mid 2019 |
| 2021 | USENIX Sec | 116,485 | exhaustive | April 2019 |
| 2022 | CCS | 186,434 | exhaustive | June 2020 |
| 2022 | CCS | 180,361 | exhaustive | January 2022 |
| 2022 | CCS | 174,355 | exhaustive | February 2021 |
| 2022 | USENIX Sec | 9,446 | not stated | May–June 2021 |
| 2023 | IEEE S&P | 134,196 | exhaustive | 2022-02-21 |
| 2023 | IEEE S&P | 47,207 | purposive | 2022-02-21 |
| 2024 | TheWebConf | 160,000 | not stated | not stated |
| 2024 | CCS | 156,997 | exhaustive | available as of January 3rd, 2024 |
| 2024 | USENIX Sec | 113,099 | exhaustive | August 2023 |
| 2024 | USENIX Sec | 39,592 | pre-existing-dataset | 2023 |
Nineteen of the 27 rows say exhaustive; three say nothing at all; three are explicitly top-N samples of the store rather than the store; one is purposive and one is a pre-existing dataset. That spread is itself the finding: “we crawled the Chrome Web Store” is a sentence with at least five different meanings in this literature.
Six of those rows are not what they look like, and none of the papers is wrong:
Which is why 2022's 186,434 and 2024's 113,099 are not evidence that the store shrank. Name the unit — snapshot, cumulative, functional subset, or top-N — before you compare two figures, and expect a factor of two either way if you do not.
Only 51.4% of the 74 extension-corpus tuples across the 38 papers that built one state a snapshot date at all — 21 of those 38 papers, 55.3%. The corpus-wide reporting rate for population.listVersion is 43.0%, but that is computed over the 5,712 papers that drew any study population, so it is a baseline, not a comparison: the two figures are over different populations and the difference between them is not a measured effect.
For a figure for today: Google publishes none. The sitemap gives 356,984 item IDs on 2026-09-02. Downloading and unpacking the CRX for a deterministic 200-item sample of them puts the composition at 146 extensions, 44 themes and 8 legacy Chrome apps out of 198 served — 73.7% extensions — so the store lists on the order of 260,000 extensions, with an interval a 200-item sample can only bound loosely. Third-party trackers publish confident numbers that disagree with each other by more than a factor of two; none of them is Google, none states its unit, and the most-cited of them is behind a bot wall that will not answer an automated fetch. Firefox is the one you can quote exactly: 99,677 listed extensions.
| Source | State on 2026-09-02 | Use |
|---|---|---|
| Rob Wu's crxviewer | live, pushed_at 2026-09-01, 1,714 stars | the reference implementation of the CRX URL construction; read it before writing your own |
| crx4chrome.com | HTTP 200 | archived CRX packages, including versions the store has removed |
| chrome-stats.com | HTTP 403 to any automated fetch (Cloudflare) | commercial tracker; the source behind most third-party store counts, including Wikipedia's |
| ExtensionTotal | redirects to dex.koi.security | absorbed into a commercial product |
| Secure Annex | HTTP 200, enterprise product | commercial |
| CRXcavator (Duo Labs) | crxcavator.io does not resolve; github.com/duo-labs/crxcavator is 404 | dead. Four papers in the corpus cite it. Do not plan around it |
| Arcanum [1Xie, Qinge; Kasi Murali, Manoj Vignesh; Pearce, Paul; Li, Frank (2024): "Arcanum: Detecting and Evaluating the Privacy Risks of Browser Extensions on Web Pages and Web Content", in: Proceedings of the USENIX Security Symposium. (Link)] | github.com/BEESLab/Arcanum, last push 2024-08-09 | the instrumented-Chromium patches |
| Mystique [13Chen, Quan; Kapravelos, Alexandros (2018): "Mystique: Uncovering Information Leakage from Browser Extensions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] | github.com/wspr-ncsu/mystique, last push 2021-06-08 | the taint framework |
| CloakX [14Trickel, Erik; Starov, Oleksii; Kapravelos, Alexandros; Nikiforakis, Nick; Doupé, Adam (2019): "Everyone is Different: Client-side Diversification for Defending Against Extension Fingerprinting", in: Proceedings of the USENIX Security Symposium. (Link)] | github.com/sefcom/cloakx, last push 2023-10-19 | the countermeasure implementation, and the only extension rewriter released by any paper in the 68 |
| Fingerprinting in Style [10Laperdrix, Pierre; Starov, Oleksii; Chen, Quan; Kapravelos, Alexandros; Nikiforakis, Nick (2021): "Fingerprinting in Style: Detecting Browser Extensions via Injected Style Sheets", in: Proceedings of the USENIX Security Symposium. (Link)] | code at github.com/plaperdr/fingerprinting-in-style; the 4,446-extension dataset at kapravelos.com/datasets/fingerprintable_extensions/, both HTTP 200 | the largest released extension-fingerprint dataset |
The pattern to notice: the released artefacts are the tools, not the corpora. All four research artefacts above are code; exactly one of them also ships a dataset, and that dataset is the 4,446 extensions the paper found fingerprintable rather than the 116,485 it analysed. If your study needs the same extensions a 2019 paper studied, you will be reconstructing them from IDs, and the store will have deleted some.
This is the part of the topic where the answer has changed, so date everything.
| Vector | Introduced | Yield as published | State in 2026 |
|---|---|---|---|
Web-accessible-resource probing — request chrome-extension://<id>/<file> and see whether it loads | [8Sanchez-Rola, Iskander; Santos, Igor; Balzarotti, Davide (2017): "Extension Breakdown: Security Analysis of Browsers Extension Resources Control Policies", in: Proceedings of the USENIX Security Symposium. (Link)] 2017 (timing side channel), [15Sjösten, Alexander; Van Acker, Steven; Picazo-Sanchez, Pablo; Sabelfeld, Andrei (2019): "Latex Gloves: Protecting Browser Extensions from Probing and Revelation Attacks", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] 2019 | 100% of 10,620 Chrome and 10,620 Firefox extensions tested [8Sanchez-Rola, Iskander; Santos, Igor; Balzarotti, Davide (2017): "Extension Breakdown: Security Analysis of Browsers Extension Resources Control Policies", in: Proceedings of the USENIX Security Symposium. (Link)]; 25,866 extensions [2Karami, Soroush; Ilia, Panagiotis; Solomos, Konstantinos; Polakis, Jason (2020): "Carnus: Exploring the Privacy Threats of Browser Extension Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] | Chrome: mitigable but not mitigated — see below. Firefox: closed to this vector, because moz-extension:// URLs carry a UUID that MDN says “is randomly generated for every browser instance” — but the UUID is per-profile, so it defeats ID-based probing and is itself a perfect identifier once any page learns it5) |
| DOM side effects on a honey page | XHOUND [12Starov, Oleksii; Nikiforakis, Nick (2017): "XHOUND: Quantifying the Fingerprintability of Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] 2017 | “at least 9.2% of extensions introduce detectable DOM changes on any arbitrary domain”; 16.6% on a top-50 URL | works; independent of the ID scheme |
| Bloat — DOM changes the extension does not need to make | [16Starov, Oleksii; Laperdrix, Pierre; Kapravelos, Alexandros; Nikiforakis, Nick (2019): "Unnecessarily Identifiable: Quantifying the fingerprintability of browser extensions due to bloat", in: Proceedings of the ACM Web Conference. (DOI)] 2019 | 3,320 extensions, 5.7% of 58,034 | works |
| Injected style sheets | [10Laperdrix, Pierre; Starov, Oleksii; Chen, Quan; Kapravelos, Alexandros; Nikiforakis, Nick (2021): "Fingerprinting in Style: Detecting Browser Extensions via Injected Style Sheets", in: Proceedings of the USENIX Security Symposium. (Link)] 2021 | 6,645 of ~116K inject styles anywhere; 4,446 uniquely identifiable | works |
| Intra- and inter-browser messaging, and outgoing HTTP | [2Karami, Soroush; Ilia, Panagiotis; Solomos, Konstantinos; Polakis, Jason (2020): "Carnus: Exploring the Privacy Threats of Browser Extension Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] 2020 | 450 and 859 extensions respectively | works |
| User-action-triggered behaviour | [17Solomos, Konstantinos; Ilia, Panagiotis; Karami, Soroush; Nikiforakis, Nick; Polakis, Jason (2022): "The Dangers of Human Touch: Fingerprinting Browser Extensions through User Actions", in: Proceedings of the USENIX Security Symposium. (Link)] 2022 | 4,971 extensions, “36% of which are not detectable by state-of-the-art techniques” | works, and is the reason a passive honey page under-counts |
| Execution traces, global namespace, storage side effects | [3Agarwal, Shubham; Fass, Aurore; Stock, Ben (2024): "Peeking through the window: Fingerprinting Browser Extensions through Page-Visible Execution Traces and Interactions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] 2024 | 2,747 Chrome and 572 Firefox extensions of 3,398 behaviourally active | current. No platform change since addresses it, and the paper says its vectors survive use_dynamic_url |
| Filter-list configuration — fingerprint the rules, not the extension | [18El Hajj Chehade, Saiid; Stock, Ben; Troncoso, Carmela (2025): "Double-Edged Shield: On the Fingerprintability of Customized Ad Blockers", in: Proceedings of the USENIX Security Symposium. (Link)] 2025 | “Our attacks identify 84% of filter lists”, cutting the anonymity set to a median of 48 users | current, and a different object: two users of the same ad blocker are distinguishable |
| CSS-only, no JavaScript | [19Trampert, Leon; Weber, Daniel; Gerlach, Lukas; Rossow, Christian; Schwarz, Michael (2025): "Cascading Spy Sheets: Exploiting the Complexity of Modern CSS for Email and Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] 2025 | all 10 tested extensions | current |
And a caution on the other side of the same coin: an extension whose purpose is to defeat fingerprinting is itself a fingerprint. [20Nguyen, Hoang Dai; Vadrevu, Phani (2025): "Breaking the Shield: Analyzing and Attacking Canvas Fingerprinting Defenses in the Wild", in: Proceedings of the ACM Web Conference. (DOI)] analysed the canvas defences of “18 browser extensions in Chrome and Firefox” and recovered the original canvas from nine of the randomising ones; [18El Hajj Chehade, Saiid; Stock, Ben; Troncoso, Carmela (2025): "Double-Edged Shield: On the Fingerprintability of Customized Ad Blockers", in: Proceedings of the USENIX Security Symposium. (Link)] fingerprints the filter list rather than the blocker. Both results say the same thing: installing a countermeasure moves you into a smaller anonymity set, and a study that treats “has a privacy extension” as protection rather than as an attribute has the sign wrong.
Chrome 130 (October 2024) added use_dynamic_url to web_accessible_resources. The documentation is exact: “If true, only allow resources to be accessed through a dynamic ID. A dynamic ID is generated per session. That means it is regenerated when the browser restarts or the extension reloads.”6)
It is opt-in, so whether the vector is closed is a measurement, not a documentation question, and there is one published measurement of it. [3Agarwal, Shubham; Fass, Aurore; Stock, Ben (2024): "Peeking through the window: Fingerprinting Browser Extensions through Page-Visible Execution Traces and Interactions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)], working on a January 2024 snapshot, reports that “we only observe 109 fingerprintable extensions using this option” — 109 out of the 3,398 behaviourally active extensions in its own set. It also notes that its vectors would work regardless.
Two years on, over a different population — a deterministic sample of the sitemap on 2026-09-02, downloading the CRX and parsing manifest.json:
population: 356984 sitemap ids; every-1784th deterministic sample of 200
CRX served: 198 204 refused: 2 not served: 0 unparsed: 0 network errors: 0
manifest_version histogram: {3: 180, 2: 17, None: 1}
item kind, from the manifest: extension=146 theme=44 app=8
-> extensions are 73.7% of the served items
of the 146 extensions, declare web_accessible_resources: 46 (31.5%)
use_dynamic_url on EVERY entry: 0
use_dynamic_url on SOME entries: 0
no use_dynamic_url anywhere: 46 <- still probeable by extension id
Manifest V2 items still served by the CRX endpoint: 17
of which the store detail page still resolves: 16
by kind: {'extension': 0, 'theme': 9, 'app': 8}
-> manifest_version 2 in the store today is a THEME or a legacy Chrome APP,
not a surviving Manifest V2 extension.
The vector is open, and the two measurements agree: the mitigation exists and is almost unused. Treat “MV3 killed extension enumeration” as false until somebody re-measures it at store scale; sandbox/war_dynamic_url.py is committed so that they can, and it prints its sample size.
There is a plausible reason, and it is also measured. [6Agarwal, Shubham; Mrowczynski, Rafael; Hellenthal, Maria; Stock, Ben (2025): ""I have no idea how to make it safer": Studying Security and Privacy Mindsets of Browser Extension Developers", in: Proceedings of the USENIX Security Symposium. (Link)] put the key in front of extension developers and found that “15/17 participants did not comment at all on specific S&P threats and related concepts”, with two of them asking what use_dynamic_url was. An opt-in mitigation that its intended adopters have not heard of is not a mitigation yet.
Two consequences for a study design. If you are building a fingerprinting result, WAR probing is a Chrome-only, opt-out-able vector and your Firefox arm needs a behavioural technique or it will read zero. If you are building a defence or a prevalence result, the vectors that no platform change touches — DOM, styles, user actions, execution traces — are the ones to key on, and [3Agarwal, Shubham; Fass, Aurore; Stock, Ben (2024): "Peeking through the window: Fingerprinting Browser Extensions through Page-Visible Execution Traces and Interactions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] is the current baseline to beat.
The method ladder, dated. Each rung is still used; the top two are what a 2026 paper is expected to do.
| Method | Papers | What it yields |
|---|---|---|
| Manual security review | [21Carlini, Nicholas; Felt, Adrienne Porter; Wagner, David (2012): "An Evaluation of the Google Chrome Extension Security Architecture", in: Proceedings of the USENIX Security Symposium. (Link)] 2012 — “security review of 100 Chrome extensions and found 70 vulnerabilities across 40 extensions” | ground truth, at 100 extensions |
| Static information flow | VEX [22Bandhakavi, Sruthi; King, Samuel T.; Madhusudan, P.; Winslett, Marianne (2010): "VEX: Vetting Browser Extensions for Security Vulnerabilities", in: Proceedings of the USENIX Security Symposium. (Link)] 2010, IBEX [23Guha, Arjun; Fredrikson, Matthew; Livshits, Benjamin; Swamy, Nikhil (2011): "Verified Security for Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] 2011, LvDetector [24Zhao, Rui; Yue, Chuan; Yi, Qing (2015): "Automatic Detection of Information Leakage Vulnerabilities in Browser Extensions", in: Proceedings of the ACM Web Conference. (DOI)] 2015, EmPoWeb [25Somé, Dolière Francis (2019): "EmPoWeb: Empowering Web Applications with Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] 2019 (197 exploitable extensions) | scales to the store; false positives need a manual pass |
| Dynamic behavioural elicitation — drive the extension against pages built to trigger it | Hulk [26Kapravelos, Alexandros; Grier, Chris; Chachra, Neha; Kruegel, Christopher; Vigna, Giovanni; Paxson, Vern (2014): "Hulk: Eliciting Malicious Behavior in Browser Extensions", in: Proceedings of the USENIX Security Symposium. (Link)] 2014: “we identified 130 malicious and 4,712 suspicious extensions” out of 48,332 | catches what static analysis cannot read |
| Store-side pipeline — submission-time signals no outside crawl has | WebEval [4Jagpal, Nav; Dingle, Eric; Gravel, Jean-Philippe; Mavrommatis, Panayiotis; Provos, Niels; Rajab, Moheeb Abu; Thomas, Kurt (2015): "Trends and Lessons from Three Years Fighting Malicious Extensions", in: Proceedings of the USENIX Security Symposium. (Link)] 2015: 9,523 malicious of 99,818 submitted | not reproducible outside Google |
| Traffic differencing — install the extension, load pages, diff the outgoing requests | Expector [27Xing, Xinyu; Meng, Wei; Lee, Byoungyoung; Weinsberg, Udi; Sheth, Anmol; Perdisci, Roberto; Lee, Wenke (2015): "Understanding Malvertising Through Ad-Injecting Browser Extensions", in: Proceedings of the ACM Web Conference. (DOI)] 2015: 292 ad injectors of 18,030, 56 of them serving malvertising; Extended Tracking Powers [11Starov, Oleksii; Nikiforakis, Nick (2017): "Extended Tracking Powers: Measuring the Privacy Diffusion Enabled by Browser Extensions", in: Proceedings of the ACM Web Conference. (DOI)] 2017: “618 extensions (6.3%) that introduce privacy leaks of browsing and search history” | cheap, and blind to anything the extension does not send |
| Hybrid taint tracking | Mystique [13Chen, Quan; Kapravelos, Alexandros (2018): "Mystique: Uncovering Information Leakage from Browser Extensions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] 2018: 3,868 of 181,683 (2.13%) | the standard for leakage since 2018 |
| Update-delta clustering | [28Pantelaios, Nikolaos; Nikiforakis, Nick; Kapravelos, Alexandros (2020): "You've Changed: Detecting Malicious Browser Extensions through their Update Deltas", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] 2020: 143 malicious extensions in 21 clusters | catches the benign-then-sold extension, which a single snapshot cannot |
| Forced execution | J-Force [29Kim, Kyungtae; Kim, I Luk; Kim, Chung Hwan; Kwon, Yonghwi; Zheng, Yunhui; Zhang, Xiangyu; Xu, Dongyan (2017): "J-Force: Forced Execution on JavaScript", in: Proceedings of the ACM Web Conference. (DOI)] 2017, FV8 [30Pantelaios, Nikolaos; Kapravelos, Alexandros (2024): "FV8: A Forced Execution JavaScript Engine for Detecting Evasive Techniques", in: Proceedings of the USENIX Security Symposium. (Link)] 2024: 110 malicious extensions | defeats evasion |
| Whole-store dynamic taint against real target sites | Arcanum [1Xie, Qinge; Kasi Murali, Manoj Vignesh; Pearce, Paul; Li, Frank (2024): "Arcanum: Detecting and Evaluating the Privacy Risks of Browser Extensions on Web Pages and Web Content", in: Proceedings of the USENIX Security Symposium. (Link)] 2024: 3,028 of 113,099 (2.68%) collecting sensitive data, “impacting up to 144M users” | current. The change from Mystique is the target: real logged-in sites, not synthetic pages |
| Disclosure-versus-behaviour consistency | ExtPrivA [31Bui, Duc; Tang, Brian; Shin, Kang G. (2023): "Detection of Inconsistencies in Privacy Practices of Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] 2023: 820 extensions, 84.6M users, 1,290 flows inconsistent with their own privacy statements | the compliance framing, and the one that maps to regulation |
| LLM-assisted flow triage | [32Nayak, Asmit; Khandelwal, Rishabh; Fernandes, Earlence; Fawaz, Kassem (2024): "Experimental Security Analysis of Sensitive Data Access by Browser Extensions", in: Proceedings of the ACM Web Conference. (DOI)] 2024, [33Wang, Weihong; Dimova, Yana; Vansteenkiste, Victor; Van Goethem, Tom; Van Cutsem, Tom (2026): "The Masks We (Think We) Wear: Privacy Threats of Browser-Extension Wallets in the Web3 Ecosystem", in: Proceedings on Privacy Enhancing Technologies. (DOI)] 2026, [5Lukić, Karlo; Papadopoulos, Lazaros (2026): "Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad Blocker Effectiveness", in: Proceedings on Privacy Enhancing Technologies. (Link)] 2026 | newest, and thin: only 4 of the 68 in-scope papers record an LLM tool or an LLM classification method at all |
Two 2020s additions to the leakage question are worth naming because they are new objects, not new methods. [34Ferreira Torres, Christof; Willi, Fiona; Shinde, Shweta (2023): "Is Your Wallet Snitching On You? An Analysis on the Privacy Implications of Web3", in: 32nd USENIX Security Symposium (USENIX Security 23). (Link)] turned the question around and measured the web: 1,325 of the top 100K sites run scripts that probe whether the visitor has a wallet extension installed, which makes extension detection a thing you can measure in the wild rather than only in a lab. And [35Vekaria, Yash; Canino, Aurelio Loris; Levitsky, Jonathan; Ciechonski, Alex; Callejo, Patricia; Mandalari, Anna Maria; Shafiq, Zubair (2025): "Big Help or Big Brother? Auditing Tracking, Profiling, and Personalization in Generative AI Assistants", in: Proceedings of the USENIX Security Symposium. (Link)] audited generative-AI assistant extensions, finding that 8 of 9 generate their responses server-side — so the extension is a pipe to a third party by design, and the taint-tracking methods above stop at the network boundary.
Two base rates worth carrying in your head, because a new detector will be compared against them: 2.13% of extensions leak privacy-sensitive information by hybrid taint [13Chen, Quan; Kapravelos, Alexandros (2018): "Mystique: Uncovering Information Leakage from Browser Extensions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)], and 2.68% propagate sensitive data from a real site to a sink [1Xie, Qinge; Kasi Murali, Manoj Vignesh; Pearce, Paul; Li, Frank (2024): "Arcanum: Detecting and Evaluating the Privacy Risks of Browser Extensions on Web Pages and Web Content", in: Proceedings of the USENIX Security Symposium. (Link)]. They are six years and two methods apart and they agree to within half a percentage point. A method that reports 30% is reporting something else, and should say what.
The vulnerability papers are a single fourteen-year argument about one design: extensions run privileged code next to hostile page content, and the boundary leaks. [36Djeric, Vladan; Goel, Ashvin (2010): "Securing Script-Based Extensibility in Web Browsers", in: Proceedings of the USENIX Security Symposium. (Link)] 2010 and VEX [22Bandhakavi, Sruthi; King, Samuel T.; Madhusudan, P.; Winslett, Marianne (2010): "VEX: Vetting Browser Extensions for Security Vulnerabilities", in: Proceedings of the USENIX Security Symposium. (Link)] 2010 attacked the old Firefox XPCOM model; [21Carlini, Nicholas; Felt, Adrienne Porter; Wagner, David (2012): "An Evaluation of the Google Chrome Extension Security Architecture", in: Proceedings of the USENIX Security Symposium. (Link)] 2012 evaluated Chrome's replacement — isolated worlds, privilege separation, a permission system — and found 70 vulnerabilities in 40 of 100 reviewed extensions anyway. [37Akhawe, Devdatta; Saxena, Prateek; Song, Dawn (2012): "Privilege Separation in HTML5 Applications", in: Proceedings of the USENIX Security Symposium. (Link)] 2012 showed why: in half the extensions they analysed, fewer than 5% of functions need any privilege at all, so the privileged surface is almost entirely gratuitous. Eleven years later [38Kim, Young Min; Lee, Byoungyoung (2023): "Extending a Hand to Attackers: Browser Privilege Escalation Attacks via Extensions", in: Proceedings of the USENIX Security Symposium. (Link)] found 59 vulnerabilities in 40 extensions in the same architecture, caused by the same requirement being violated. EmPoWeb [25Somé, Dolière Francis (2019): "EmPoWeb: Empowering Web Applications with Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] is the sharpest version: 197 extensions whose message listeners let an arbitrary web page borrow the extension's privileges.
A different failure of the same boundary is that extensions rewrite the security headers the server sent. [39Agarwal, Shubham (2022): "Helping or Hindering?: How Browser Extensions Undermine Security", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] analysed three Chrome Web Store snapshots and found 1,129 extensions that interfere with security-related request or response headers, 205 of which “strip the entire CSP header”. If your measurement reads a header out of the browser rather than off the wire, an extension in the profile can have changed it.
The password-manager strand is the same problem with a concrete asset. [40Anliker, Claudio; Lain, Daniele; Capkun, Srdjan (2025): "Phishing Attacks against Password Manager Browser Extensions", in: Proceedings of the USENIX Security Symposium. (Link)] imitated the extension's own UI inside the page and reports “a high average attack success rate of 31.25%” across the password managers it tested, with a peak of 58.82% for one of them — an attack that exists because the extension draws its interface in a context the page controls.
Over-privilege has stalled. The corpus's permission papers are [23Guha, Arjun; Fredrikson, Matthew; Livshits, Benjamin; Swamy, Nikhil (2011): "Verified Security for Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] 2011 (“1,139 popular Chrome extensions”; nearly a third request full privileges over arbitrarily many sites), [41Chia, Pern Hui; Yamamoto, Yusuke; Asokan, N. (2012): "Is this app safe?: a large scale study on application permissions and risk signals", in: Proceedings of the ACM Web Conference. (DOI)] 2012 (5,943 extensions alongside Android and Facebook apps), [42Dong, Xinshu; Chen, Zhaofeng; Siadati, Hossein; Tople, Shruti; Saxena, Prateek; Liang, Zhenkai (2013): "Protecting sensitive web content from client-side vulnerabilities with CRYPTONS", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] 2013 (23 of the top 30 Chrome extensions request access to Gmail) and TKPERM [43Shezan, Faysal Hossain; Cheng, Kaiming; Zhang, Zhen; Cao, Yinzhi; Tian, Yuan (2020): "TKPERM: Cross-platform Permission Knowledge Transfer to Detect Overprivileged Third-party Applications", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] 2020. Nothing since. Manifest V3 changed the permission model — host permissions became optional and revocable at runtime — and no paper in this corpus has measured what that did.
Eleven of the 68 papers ask this, and they are the most methodologically varied group on the page, because “protect” has to be operationalised differently every time.
The through-line: none of these treats “the extension is installed” as the outcome. Each defines a concrete thing the extension is supposed to prevent and then tries to make it happen anyway. A study that reports “we installed uBlock Origin” and moves on has skipped the measurement.
Seven of the 68 papers study the humans rather than the code, and all but one are from 2021 onwards. They are worth knowing about because they are the only papers here that ask what the numbers above mean to anyone.
Store reviews are a data source here, and an unusual one: they are one of the few public per-extension signals that comes from users rather than from the vendor. [28Pantelaios, Nikolaos; Nikiforakis, Nick; Kapravelos, Alexandros (2020): "You've Changed: Detecting Malicious Browser Extensions through their Update Deltas", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] used rating anomalies as the first stage of malicious-extension detection for the same reason.
Only 11 of the 68 in-scope papers name Manifest V3 at all — the earliest in 2020, and seven of the eleven from 2024 onwards. Every result older than those is a result about Manifest V2, and that platform no longer exists in Chrome.
| Date | Milestone, from Chrome's own timeline7) |
|---|---|
| January 2022 | the Chrome Web Store stops accepting new public Manifest V2 extensions |
| June 2024 | Manifest V2 extensions “lose their badge” — the Featured badge is withdrawn |
| October 2024 | disabling of installed MV2 extensions begins in Chrome stable |
| 2025-03-31 | MV2 disabled by default, users may re-enable; enterprises exempt via ExtensionManifestV2Availability |
| 2025-07-24 | “With Chrome 138 all users on all channels of Chrome have now Manifest V2 extensions disabled. Users can no longer turn them back on.” The enterprise policy is “removed with Chrome 139” |
| 2026-08-31 | “All remaining Manifest V2 extensions are removed from the Chrome Web Store.” Installed copies on Chrome 138 or earlier “will remain installed, but will be unable to receive any updates and cannot be reinstalled” |
The other browsers did not follow. Firefox supports both, and keeps blocking webRequest: MDN documents webRequestBlocking as an ordinary API permission with no Manifest V3 restriction8). Microsoft Edge has not set a date: its own policy documentation says “the timeline of the migration hasn't been established”9). So a cross-browser extension study in 2026 is comparing two different extension platforms, and has to say which.
If your instrument is an extension — and for 92 of the 218 candidate papers it was — three MV3 properties will bite:
webRequestBlocking, the webRequest API is unchanged and available for normal use”, and blocking “is only available to policy installed extensions”. Passive instrumentation survives MV3 unchanged. A blocking treatment arm does not, unless you install the extension by enterprise policy.declarativeNetRequest as documented on 2026-09-02: at most 100 static rulesets, of which 50 may be enabled; 30,000 guaranteed static rules and 30,000 dynamic rules; 5,000 session rules, 5,000 “unsafe” dynamic and 5,000 “unsafe” session rules; 1,000 regular-expression rules, each under 2 KB compiled; getMatchedRules callable 20 times per quota interval11). For scale: EasyList on 2026-09-02 (version 202609020241) is 80,470 non-comment lines12) against a 30,000-rule guaranteed static allowance. That gap is the whole argument about MV3 and ad blocking, and the section below is the only measurement of it in this corpus.The argument was that MV3 would cripple content blocking. The one measurement of it in this corpus, [5Lukić, Karlo; Papadopoulos, Lazaros (2026): "Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad Blocker Effectiveness", in: Proceedings on Privacy Enhancing Technologies. (Link)], PoPETs 2026, ran MV3 and MV2 ad blockers over the same sites in July 2025 and found no significant difference in ads blocked — means of 1.42 and 1.46 — while MV3 blocked more trackers per site than MV2 and produced no substantial difference in breakage. The one clear MV3 effect it found was cosmetic: placeholder frames left where an ad was removed, in 21% of cases against 0% under MV2.
That is one paper, on a sample of a few hundred sites, and it measures the blockers that survived the migration rather than the ones that did not. But it is the current evidence, and it points the other way from the consensus. If you are going to assert that MV3 broke ad blocking, measure it.
The corpus has exactly one paper on this: [7Snyder, Peter; Ansari, Lara; Taylor, Cynthia; Kanich, Chris (2016): "Browser Feature Usage on the Modern Web", in: Proceedings of the ACM Internet Measurement Conference. (DOI)], IMC 2016, which instrumented a browser and re-ran its feature census with AdBlock Plus and Ghostery installed. Its result — a set of “approximately 10%” of browser features “which ad and tracking blockers prevent from executing more than 90% of the time” — is the only estimate in this corpus of how much an installed extension changes what a crawl sees.
That should worry you more than it seems to worry the field, because:
Report the extension set, with versions, the way you report the browser version. The extraction schema has no field for it, so this page cannot tell you how often anyone does — which is itself the answer: it is not a thing the field records.
id_util.cc computes them as the “First 16 bytes of SHA256 hashed public key”, hex-encoded and mapped from 0–f onto a–p “to avoid ever having a completely numeric host”. So an ID persists across versions and across store removal, and it changes only if the developer rotates the signing key. Key on the ID, not on the name.use_dynamic_url is per-entry.use_dynamic_url adoption at scale. The sample here is small and was taken on one day. A store-wide figure is one crawl away and would settle whether extension enumeration is a live threat or a historical one.Corpus figures come from the 5,859-paper extraction over seven venues (CCS, IMC, NDSS, PoPETs, USENIX Security, TheWebConf, IEEE S&P), 2010–2026. Any claim about “the literature” here is a claim about those seven venues, and extension work is also published at ACSAC, EuroS&P, ASIACCS, SOUPS and CODASPY, which are absent. 2025 and 2026 venue-years are provisional — see corpus.
The 68-paper population is a candidate sweep plus a hand verdict on every one of 218 candidates, not a keyword count: a bare /browser extensions?/i fires on 535 papers. The rule was written before the first figure was computed, the report fails if the sweep and the hand map diverge in either direction, and widening the sweep to any extension artefact string adds 32 papers and zero in-scope ones. Every per-paper figure quoted above was checked against the paper's own text as a verbatim phrase (64 of 64 located, all of them in the de-columned rendering the extraction itself read).
All external figures were fetched on 2026-09-02 and are re-checkable with scripts/external_checks_browser_extensions.sh. The Chrome Web Store sitemap count, the item sample, the use_dynamic_url sample and the AMO data_collection_permissions measurement are original to this page; each is a single-day snapshot of a store that changes daily.
The full query log — every population and denominator, the report script and its unedited output, the hand verdicts, the quotes spot-checked, and the external sources verified or rejected — is at browser_extensions, including which review passes this page has had and which one it is still missing. Corpus-level caveats are on corpus.
https://developer.chrome.com/docs/webstore/api (the older api_index path redirects there) — the Chrome Web Store API publishes and manages the developer's own item; there is no listing endpoint. Fetched 2026-09-02.https://learn.microsoft.com/en-us/microsoft-edge/extensions/update/api/using-addons-api, fetched 2026-09-02: the documented endpoints are package upload, submission status and publish, all scoped to a productID you own.safari-extensions.apple.com — the 721-extension corpus [8Sanchez-Rola, Iskander; Santos, Igor; Balzarotti, Davide (2017): "Extension Breakdown: Security Analysis of Browsers Extension Resources Control Policies", in: Proceedings of the USENIX Security Symposium. (Link)] enumerated in 2017 — no longer answers at all: the connection times out.https://easylist.to/easylist/easylist.txt on 2026-09-02; the version and commit are in the list's own header. Not all of those lines are network rules a declarativeNetRequest ruleset would express — cosmetic and exception rules are counted too — so this is an upper bound on the rules that have to fit.