“2,523” Opera needle; the run of 2026-09-22 added two with the corrected Carnus row. The count reached 64 at some point before 2026-09-21 and is 66 now — but the exact increments cannot be reconstructed from this log, which is the point: the count is only trustworthy from the script. The Quote-check refresh, 2026-09-21 section below said 70-needle, which the check has never been. Re-run the script rather than quoting any figure in prose, including this one.This is an old revision of the document!
Table of Contents
Provenance: privacy:browser_extensions
Working log behind Measuring Browser Extensions. Corpus-wide caveats — the venue scope, the selection funnel, the extraction's own error rates — are on corpus and are not repeated here. This page carries only what is specific to that one page: the population rule, every query with its denominator, the report script and its unedited output, the quote checks, the external sources verified and rejected, the reviewer log, and the things that could not be established.
Written as a working log, not as prose. Read it if you are checking a number.
The run
| Date | 2026-09-02 |
| Corpus | data/extract/run1/extractions.jsonl, 5,859 papers, 7 venues (CCS, IMC, NDSS, PoPETs, USENIX Security, TheWebConf, IEEE S&P), 2010–2026 |
| Page created | new; no earlier version to diff against |
| Scripts committed | scripts/ext_fold.mjs, scripts/report_browser_extensions.mjs, scripts/verify_extension_figures.mjs, scripts/ext_quotecheck.mjs, scripts/external_checks_browser_extensions.sh, scripts/build_ext_codeblock.py, scripts/build_provenance_browser_extensions.py, scripts/append_bib_browser_extensions.py, scripts/bib_additions_browser_extensions.bib |
| Measurement scripts | sandbox/cws_sitemap_count.sh, sandbox/cws_sitemap_sample.sh, sandbox/war_dynamic_url.py, sandbox/amo_data_collection.py |
| Published code | pages/ext_corpus.py — embedded on the page as a <file> block |
| Bibliography | 32 entries appended to bibliography; the page cites 49 distinct keys, 32 of them new and 17 already live |
The written boundary
Fixed before the first figure was computed, because “papers about browser extensions” is a phrase and not a population. It is the comment at the top of scripts/ext_fold.mjs and is reproduced here verbatim so the page can be audited without repository access:
IN SCOPE (object/object-partial): the paper produces a reported finding ABOUT browser extensions — their code, their store listings, their permissions, their observable effect on a page, the set a user has installed, or the people who write and use them.
OUT OF SCOPE: a paper that only SHIPS an extension as its instrument or as the deployment vehicle for its own defence (instrument); a paper that assumes a malicious extension in a threat model without measuring any real ones (attack-vector); a paper that draws samples of something else out of an extension store (corpus-source); a citation (mention); and a different sense of the word (homograph).
Candidate sweep
A paper is a candidate if any of four signals fires. Each candidate then carries a hand verdict; report_browser_extensions.mjs exits 1 if a candidate has no verdict or a verdict has no candidate, in both directions.
| Signal | Definition | Candidates |
|---|---|---|
| D | paper.cols.txt matches the object regex ≥ 5 times | 197 |
| S | population[].sourceList names an extension distribution channel | 52 |
| C | classification[].resourceName names extensions | 13 |
| X | a detection[] tuple names extensions | 39 |
| union | 218 |
The object regex, counted rather than tested:
/\bbrowser[- ]extensions?\b|\bChrome extensions?\b|\bFirefox (add-?ons?|extensions?)\b |\bWebExtensions?\b|\bChrome Web Store\b|\baddons\.mozilla\.org\b|\bextension stores?\b |\.crx\b|\bcontent scripts?\b|web[- ]accessible resources?/gi
Why the threshold is 5, and what it costs
The threshold is not defended by roundness. Three measurements bound it:
- A bare
/browser extensions?/iover the corpus fires on 535 papers. Almost all are one citation of Ghostery or uBlock Origin. - The density distribution over the 626 papers with at least one match (report section A3):
| Matches | Papers | Cumulative from the top |
|---|---|---|
| 1 | 248 | 626 |
| 2 | 82 | 378 |
| 3–4 | 99 | 296 |
| 5–9 | 93 | 197 ← the cut |
| 10–19 | 52 | 104 |
| 20–49 | 30 | 52 |
| 50–99 | 16 | 22 |
| 100+ | 6 | 6 |
The cut admits 197 papers on the D signal and leaves 429 out.
- The recall check. Re-running the candidate sweep with a much wider signal — full text matching any extension artefact string (
Chrome Web Store,.crx,moz-extension,chrome-extension://,manifest.json,MV3,web accessible resources,addons.mozilla.org) — adds 32 papers outside the 218. All 32 were read on 2026-09-02. Every one is a reference-list URL, a one-sentence citation, or a different sense of the word. In-scope papers added: 0.
Two of the 32 are worth recording because they are the near-misses: Mistrust Plugins You Must (USENIX Security 2022) is about WordPress plugins and cites the malicious-extension literature in its first paragraph; The Curious Case of the PDF Converter that Likes Mozart (PoPETs 2016) samples from the Google Drive apps section of the Chrome Web Store, which is a store crawl of something that is not an extension. Both are homograph.
Role tally
| Role | Papers | Share of 218 | In scope |
|---|---|---|---|
instrument | 92 | 42.2% | no |
object | 47 | 21.6% | yes |
mention | 38 | 17.4% | no |
object-partial | 21 | 9.6% | yes |
homograph | 13 | 6.0% | no |
attack-vector | 5 | 2.3% | no |
corpus-source | 2 | 0.9% | no |
| 68 in scope |
How the verdicts were made, and the ones a reasonable person would decide otherwise
Every verdict was made by reading the paper's own first-person sentences about extensions, printed by a helper over paper.cols.txt (the query is in the report's --quotes path; the raw dossier is out/ext/firstperson.txt, not published because it is 98 KB of quotations). The distinction that did the most work is between a paper whose results include a statement about extensions and one where extensions appear only in the evaluation of the paper's own system.
| Paper | Verdict | Why, and the case against |
|---|---|---|
| Cookieless Monster, IEEE S&P 2013 | object-partial | It tests user-agent-spoofing extensions and reports that all of them fail to hide the browser — a finding about extensions. Against: the paper is about fingerprinting scripts, and extensions are one section |
| Tales of Favicons and Caches, NDSS 2021 | object-partial | “We also explore the effect of popular privacy-enhancing browser extensions and find that while they can impact performance they do not prevent our attack.” That is a reported finding about extensions. Against: they are a robustness check on an attack |
| Fashion Faux Pas, IEEE S&P 2023 | object-partial | Same shape: six anti-fingerprinting extensions evaluated. Against: they are baselines |
| Spook.js, IEEE S&P 2022 | object-partial | “we look at the security implications of consolidating Chrome extensions”, demonstrated against LastPass. Against: the paper is a transient-execution attack |
| Finding and Preventing Bugs in JavaScript Bindings, IEEE S&P 2017 | object-partial | The Chrome extension system is one of four runtimes checked and 65 errors are flagged in it. Against: the object is the binding layer, not extensions |
| A Security and Usability Analysis of Local Attacks Against FIDO2, NDSS 2024 | attack-vector | It builds malicious extensions and attacks ten servers with them; it measures no real extension. A reasonable person would call this in scope |
| Passwords and FIDO2 Are Meant To Be Secret, CCS 2025 | attack-vector | Same: a Firefox defence evaluated against extensions the authors wrote |
| When Ads Become Profiles, TheWebConf 2026 | attack-vector | Names “browser extensions that abuse legitimate privileges” as its threat model and measures none |
| Automated WebAssembly Function Purpose Identification, TheWebConf 2023 | corpus-source | Crawls 17,682 Chrome extensions and 16,385 Firefox add-ons — for the .wasm files inside them |
| An Empirical Study of Real-World WebAssembly Binaries, TheWebConf 2021 | corpus-source | Same shape, 2,500 Firefox add-ons |
| Analyzing Societal Awareness … Fingerprinting Countermeasures, PoPETs 2026 | mention | Extensions are one of several named countermeasure categories in a survey about fingerprinting; no per-category result is reported. Against: the survey does ask about them |
| Support Personas, PoPETs 2024 | instrument | Builds an extension prototype and studies the interface, not the extension ecosystem |
The single-label choice is a limitation. object-partial is not a confidence level, it is a scope statement, and eleven papers sit close to the line. The page's headline (68) is reported with its split (47 + 21) everywhere so a reader who disagrees can take the 47.
Every query, with its denominator
| Figure on the page | Population | Denominator | Where |
|---|---|---|---|
| 218 candidates | all papers | 5,859 | report §A |
| 68 in scope, 47 + 21 | candidates | 218 | report §A |
92 instrument | candidates | 218 | report §A |
| 535 bare-mention control | all papers | 5,859 | report §A2 |
| 32 added by the widened sweep, 0 in scope | all papers minus the 218 | 5,859 | report §A2 |
| USENIX 24 / 35.3%, IEEE S&P 12, TheWebConf 12, CCS 8, NDSS 6, PoPETs 5, IMC 1 | in-scope papers | 68 | report §B |
| 9 / 14 / 19 / 15 / 11 per period | in-scope papers | 68 | report §B |
| the eight question rows | in-scope papers | 68 | report §C |
| 74 extension-corpus tuples across 38 papers; 50 from a Chrome store | in-scope papers with a unit == “browser-extensions” population tuple carrying a stated n | 68 papers / 74 tuples | report §D |
| snapshot stated: 51.4% of tuples, 55.3% of papers | those tuples / those papers | 74 / 38 | report §D |
| the per-paper store-size table | the same tuples | — | report §D |
| 4 papers record an LLM tool or LLM classification method | in-scope papers | 68 | report §G |
| every per-paper measured figure | that paper's own text | its own | report §H, verify_extension_figures.mjs |
The corpus-wide 43.0% baseline for population.listVersion is from data/extract/OVERVIEW.md (Where the field goes quiet), computed over the 5,712 papers that drew a study population — a different population from the 38 here, which is why it is labelled as a baseline and not compared arithmetically.
The report, unedited
node scripts/report_browser_extensions.mjs. The full output is long; the sections the page reads from are reproduced verbatim below. Nothing here is retyped.
- report_browser_extensions-output.txt
============================================================================== A. THE POPULATION ============================================================================== corpus 5859 papers, CCS, IEEE-SP, IMC, NDSS, PETS, USENIX, WWW, 2010-2026 candidate sweep (D>=5 | S | C | X) 218 papers hand verdicts 218 (sweep and map agree in both directions) Role Papers Share of 218 In scope -------------- ------ ------------ -------- instrument 92 42.2% no object 47 21.6% yes mention 38 17.4% no object-partial 21 9.6% yes homograph 13 6.0% no attack-vector 5 2.3% no corpus-source 2 0.9% no IN SCOPE (object + object-partial) 68 papers = 47 object + 21 object-partial as a share of the whole corpus 1.2% of 5859 OUT OF SCOPE 150 papers of which role=instrument 92 - the extension is the paper's own tool or defence vehicle -- why each candidate fired (a paper can fire on several) -- Signal Candidates ----------------------------------------------------- ---------- D full text matches the object regex >= 5 times 197 S population[].sourceList names an extension channel 52 C classification[].resourceName names extensions 13 X a detection[] tuple names extensions 39 ============================================================================== A2. RECALL CHECK — widening the sweep to any extension artefact ============================================================================== Papers outside the candidate set whose full text names Chrome Web Store, .crx, moz-extension, chrome-extension://, manifest.json, MV3 or a web-accessible resource: 32. All 32 were read by hand on 2026-09-02; every one is a citation, a reference-list URL or a different sense of the word. In-scope papers added by the widening: 0 — true by construction, not a measurement: the verdict map is keyed on candidates, so a non-candidate cannot carry a verdict. The hand reading is what rules them out. Bare-mention control: /browser extensions?/i alone fires on 535 papers. ============================================================================== A3. THE DENSITY DISTRIBUTION THE THRESHOLD IS CUT FROM ============================================================================== papers whose full text matches the object regex at least once: 626 Matches Papers Cumulative from the top ------- ------ ----------------------- 1-1 248 626 2-2 82 378 3-4 99 296 5-9 93 197 10-19 52 104 20-49 30 52 50-99 16 22 100-+ 6 6 The threshold is 5. Everything at 5 or above is a candidate; the 429 papers below it are not, and A2 is the check on that cut. ============================================================================== B. SHAPE OF THE IN-SCOPE POPULATION ============================================================================== Venue In-scope papers Share of 68 Corpus papers Share of corpus ------- --------------- ----------- ------------- --------------- USENIX 24 35.3% 1410 24.1% IEEE-SP 12 17.6% 767 13.1% WWW 12 17.6% 843 14.4% CCS 8 11.8% 990 16.9% NDSS 6 8.8% 701 12.0% PETS 5 7.4% 510 8.7% IMC 1 1.5% 638 10.9% The base-rate column is the point: a venue share is only interesting against the venue's share of the corpus as a whole. Years In-scope Corpus Share of corpus ---------- -------- ------ --------------- 2010–2013 9 511 1.8% 2014–2017 14 769 1.8% 2018–2021 19 1439 1.3% 2022–2024 15 1955 0.8% 2025–2026* 11 1185 0.9% (* 2025-2026 is provisional: CCS and IMC 2026 have not been held and IEEE S&P / TheWebConf 2026 are incompletely selected. See literature:corpus.) -- per year, in-scope only -- Year In-scope papers ---- --------------- 2010 3 2011 1 2012 3 2013 2 2014 2 2015 5 2016 1 2017 6 2018 4 2019 6 2020 5 2021 4 2022 4 2023 5 2024 6 2025 9 2026 2 ============================================================================== C. WHICH QUESTION EACH IN-SCOPE PAPER ASKS ============================================================================== Question Papers Share of 68 What it asks ------------------ ------ ----------- ------------------------------------------------------------------------ security 18 26.5% Is the extension itself vulnerable, and what does that give an attacker? fingerprintability 11 16.2% Can a page tell which extensions you have installed? blocking-efficacy 11 16.2% Does the protective extension actually protect? privacy-leak 8 11.8% What does the extension send, and to whom? malice 8 11.8% Is the extension deliberately abusive, and can a store catch it? people 7 10.3% What do the users and the developers of extensions know and do? permissions 4 5.9% Does the extension ask for more than it needs? crawl-effect 1 1.5% What does an installed extension do to your own measurement? -- question by period (papers) -- Question 2010–2013 2014–2017 2018–2021 2022–2024 2025–2026* ------------------ --------- --------- --------- --------- ---------- fingerprintability 0 2 5 2 2 security 5 4 4 3 2 privacy-leak 0 1 1 4 2 malice 0 5 2 1 0 blocking-efficacy 1 0 5 2 3 people 0 1 1 3 2 permissions 3 0 1 0 0 crawl-effect 0 1 0 0 0 ============================================================================== D. HOW BIG IS THE STORE? — every extension-corpus size in the in-scope papers ============================================================================== Population tuples with unit == "browser-extensions" and a stated n: 74 across 38 of the 68 in-scope papers of which the source is a Chrome store: 50 tuples -- Every Chrome-store extension corpus in the in-scope papers with n >= 5000 -- (the rule is the size alone; the paper's own sampling word is a column, not a filter, so nothing is silently excluded) Year Venue Extensions Sampling as the paper states it Snapshot stated ---- ------- ---------- ------------------------------- --------------------------------------------- 2012 WWW 5,943 exhaustive not-stated 2014 USENIX 47,940 not-stated not-stated 2015 IEEE-SP 1,000,000 exhaustive 2011-03 to 2014-10 2015 WWW 18,030 exhaustive March 2014 2017 WWW 12,132 exhaustive July 2016 2017 USENIX 10,620 top-n not-stated 2017 WWW 10,000 top-n not-stated 2017 IEEE-SP 10,000 top-n not-stated 2018 CCS 178,893 exhaustive crawled between September 2016 and March 2018 2019 IEEE-SP 66,401 exhaustive not-stated 2019 NDSS 62,994 exhaustive 2018-02 2019 USENIX 59,255 exhaustive November 2017 2019 WWW 58,034 exhaustive October 2017 2020 CCS 152,341 exhaustive January 2014–April 2020 2020 NDSS 102,482 exhaustive not-stated 2021 USENIX 501,349 exhaustive mid 2014 to mid 2019 2021 USENIX 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 9,446 not-stated May–June 2021 2023 IEEE-SP 134,196 exhaustive 2022-02-21 2023 IEEE-SP 47,207 purposive 2022-02-21 2024 WWW 160,000 not-stated not-stated 2024 CCS 156,997 exhaustive available as of January 3rd, 2024 2024 USENIX 113,099 exhaustive August 2023 2024 USENIX 39,592 pre-existing-dataset 2023 of those 27 rows, by sampling word: exhaustive=19, not-stated=3, top-n=3, purposive=1, pre-existing-dataset=1 -- every extension-corpus tuple (any store, any size) -- Year Venue n Source Sampling Snapshot ---- ------- --------- ------------------------------------------ -------------------- ---------------------------------- 2010 USENIX 1,827 Mozilla add-ons web site random October 2008 2010 USENIX 699 Mozilla add-ons web site top-n November 2009 2010 USENIX 100 official Chrome extensions gallery top-n not-stated 2010 USENIX 40 addons.mozilla.org top-n not-stated 2011 IEEE-SP 1,139 popular Chrome extensions purposive not-stated 2012 WWW 5,943 Chrome Web Store exhaustive not-stated 2012 USENIX 50 official Chrome extension directory top-n June 2011 2012 USENIX 50 official Chrome extension directory random June 2011 2012 USENIX 50 top 50 Google Chrome extensions top-n not-stated 2012 USENIX 42 top 50 Google Chrome extensions requiring top-n not-stated 2012 USENIX 20 top 20 Google Chrome extensions top-n not-stated 2013 CCS 30 Top 30 Chrome extensions top-n not-stated 2014 USENIX 47,940 Chrome Web Store not-stated not-stated 2014 USENIX 392 Anubis pre-existing-dataset not-stated 2015 IEEE-SP 1,000,000 crawling the web; Safe Browsing; Chrome We exhaustive 2011-03 to 2014-10 2015 WWW 18,030 Google Chrome Web Store exhaustive March 2014 2015 WWW 78 Extension Defender pre-existing-dataset March 2014 2015 WWW 17 Firefox extension store purposive not-stated 2015 WWW 11 Google Chrome extension store purposive not-stated 2017 WWW 12,132 Chrome Web Store exhaustive July 2016 2017 USENIX 10,620 Chrome Web Store top-n not-stated 2017 USENIX 10,620 Firefox Add-ons top-n not-stated 2017 WWW 10,000 Google Chrome Web Store top-n not-stated 2017 IEEE-SP 10,000 Google Chrome Store top-n not-stated 2017 IEEE-SP 2,000 Google Chrome Store purposive not-stated 2017 IEEE-SP 1,000 Google Chrome Store random not-stated 2017 IEEE-SP 1,000 Mozilla Firefox extension market top-n not-stated 2017 USENIX 721 Safari Extension Gallery exhaustive not-stated 2018 CCS 178,893 Chrome Web Store exhaustive crawled between September 2016 and 2018 CCS 2,790 Opera browser extensions exhaustive at the time of analysis 2018 USENIX 46 extension store purposive not-stated 2019 IEEE-SP 66,401 Chrome Extensions exhaustive not-stated 2019 NDSS 62,994 Chrome Web Store exhaustive 2018-02 2019 USENIX 59,255 Chrome Store exhaustive November 2017 2019 WWW 58,034 Google Chrome store exhaustive October 2017 2019 IEEE-SP 9,391 Firefox Add-ons exhaustive not-stated 2019 NDSS 8,646 Mozilla add-on store exhaustive 2018-02-23 2019 IEEE-SP 2,523 Opera Add-ons exhaustive not-stated 2019 WWW 1,000 custom seed list purposive not-stated 2020 CCS 152,341 Chrome Web Store exhaustive January 2014–April 2020 2020 NDSS 102,482 Chrome Web Store exhaustive not-stated 2020 CCS 102 Chrome Web Store convenience January 2014–April 2020 2021 USENIX 501,349 Chrome Web Store exhaustive mid 2014 to mid 2019 2021 USENIX 116,485 Chrome Web Store exhaustive April 2019 2021 IEEE-SP 30 Chrome Web Store purposive 2020-07-16 2021 IEEE-SP 15 Chrome Web Store and default browsers purposive 2020-11-13 to 2020-11-18 2022 CCS 186,434 Chrome Web Store exhaustive June 2020 2022 CCS 180,361 Chrome Web Store exhaustive January 2022 2022 CCS 174,355 Chrome Web Store exhaustive February 2021 2022 USENIX 27,342 Carnus dataset by Karami et al. pre-existing-dataset not-stated 2022 CCS 23,363 Firefox extension store exhaustive April 2021 2022 USENIX 9,446 Chrome Web Store not-stated May–June 2021 2022 USENIX 3,311 Dataset of detected extensions from Laperd pre-existing-dataset not-stated 2023 IEEE-SP 134,196 Chrome Web Store exhaustive 2022-02-21 2023 IEEE-SP 47,207 Chrome Web Store purposive 2022-02-21 2023 USENIX 100 Google Chrome Web Store top-n January 2023 2023 USENIX 20 Chrome Web Store top-n as of April 9, 2022 2023 USENIX 20 Firefox Add-ons top-n as of April 9, 2022 2024 WWW 160,000 Chrome Web Store not-stated not-stated 2024 CCS 156,997 Chrome Web Store exhaustive available as of January 3rd, 2024 2024 USENIX 113,099 Chrome Web Store sitemap exhaustive August 2023 2024 CCS 104,484 Carnus pre-existing-dataset not-stated 2024 USENIX 39,592 Chrome Web Store pre-existing-dataset 2023 2024 CCS 26,591 Mozilla Add-ons Store exhaustive available as of January 3rd, 2024 2024 USENIX 1,000 custom evaluation dataset purposive not-stated 2024 USENIX 200 chrome-mal-ids repository top-n 2023 2024 WWW 170 Google Chrome extension samples pre-existing-dataset not-stated 2025 WWW 15 Chrome Web Store purposive not-stated 2025 NDSS 10 custom Chrome extension set purposive not-stated 2025 USENIX 9 Chrome Web Store top-n not-stated 2025 WWW 3 Firefox Add-ons Store purposive not-stated 2026 PETS 100 Torres et al. wallet dataset pre-existing-dataset 2023 exact versions 2026 PETS 100 Chrome Web Store purposive November 2025 2026 PETS 85 Chrome Web Store purposive November 2025 Snapshot date / list version stated: 38 of 74 extension-corpus tuples (51.4%) of those, carrying an actual year: 37 of 74 (50.0%) stated but with NO year in the value: 1 2018 CCS n=2790 :: "at the time of analysis" counted by paper: 21 of 38 papers (55.3%) corpus-wide BASELINE, a DIFFERENT population: population.listVersion stated by 2455 of the 5712 papers that drew any study population = 43.0% This reproduces OVERVIEW.md's row for the same field exactly. It is NOT comparable to the 55.3% above: different population, different unit. ============================================================================== E. WHAT THE IN-SCOPE PAPERS USED ============================================================================== Tool Category In-scope papers Share of 68 ------------------ ---------------------- --------------- ----------- Selenium browser-automation 13 19.1% Chrome browser 12 17.6% Chromium browser 10 14.7% Google Chrome browser 9 13.2% Firefox browser 8 11.8% Python other 6 8.8% Docker infrastructure 6 8.8% VirusTotal classification-service 5 7.4% Mozilla Firefox browser 5 7.4% Puppeteer browser-automation 5 7.4% Esprima program-analysis 4 5.9% mitmproxy proxy-interception 4 5.9% EasyList blocklist 4 5.9% Ghostery other 3 4.4% Selenium WebDriver browser-automation 3 4.4% EasyPrivacy blocklist 3 4.4% XHOUND program-analysis 3 4.4% Xvfb infrastructure 3 4.4% Brave browser 3 4.4% ChatGPT llm 3 4.4% -- classification[].method over the in-scope papers that classify -- denominator: 61 of 68 in-scope papers carry a classification tuple Method Papers Share of 61 ------------------- ------ ----------- manual-labelling 32 52.5% heuristic-rules 29 47.5% static-analysis 13 21.3% third-party-service 11 18.0% curated-database 9 14.8% dynamic-analysis 9 14.8% blocklist 6 9.8% regex-or-signature 4 6.6% supervised-ml 3 4.9% other 2 3.3% unsupervised-ml 2 3.3% llm 1 1.6% graph-analysis 1 1.6% ============================================================================== F. FULL-TEXT PROBES — upper bounds, over the 5,859-paper corpus ============================================================================== Probe Papers in corpus Of which in scope ----------------------------- ---------------- ----------------- Manifest V3 named 19 11 Manifest V2 named 10 9 declarativeNetRequest 11 6 web-accessible resources 27 19 chrome-extension:// URL 13 12 moz-extension:// URL 4 4 use_dynamic_url 2 2 CRXcavator 4 1 chrome-stats / crx4chrome 0 0 Chrome Web Store sitemap 5 5 an extension-store API 1 1 extension enumeration/probing 5 5 LLM used on extensions 552 6 Every figure here is an upper bound: a match is a mention, not a use. ============================================================================== G. THE 2025-2026 SLICE — is anything superseded? ============================================================================== in-scope papers 2025-2026 (provisional venue-years): 11 2025 CCS [blocking-efficacy] Local Frames: Exploiting Inherited Origins to Bypass Content Blockers. 2025 IEEE-SP [security] Security Analysis of Master-Password-Protected Password Management Protocols. 2025 NDSS [fingerprintability] Cascading Spy Sheets: Exploiting the Complexity of Modern CSS for Email and Browser Fi 2025 USENIX [fingerprintability] Double-Edged Shield: On the Fingerprintability of Customized Ad Blockers 2025 USENIX [privacy-leak] Big Help or Big Brother? Auditing Tracking, Profiling, and Personalization in Generati 2025 USENIX [people] "I have no idea how to make it safer": Studying Security and Privacy Mindsets of Brows 2025 WWW [blocking-efficacy] Breaking the Shield: Analyzing and Attacking Canvas Fingerprinting Defenses in the Wil 2025 WWW [people] Broken Access: On the Challenges of Screen Reader Assisted Two-Factor and Passwordless 2025 USENIX [security] Phishing Attacks against Password Manager Browser Extensions 2026 PETS [privacy-leak] The Masks We (Think We) Wear: Privacy Threats of Browser-Extension Wallets in the Web3 2026 PETS [blocking-efficacy] Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad Block in-scope papers whose schema records an LLM tool or an LLM classification method: 4 2024 WWW Experimental Security Analysis of Sensitive Data Access by Browser Extensions. 2025 USENIX "I have no idea how to make it safer": Studying Security and Privacy Mindsets of 2026 PETS The Masks We (Think We) Wear: Privacy Threats of Browser-Extension Wallets in th 2026 PETS Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad ============================================================================== H. MEASURED RESULTS QUOTED ON THE PAGE (detection[].prevalence) ============================================================================== 2017 USENIX Extension Breakdown: Security Analysis of Browsers Extension Resources installed-extension enumeration 100% of 10,620 Chrome and 10,620 Firefox extensions detected. technique: Probed extension identifiers using timing or Firefox exception behavior. 2017 IEEE-SP XHOUND: Quantifying the Fingerprintability of Browser Extensions. browser-extension fingerprintability At least 9.2% on arbitrary domains and more than 16.6% on popular URLs among the top 10,000 Chrome extensions technique: Static patching, honey pages, DOM instrumentation, and DOM comparison 2019 NDSS latex-gloves-protecting-browser-extensions-from-probing-and-revelation WAR probing by web pages 2,572 out of 10,000 domains probed for 45 different extensions technique: Monitored chrome-extension requests across popular pages 2020 NDSS Carnus: Exploring the Privacy Threats of Browser Extension Fingerprint web-accessible-resource extension fingerprinting 25,866 extensions detected technique: Parsed manifests and probed each WAR URL using response status codes 2018 CCS Mystique: Uncovering Information Leakage from Browser Extensions. Privacy-sensitive information leakage 3,868 of 181,683 extensions (2.13%) technique: Hybrid dynamic taint tracking with static data-flow and control-flow analysis 2024 USENIX Arcanum: Detecting and Evaluating the Privacy Risks of Browser Extensi Sensitive-data taint flows 3,028 extensions (2.68%) across 113,099 analyzed extensions technique: Dynamic taint tracking from sources to exfiltration or storage sinks. 2020 CCS You've Changed: Detecting Malicious Browser Extensions through their U malicious browser extensions 143 malicious extensions in 21 clusters technique: Rating anomalies, malicious keywords, and manual code-delta inspection 2019 IEEE-SP EmPoWeb: Empowering Web Applications with Browser Extensions. exploitable extensions 197 extensions technique: Manual code review and exploit testing 2023 IEEE-SP Detection of Inconsistencies in Privacy Practices of Browser Extension Flow-to-Dashboard inconsistencies 1,290 inconsistent flows across 820 extensions technique: Compared extracted data flows with positive and negative Dashboard statements. 2026 PETS Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Up ad blocking MV3 and MV2 means were 1.42 and 1.46 blocked ads. technique: Subtracted ad-blocker display-ad counts from no-blocker baseline. ============================================================================== H2. THE PEOPLE HALF — the guard for "all but one are from 2021 onwards" ============================================================================== 2017 CCS Let's Go in for a Closer Look: Observing Passwords in Their Natural Habitat. 2021 IEEE-SP They Would do Better if They Worked Together: The Case of Interaction Problems Betwe 2022 PETS How Can and Would People Protect From Online Tracking? 2023 USENIX Defining "Broken": User Experiences and Remediation Tactics When Ad-Blocking or Trac 2024 PETS Block Cookies, Not Websites: Analysing Mental Models and Usability of the Privacy-Pr 2025 USENIX "I have no idea how to make it safer": Studying Security and Privacy Mindsets of Bro 2025 WWW Broken Access: On the Challenges of Screen Reader Assisted Two-Factor and Passwordle people papers: 7; from 2021 onwards: 6; before 2021: 1 ============================================================================== Z. EVERY NON-CORPUS NUMBER ON THE PAGE ============================================================================== These come from primary sources outside the extraction and are re-checked by scripts/external_checks_browser_extensions.sh, not by this report. They are listed here so scripts/check_page_numbers.mjs can run whole-page. Chrome Web Store / Firefox AMO live listing counts external_checks (chromewebstore.google.com, addons.mozilla.org) Chrome Manifest V2 end-of-life dates developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline Chrome declarativeNetRequest rule limits developer.chrome.com/docs/extensions/reference/api/declarativeNetRequest Firefox moz-extension:// UUID randomisation bugzilla / MDN use_dynamic_url manifest key developer.chrome.com/docs/extensions/reference/manifest/web-accessible-resources Extension IDs / update-manifest endpoints clients2.google.com/service/update2/crx CRXcavator status github.com/duo-labs/crxcavator Repository states, tags and last-push dates GitHub API
Folding
No name fold was written, and that is a decision. Everything the page counts is an enum (classification[].method), an integer (population[].n), a date (population[].listVersion), a hand verdict, or a published regex. The one place a fold would have been needed — free-text tool names in §E — is reported as a ranking with a case-and-punctuation skeleton merge only, exactly as OVERVIEW.md says such fields may be reported, and the page does not quote any figure from it.
The residue that a fold would normally print is instead the homograph role: 13 papers where the word means something else. In full: IPv6 extension headers, ARM's Memory Tagging Extension, WordPress plugins, WeChat and app-in-app mini-programs, Deno third-party modules, Google Drive apps in the Chrome Web Store, RapidAPI listings, “manifest file extension”, “malicious file extensions”, “file extensions” used to identify ML model formats, “extension of the Android Middleware”, and two mobile-app-store papers. Each is listed with its reason inline in ROLE in scripts/ext_fold.mjs.
Quotes and per-paper figures
node scripts/verify_extension_figures.mjs --show. 66 of 66 needles located as of 2026-09-221). Every needle is a phrase from the paper, checked against two renderings — paper.cols.txt with whitespace collapsed, and an independent pypdf extraction of paper.pdf — because the two fail on different sentences. On this run all 66 were found in .cols and none needed the PDF route, which is worth recording: the fallback exists and was not exercised, so it is not evidence that it works here.
Needles were deliberately rewritten from bare numbers into verbatim phrases after the first run: the first version had 53 of 58 needles under 20 characters, which is the length at which a match can happen by accident and “0 not located” starts passing for the wrong reason. How much of the page the 66 actually cover is now measured, not asserted. The script prints it: 40 of the 66 guard a figure the page prints, 9 guard a figure it does not, and 17 carry no number distinctive enough to test either way — and it lists all 26 of the latter two groups so they can be read. Until 2026-09-22 this section said every needle is a phrase the page prints, which was an assertion and was false: a needle is the paper's sentence, and the page usually states the figure in its own words. A needle in the 9 still does real work — it proves the figure exists in the paper — it just is not evidence about this page. Quote the 40, not the 66, when you mean page coverage.
After the rewrite three needles are short: “= 1.46 ” and “= 1.42 ” from a figure caption in the PoPETs 2026 MV3 paper, and “2,523” — the Opera corpus of [1Somé, Dolière Francis (2019): "EmPoWeb: Empowering Web Applications with Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)], added when the self-audit corrected the nothing measures Opera claim. All three were read in context by hand. The script prints the count, so the figure in this sentence is checkable against it.
Three needles failed on the first run and were not fabrications; all three were the page paraphrasing where the paper had different wording, and all three were corrected on the page rather than in the checker:
| Paper | Page had | Paper says |
|---|---|---|
| VEX, USENIX Security 2010 | 1,827 | “a random sample of 1827 extensions from the Mozilla add-ons web site” — no thousands separator |
| IBEX, IEEE S&P 2011 | 1139 Google Chrome extensions | “manifests for over 1,139 popular Chrome extensions” |
| Browser Feature Usage, IMC 2016 | 10% of browser features | “(approximately 10%) that are used by websites, but which ad and tracking blockers prevent from executing more than 90% of the time” |
One needle is present in paper.cols.txt only after undoing a column splice: the CCS 2024 sentence about the store's rounded install counts reads “the Store pro|dataset2 . 3,398 extensions … |vides only inaccurate numbers for popular extensions” in the decolumned text, so the needle is the second half of the phrase.
The extraction's own quotes, behind the store-size table
The per-paper figures above are checked against the papers. Separately, the
evidence.quote the extraction attached to each population tuple is checked
against the rendering the extractor read, because the store-size table is built
entirely from those tuples. node scripts/ext_quotecheck.mjs:
| Verdict | Tuples |
|---|---|
| exact | 54 |
| partial (≥60% of 5-word windows present) | 15 |
| rescued from the PDF | 4 |
| below threshold in both renderings | 1 |
| total extension-corpus tuples with a quote | 74 |
Re-run 2026-09-21 with the PDF fallback. The old figure was 5 below threshold; 4 of the 5 reach threshold against an independent pypdf rendering of the same paper.pdf and are reported as rescued. exact (54) and partial (15) did not move. See Quote-check refresh, 2026-09-21 at the foot of this page.
The one still below threshold in both renderings is the IBEX 2011 n = 1,139 tuple, at 56% coverage — the first row of the table below, and a column splice. By elimination the other four are the remaining rows: Ad Injection at Scale 2015, Experimental Security Analysis 2024 and both Privilege Separation in HTML5 2012 tuples.
Two of the four rescued are the two the table calls paraphrases, and that is a caution about what rescued means. It means ≥60% of the quote's five-word windows are present in the second rendering — not that the extraction is verbatim. A paraphrase that keeps most of the paper's wording can clear the threshold in a cleaner rendering. The hand verdicts below are the stronger evidence and they are unchanged.
Below threshold is not “unsupported”. All five were read by hand against
paper.cols.txt and all five are present; three are column splices in the
de-columned text and two are model paraphrases of a nearby sentence:
| Tuple | What the paper actually has |
|---|---|
| IBEX 2011, n = 1,139 | “Figure 2 shows manifests for over 1,139 popular Chrome extensions, to the manifest of an extension called…” — a column splice runs the two columns together mid-sentence |
| Ad Injection at Scale 2015, n = 1,000,000 | “we dynamically execute 25 million binaries and 1 million extensions” and “we cast a wide net and dynamically analyzed over 1 million extensions”. The quote's phrasing (“We used WebEval to evaluate all three sources”) is a paraphrase; the figure is the paper's |
| Experimental Security Analysis 2024, n = 170 | “we use the extension samples provided by Google Chrome” — present, split across a column boundary |
| Privilege Separation in HTML5 2012, n = 42 and n = 20 | both quotes paraphrase the paper's Figure 5 discussion and its top-20 bundling result; the paper's own sentence is “19 out of the top 20 extensions exhibited bundling” |
None of these five figures is used on the page as a headline. The 1,000,000 row is on the page, and it is on the page precisely as a caution that it is not a store size.
A trap that did not fire here, and why it might elsewhere
detection[].prevalence is a model summary of a result, not a quotation (see corpus). Every per-paper figure on the page was therefore checked against the paper's own full text, not against the attached evidence.quote. Two figures were dropped in drafting because the prevalence string and the paper disagreed on the denominator:
- Hulk's population tuple records 47,940 Chrome Web Store extensions; its own text says “Our analysis of 48,332 Chrome extensions”. The difference is a second corpus of 392 samples from Anubis. The page uses 48,332, the paper's number.
- The 2015 malvertising paper's tuple says 18,030; its abstract says “over 18,000”. The page uses 18,030, which is in the paper's methodology section, and says “of 18,030” rather than “over 18,000”.
External sources
Everything below was fetched on 2026-09-02 and is re-checkable with bash scripts/external_checks_browser_extensions.sh. That script prints FAILED explicitly per check, so a blocked or rate-limited call cannot read as a pass.
Verified, and used
| Claim on the page | Primary source | What was actually seen |
|---|---|---|
| No Chrome Web Store listing API | developer.chrome.com/docs/webstore/api_index | the documented API publishes and manages the developer's own item; no list or search endpoint |
42 sitemap shards, lastmod 2026-09-01, 356,984 distinct item IDs | chromewebstore.google.com/sitemap + all 42 shards | sandbox/cws_sitemap_count.sh; every <loc> parsed to a 32-character ID, 0 unparsed, 0 duplicates |
| 30 extensions / 8 themes / 2 dead in a 40-item sample | the store's own detail pages | sandbox/cws_sitemap_sample.sh; existence read from <title> |
/detail/<id> is a soft-404 | three fetches | a fabricated ID and uBlock Origin both return HTTP 200 titled exactly “Chrome Web Store”; uBlock Origin Lite returns “uBlock Origin Lite - Chrome Web Store” |
| uBlock Origin gone from the store | the sitemap ID list and the detail page | cjpalhdlnbpafiamejdnhcphjbkeiagm is not among the 356,984 IDs, and its page is a soft-404 |
CRX endpoint works; 204 on a bad prodversion | clients2.google.com/service/update2/crx | prodversion=120.0 → HTTP 204, 0 bytes; prodversion=140.0.7339.80 → HTTP 200, 9,665,461 bytes, magic Cr24 |
| Install counts rounded | two store pages | “16,000,000 users” (AdGuard AdBlocker), “200,000 users” (Consent-O-Matic); no precise numeric field anywhere in either response |
| AMO API: 99,677 extensions, exact daily users | addons.mozilla.org/api/v5 | count: 99677 for type=extension; average_daily_users: 10766677 for uBlock Origin |
data_collection_permissions coverage | the same API, 400 most-installed | 314 empty (78.5%), 63 [“none”] (15.8%), 23 named (5.8%); sandbox/amo_data_collection.py |
| Edge has no bulk listing; Opera has none | Microsoft Learn; addons.opera.com | Edge's REST API covers your own submissions; Opera's robots.txt and sitemap.xml both 404 |
| Manifest V2 timeline, all six milestones | developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline | page footer “Last updated 2026-07-08 UTC”; every quoted phrase on the page is verbatim from it |
| Edge has set no MV2 date | learn.microsoft.com ExtensionManifestV2Availability | “More information about and the timeline of the migration hasn't been established.” |
Firefox keeps blocking webRequest | MDN webRequest vs Chrome webRequest | MDN documents webRequestBlocking as an ordinary permission with no MV3 note; Chrome's says “only available to policy installed extensions” |
declarativeNetRequest limits (nine values) | the reference page's Properties table | read from the raw HTML, not from a summary |
| Service-worker lifetime | …/service-workers/lifecycle | “After 30 seconds of inactivity”; “longer than 5 minutes”; “Any global variables you set will be lost” |
use_dynamic_url semantics, Chrome 130 | the manifest reference + the chromium-extensions announcement | “only allow resources to be accessed through a dynamic ID … regenerated when the browser restarts or the extension reloads”; the version comes from the announcement because the reference page carries no version badge |
Firefox moz-extension UUID | MDN web_accessible_resources | “This ID is randomly generated for every browser instance.” |
| Extension ID derivation | Chromium components/crx_file/id_util.cc | “First 16 bytes of SHA256 hashed public key”; ConvertHexadecimalToIDAlphabet maps hex onto a–p “to avoid ever having a completely numeric host” |
| Chrome Web Store 2026 policy | developer.chrome.com/blog/cws-policy-updates-2026 | “strictly necessary to the extension's disclosed single purpose”; “Enforcement … will begin on August 1, 2026.” |
| Cyberhaven, December 2024 | the company's own post, Internet Archive capture 2024-12-27 | the live URL now redirects to a support site; the archived capture carries the quoted sentences |
| Tool and artefact states | GitHub API and direct fetches | see the table on the page; crxcavator.io does not resolve and duo-labs/crxcavator is HTTP 404 |
| “Rob Wu's crxviewer” | GitHub user API | Rob–W → name: Rob Wu |
Rejected
| Source | Why |
|---|---|
backlinko, aboutchromebooks, sitew.com and similar “how many Chrome extensions” listicles | not primary, and they disagree with each other by a factor of two. The page says Google publishes no count instead of picking one |
| Wikipedia's Chrome Web Store article (“about 138,000 extensions”) | its own citation is ChromeStats, a third-party tracker, retrieved 2022-12-28 — a secondary source dated earlier than the year the prose claims |
ntcompatible, mlq.ai, windowsforum and similar, all asserting Edge will drop MV2 “by end of 2026” | contradicted by Microsoft's own policy page, updated June 2026, which says no timeline has been established |
extensionbooster.net, moldstud.com “Chrome Web Store 2026 compliance guides” | SEO content farms; replaced with Google's own policy post |
| Secondary reporting of “35 extensions / 2.6M users” in the Cyberhaven campaign | no primary source found for the aggregate. The page cites only what Cyberhaven itself stated, and says so |
dl.acm.org for the Mystique artefact | HTTP 403 to every non-browser client; the GitHub repository was used instead |
A model's recall of the declarativeNetRequest limits and the MV2 dates | discarded on principle: both changed in 2025–2026, after training. Every value on the page was fetched |
Could not be established
- No authoritative count of Chrome Web Store extensions exists. Google publishes none. The page reports the sitemap's 356,984 items with the unit stated, and converts it with the 200-item CRX sample (146 extensions of 198 served, 73.7%) to “on the order of 260,000 extensions”, saying in the same sentence that a 200-item sample can only bound that loosely. The earlier 40-item sample is superseded for the composition question and supports nothing narrower than roughly three-quarters extensions. Until 2026-09-22 this bullet said the page declined to convert at all, and cited the 40-item sample for it — a description of a decision the page had already stopped making.
- Nothing. The Manifest V2 question that looked like a discrepancy was resolved by measurement rather than left open: 17 of the 198 CRXs served are
manifest_version: 2, which two days after the MV2 removal looks like the sitemap lagging the store. Reading the manifests shows it is not — 0 of the 17 are extensions; 9 are themes and 8 are legacy Chrome apps, neither of which the MV2 phase-out touches. The page states this, because a researcher filtering a corpus onmanifest_versionwill otherwise conclude that MV2 extensions survived. - Apple's current position on Manifest V2 in Safari.
developer.apple.comserves client-rendered pages with no fetchable text, and the only Apple primary source found is a 2022 WWDC transcript. The page therefore says only that the standalone Safari extension gallery no longer exists, which is checkable, and makes no claim about Safari's manifest support. - Whether the two CRX 204s in the sample are refusals or throttling. The endpoint's 204 is documented nowhere and is the same response as for an implausible
prodversion. The published script raises on it rather than counting it as “no such extension”; the sweep counts it as its own outcome. chrome-stats.comcontent. HTTP 403 to every automated fetch. It is named on the page as the source behind most third-party counts, which is checkable from Wikipedia's citation, and nothing is quoted from it.
Original measurements on this page
Five numbers on the page are not in any paper and not in any vendor document. Each is a single-day snapshot and the page says so.
| Measurement | Script | Result | Weakness |
|---|---|---|---|
| Chrome Web Store size, from Google's own sitemap | sandbox/cws_sitemap_count.sh | 42 shards, 356,984 distinct item IDs, 0 unparsed URLs, 0 duplicates | items, not extensions; one day |
| What those items are, from the store page | sandbox/cws_sitemap_sample.sh | 40-item deterministic sample: 30 extensions, 8 themes, 2 with no live detail page | n = 40, and the extension/theme split is read from the store page's category breadcrumb, which is weaker evidence than the manifest |
| What those items are, from the manifest | sandbox/war_dynamic_url.py | 200-item sample, 198 CRXs served: 146 extensions, 44 themes, 8 legacy Chrome apps | the better of the two; supersedes the row above for the composition question |
use_dynamic_url adoption | sandbox/war_dynamic_url.py | 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; of the 146 extensions, declare web_accessible_resources: 46 (31.5%); Manifest V2 items still served by the CRX endpoint: 17 | n = 200 items, of which 146 are extensions and 46 declare web-accessible resources — so the “0 of 46” is over a small denominator. A deterministic every-1784th sample of the sitemap, representative of listed items and not of installs |
AMO data_collection_permissions coverage | sandbox/amo_data_collection.py | 400 most-installed: 78.5% empty, 15.8% [“none”], 5.8% named | most-installed, not random — the sample is biased towards the add-ons most likely to have been updated, so the true coverage across the whole catalogue is probably lower |
The sampling design in the two sitemap samples is every-k-th over a sorted list of IDs, not random, so a re-run on the same sitemap reproduces the same items exactly. The AMO sample is not of that shape: it is the top 400 by installs, a biased frame, labelled as one in its row above. That is deliberate: a random sample would be unreproducible, and the store changes daily anyway.
The published script
pages/ext_corpus.py is embedded on the page as a downloadable <file> block and carries the whole file, not an abridgement; scripts/build_ext_codeblock.py --check fails if the embedded block stops being byte-identical to the committed one. An earlier draft published only the docstring, which is the failure that guard exists to prevent.
It was mutation-tested, not read:
| Mutant | Expected | Observed |
|---|---|---|
item_exists body replaced with return True | self-test fails | SELF-TEST FAILED — the two negative cases fire |
the status == 204 branch in download_crx disabled | self-test fails | SELF-TEST FAILED — “a two-part prodversion should have raised” |
That is the point of the three-case self-test. A one-case test of the form “the extension I know exists is found” passes on the first mutant, which is exactly the shape of a check that verifies nothing (see corpus and the passing wait condition note in the repository's known traps).
Bibliography
32 entries appended to bibliography before the closing </bibtex>, against a fresh ?do=export_raw of the live page rather than the stale workdir copy. Checks run before saving:
- key collisions against the live page: 0
- internal duplicates in the additions: 0
- DOI, normalised-title and URL collisions against the live page: 0 — a key-string check alone would not have caught the same paper already present under a different key
- 17 of the page's keys already existed and were reused rather than re-created — that is the check that matters most, because a second entry for the same paper under a new key breaks nothing visibly and corrupts the bibliography permanently:
franken2018_cookiejar,karami2020_carnus,laperdrix2021_fingerprinting,agarwal2022_helping,agarwal2024_peeking,pantelaios2024_fv8,thomas2015_injection,borgolte2020_performance,bui2023_detection,ukani2025_local,trampert2025_cascading,demir2024_bannertools,torres2023_wallet,vekaria2025_bighelp,wang2026_masks,elhajjchehade2025_double,lukic2026_mv3
PoPETs and USENIX index records carry no authors and no DOI (100% of both venues), so those entries were built from the venue landing pages. scripts/fetch_authors.py returned affiliations glued into the author list for four of them (Berkeley, Santa Barbara, UC Davis, DeustoTech, Eurecom, UNIRC, UCL, UC3M); those were removed by hand. The two USENIX 2010 records added here share a legacy landing URL with no per-paper page, and their authors were read from the paper's own first page. Every URL in the additions file was then fetched: 16 of 16 return HTTP 200, after two USENIX slugs were corrected in review (see the reviewer log).
Reviewer log
Five review passes, all told explicitly that the reviewer's own context may not be exhaustive and all handed a frozen snapshot of the page, the report script and its output, the external-check output, and this log. Corrections were applied after each pass returned, not while it was running, so each finding can be attributed to what the reviewer actually saw.
There is more than one freeze, and saying so matters, because a finding is only
attributable if you know which text produced it. The three focused passes saw
out/ext/freeze/; the generic pass of 2026-09-02 saw out/ext/freeze2/, the
re-frozen copy taken after those passes' fixes were applied; the generic pass of
2026-09-22 saw out/ext/freeze3/, taken after the deferred findings below were
closed. Until 2026-09-22 this paragraph said the same freeze and named only
the first — which the 2026-09-02 pass flagged about itself, and which stood
uncorrected for three weeks.
freeze3 was not a clean freeze either: the external-check re-run
overwrote external.txt inside it while pass 5 was reading it, and pass 5
noticed and said so. Nothing in its findings depends on that file's live values,
but it is the second time this page has made the same mistake, so it is recorded
here rather than quietly fixed.
Rejections are recorded as carefully as fixes: they are the only evidence of whether a reviewer slot is worth its cost.
Pass 3 — external currency (sonnet)
Re-ran scripts/external_checks_browser_extensions.sh independently and fetched
every external claim from its primary source.
| Finding | Disposition |
|---|---|
The AMO extension count moved between the freeze and the review: count went 99,677 → 99,678 in about fifteen minutes. Every other line of the 175-line external-check output was byte-identical | accepted, and it improved the page. The figure is a live counter, not a published statistic. A footnote now says so and gives both readings, because a reader who re-runs the query and gets a different number should be able to tell drift from error |
| “Manifest V2 is gone, and it went last week” is wrong about the calendar: 2026-08-31 is the Monday of the same week as 2026-09-02 | accepted — already corrected to “two days ago” before this pass returned, which is why the reviewer saw the old wording. Recorded anyway: the reviewer was right about the snapshot it was given |
developer.chrome.com/docs/webstore/api_index redirects to …/api | accepted. The footnote now cites the canonical path and notes the redirect |
A secondary blog claims Chrome raised the declarativeNetRequest static-rule ceiling “from 30,000 to roughly 330,000”. The reviewer checked it against Chrome's own reference page and its 2023-11-15 content-filtering post and found no corroboration | recorded, not used. The page publishes only what the Properties table says |
All nine declarativeNetRequest limits, both service-worker figures, use_dynamic_url and its Chrome 130 provenance, the Firefox UUID claim, Firefox's retained webRequestBlocking, Edge's absent MV2 date, the Chromium ID derivation, the Chrome Web Store 2026 policy date and the Cyberhaven quotes: all verbatim-correct against sources fetched during the review | no change needed |
| Every URL on both pages fetched: 27 of 27 as expected, the only non-200s being the two the page itself asserts are dead | no change needed |
Apple's current Safari Manifest V2 position could not be verified — developer.apple.com serves a client-rendered shell with no fetchable text | no change needed; the page makes no claim about Safari's manifest support, only about the gallery, and the omission is disclosed |
A targeted search for a 2026 development the page has missed — a Manifest V4, a new enumeration countermeasure, a change to the declarativeNetRequest ceilings — turned up nothing | recorded. That is the pass's most useful negative result and it is not visible anywhere else |
Pass 1 — figures versus the script (sonnet)
Re-ran both scripts independently and mutation-tested the guards.
| Finding | Disposition |
|---|---|
A script bug in the report itself. The “exhaustive/whole-store samples only” table was selected by a filter that took a row if its sampling word was exhaustive, or if its n exceeded 5,000 and its sampling word was anything other than top-n — so it admitted any large corpus not literally labelled top-n. (The expression itself contains a logical-or and cannot be written in a DokuWiki table cell; it is in the comment above the corrected line in scripts/report_browser_extensions.mjs.) Five rows in a table headed exhaustive only were not-stated, purposive or pre-existing-dataset. The table's header was a false claim about what the code selected. | accepted, and it is the most valuable finding of the four passes. The filter is now the size alone (n ≥ 5,000) and the paper's own sampling word is a column, so nothing can be silently excluded and the header cannot lie. The comment above the line records the old expression and what it let through |
The page's store-size table said “every Chrome-store corpus in the 68” and was not. It silently dropped 9,446, 47,207, 160,000 and 39,592 while keeping 47,940, whose sampling word is the same not-stated as two of the dropped rows. A draft fix that named “three further” excluded corpora still missed the 160,000 row | accepted. The table is now generated mechanically from the report's rule and carries all 27 rows, with a sentence naming the spread of sampling words as the finding rather than hiding it |
| The page's own quote-check tally was stale: it said “54 of 54” against a tool that printed 57/57 | accepted. Now 60 of 60, and the sentence names the rendering |
| Arcanum was cited to the wrong section: §3 is “System Design”; the target-site discussion is in §4.1 and the 2.68% is in §4.2 with Table 4 | accepted — corrected before this pass returned, and the section titles are now checked against the paper's own headings by verify_extension_figures.mjs rather than asserted |
| An apples-to-oranges comparison: “51.4% … is better than the corpus-wide 43.0%” compares a tuple-level rate over 38 papers with a paper-level rate over 5,712 | accepted. The page now calls 43.0% a baseline and says in terms that the difference is not a measured effect. The report prints the same warning beside the figure |
| “Roughly flat for sixteen years” used only the numerator while the corpus quadrupled underneath it | accepted. The page gives both series and says the share shrank |
| The Safari row asserted “since Safari 13” with no check in the external script | accepted. The claim is dropped, Apple's own distribution sentence is quoted, and a Safari check is now check 24 in the external script |
| The reviewer could not reproduce the frozen outputs byte-for-byte, because both scripts had been edited after the freeze while it worked | recorded as a process failure, not a page defect. It is the reason the freeze exists and the reason it has to be a real freeze. The reviewer flagged it rather than assuming, which is the behaviour the brief asked for; four of its eight findings had in fact already been fixed in the unfrozen copy, and it said so each time |
Verified clean: the 218/68/47/21 splits and every percentage and sum, the year buckets closed at both ends, the <file> block byte-identical to the committed script, ext_corpus.py --self-test passing, and — by mutation-testing both directions — that the report really does exit 1 when a verdict has no candidate or a candidate has no verdict | no change needed |
Pass 2 — citations, quotes and attribution (sonnet)
Checked every citekey, every new BibTeX entry against the venue's own page or the paper's first page, every quoted sentence against the paper, and every external footnote against its source.
| Finding | Disposition |
|---|---|
A fabricated middle name in a BibTeX entry. snyder2016_browser listed “Taylor, Cynthia Bagier”. The paper's own first page, Crossref and the author's own pages all say Cynthia Taylor; “Bagier” appears nowhere | accepted. The entry is corrected. The name came from bibgen.mjs, which reads OpenAlex — so publisher metadata is not a defence: a generated entry needs the same check as a hand-written one. This is the finding that most justifies the pass |
Two dead URLs in the additions: usenixsecurity23/presentation/nisenoff-defining and usenixsecurity25/presentation/agarwal-mindsets both 404. The real slugs are nisenoff-broken and agarwal-shubham | accepted. Both corrected, and every one of the 16 URLs in the additions file was then fetched: 16 of 16 return 200 |
| The provenance's “11 reused citekeys” list named six keys the page never cited | accepted, and it exposed a real gap rather than a bookkeeping slip. Those six — agarwal2022_helping, borgolte2020_performance, ukani2025_local, demir2024_bannertools, torres2023_wallet, vekaria2025_bighelp — are all in-scope papers the page should have been citing. All six are now cited, one of them in a new section on how the eleven “does the protective extension protect?” papers operationalise “protect”. The count is now 49 citekeys, 17 reused, 32 new |
verify_extension_figures.mjs carried needles for four papers the page did not cite, inflating the needle count without checking anything on the page | accepted in effect, resolved the other way: rather than deleting the needles, the page now cites all four papers, so every needle checks something the page prints. Worth recording that the reviewer's proposed fix and the applied fix are opposite and both close the finding |
| “Three USENIX 2010 records” — there are two in the additions; the third was already live | accepted, corrected |
Five duplicate entry pairs already in bibliography — the same paper under two keys (lerner2016internet/lerner2016_internet and four others). None is cited by this page | recorded, not fixed. Out of scope for this page and a page-rewriting job, not a bibliography job. Filed as the dedupe-bibliography work item, with the note that a key-string check does not find these and the dedupe has to be by DOI and normalised title |
The Cyberhaven archived post could not be re-fetched — archive.org returned HTTP 429 throughout the review | no change; the capture was fetched successfully during the run and again during the currency pass, and the footnote names the capture date so a reader can retry |
| Verified clean: all citekeys resolve, no DOI/title/URL collisions between the additions and the live page, all 32 author lists correct apart from the one above, all 60 needles genuinely present in their papers, and every external footnote verbatim against its primary source | no change needed |
Pass 4 — generic, no checklist (fable)
It returned after the pages were first published; its findings were applied in a follow-up save. Twelve findings, of which four were already fixed by the self-audit below (Edge/Opera, “Nobody does”, “Nobody has published it”, and two placeholder slots that were filled between the freeze it saw and publication) — it said so where it could tell. The rest:
| Finding | Disposition |
|---|---|
| A novelty claim contradicted by the page's own report. The page said Torres et al. “turned the question around and measured the web… rather than only in a lab”; report §H records [2Sjö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)] doing exactly that in 2019, “2,572 out of those 10,000 domains probe for WARs” | accepted. The sentence now credits the 2019 precedent and says what Torres's contribution actually is: the object, not the framing |
| The methodology claimed more quote coverage than the guard gives. “Every per-paper figure quoted above was checked” — but several figures on the page (akhawe's 5%-of-functions, dong's 23-of-top-30, lukic's 21%-vs-0%) have no needle | accepted. The sentence now says the check covers the 64 needles and not every number on the page, and points at where the rest come from |
| “Every result older than those is a result about Manifest V2” over-reaches, and the page contradicts it three sections earlier by describing VEX and Djeric attacking Firefox's XPCOM model | accepted. Scoped to Chrome, with the pre-MV2 model and the Firefox/Opera corpora named |
| The detection table's “Introduced” column credits the wrong paper: the page's own pitfalls section says WAR probing was introduced at CODASPY 2017, outside the corpus | accepted. The column is now headed Introduced, in this corpus |
| “They agree to within half a percentage point” — 2.68 − 2.13 = 0.55 | accepted, and the difference is now stated as a number |
| The opening box's 146 + 44 + 8 sums to 198, not 200, and the two refusals are explained 400 lines later | accepted. The box says “(198 served; the endpoint declined 2)” |
| This log contradicted itself three ways: “Four numbers” over a five-row table; “all three samples are every-k-th” when the AMO sample is a top-400; and “byte-identical apart from the docstring, which is all the page shows” when the page shows the whole script | all three accepted. The last is the serious one — it is the exact “published code must be the real script” failure, described in the log as though it had happened. The whole file is on the page and build_ext_codeblock.py --check proves it |
The external-check script has vacuous checks: check 1 prints its “expected” comment and no fetched evidence; check 9 probes an obsolete constant name and prints NOT FOUND rather than FAILED; the MDN webRequestBlocking quote in a page footnote is fetched by no check | accepted, and closed on 2026-09-22 (see Deferred findings, closed below). Originally deferred with this note: it is the trap this log lectures about, found in this log's own script. Recorded here and carried by the ext-generic-review work item |
Carnus row labels three things and gives two numbers; the “all but one from 2021 onwards” people claim has no guard; the log names out/ext/freeze/ when this pass saw freeze2 | accepted, and all three closed on 2026-09-22 — see Deferred findings, closed below. They were recorded here and carried by an ext-generic-review work item that, it turned out, was never actually filed: the only record they existed was this row |
| “The page answers its own question well… the What-to-Report box is the best section”, structure sound, all re-checked arithmetic correct, the published script reads correctly as code and as prose, and its self-test genuinely asserts negative cases | recorded |
The lesson this pass repeats. Three of its findings — the Torres novelty claim, the Manifest V2 over-reach, and the WAR-probing attribution — are each true in the sentence where they appear and false against a different section of the same page. That is the third run on this site where the generic pass earned its slot by reading the joins that no focused brief owns.
Pass 5 — generic, no checklist (fable), 2026-09-22
Run over out/ext/freeze3/ — the page, the provenance, the three scripts and
their fresh outputs — after the deferred findings below were closed, so that
the newest and least-reviewed material was the material under review. Twelve
findings confirmed, five raised as suspected. It opened with a process note that
is itself a finding: external.txt inside the frozen directory changed
while it was reading it, because the external-check re-run overwrote it. That is
pass 1's “the freeze was not a freeze” failure, committed again, by me.
| Finding | Disposition |
|---|---|
A fingerprintability paper was illustrating the eleven blocking-efficacy papers. The Does the Protective Extension Protect? section says “Eleven of the 68 papers ask this” and then closes with “[3Nguyen, 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)] and [4El 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)], above”. The report tags [4El 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)] fingerprintability, and the page's own detection table treats it as a fingerprinting paper — “fingerprints the filter list rather than the blocker” | accepted, and it is the best finding of the pass. The bullet now keeps [3Nguyen, 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)], says in terms that [4El 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)] is not one of the eleven, and gives the reason. This is the two-sections-meet failure again: both sentences are right where they sit |
| The needle fix left the stale number on the public page. The count moved to 66 and the provenance was corrected; the content page still said “64 of 64 located” and “a check on those 64” | accepted, fixed. Mine, from earlier the same day, and exactly the defect the residue table does not fix the prose rule exists for: I corrected the section that computes the number and not the sentence that quotes it |
Every needle is a phrase the page prints was false. At least six needles guard figures that appear nowhere on the page: 699, 29,428, 854, 18,937, 190 extensions, 5.5% | accepted, and resolved by measuring instead of asserting. verify_extension_figures.mjs now computes page coverage and prints the residue: 40 of the 66 guard a figure the page prints, 9 guard one it does not, 17 carry no number distinctive enough to test. The prose gives all three and says to quote the 40 when you mean page coverage. A needle in the 9 still proves its figure exists in the paper — it is just not evidence about this page |
The new failure mechanism covers 3 of 26 checks, and the closing line claimed all of them. fail()/expect() are called in checks 1, 9 and 26 only. Five checks use || echo FAILED, which prints the word without incrementing FAILS. The remaining eighteen are regex printers that emit nothing on a 403, a 429 or a reworded source. Yet the run ended “all checks produced fetched evidence” | accepted in full, and it is the sharpest thing said about my own work today. Closing the vacuous-check finding made the summary line claim more than before the fix. The header now sets out which checks are asserted, which are reported and which are unasserted, and the closing line says the 3 asserted checks passed; the other 23 are printers — an empty section is not a pass. Converting the other 23 is the ext-external-assertions work item, filed this time |
| The report printed a hand verdict as though the run computed it. “All ${extra.length} were read (2026-09-02)” — the count is computed, the reading and the date are string literals. If the corpus moved, the report would claim 40 papers were read on a day 32 were. Same shape at the 429 papers below it | accepted, fixed, and mutation-tested. The hand-read set is pinned at 32; a different count now prints HAND-READ SET IS STALE and exits non-zero (verified by setting it to 33). The 429 is computed from the distribution. The In-scope papers added: 0 line now says it is true by construction — the verdict map is keyed on candidates, so a non-candidate cannot carry a verdict — rather than posing as a measurement |
| The provenance contradicted the page on the store composition. Could not be established said the page “declines to convert [the sitemap count] into an extension count”, citing the 40-item sample; the page converts, from the 200-item sample, to “on the order of 260,000 extensions”. The provenance's own tools table says the 200-item sample supersedes the 40 | accepted, fixed. The bullet describes what the page actually does and its stated caveat; the TODO that rested on the 40-item sample is rewritten |
Only 51.4% state a snapshot date at all counted a non-date. The filter excludes only not-stated and null, so the Mystique Opera tuple's “at the time of analysis” passed as a stated date | accepted, fixed, and made computable. The report now prints the dated/undated split and the offending value: 37 of 74 (50.0%) carry an actual year, and the one exception is named. The per-paper 21 of 38 is unaffected — the same paper's Chrome tuple gives a real range. A non-sentinel value is not automatically a value |
| A top-400 sample generalised to the catalogue. “currently a signal about 5.8% of the catalogue”, where the provenance says the sample is most-installed, not random, so true coverage is probably lower | accepted, fixed. 5.8% is now stated as an upper bound from the head of the catalogue, not an estimate of it |
A bullet contradicted itself two clauses later. “Every whole-store paper rebuilt one” against “Corpora //do get passed between groups”// and two pre-existing-dataset tuples in the report | accepted, fixed — either rebuilt one or inherited one privately |
| A dangling reference. The page promised the provenance says “which one it is still missing”; no pass is missing | accepted, fixed — it now promises what each pass changed |
| The rewritten Carnus row carried its own changelog. “two novel vectors, not three” contrasts with a version the reader has never seen, and sat under a bullet calling Carnus a comparison of four vectors | accepted, fixed. The row names the two vectors Carnus introduces, says the other two are the rows above, and drops the not three |
The needle ledger does not close. The footnote's 60 → 64 → 66 story cannot accommodate the 2,523 Opera needle, which the next paragraph attributes to the self-audit | accepted, and fixed by giving up the reconstruction. The footnote now says the increments cannot be reconstructed from this log and that the count is only trustworthy from the script — which is the honest version and the one that stops the next person quoting a number out of prose |
It also flagged five suspected items it could not decide: the people verdict on the CCS 2017 password study (which, if it is instrument rather than people, makes the sentence all seven); the Firefox WAR result of 2017 sitting beside Firefox: closed to this vector with no reconciliation; four quantified claims the report does not print; and none needed the PDF route resting on a run without --show | recorded. The last is settled here: re-run with --show, 66 of 66 located in .cols, 0 via the PDF — the claim is true and now has a run behind it. The other four are carried by the ext-suspected-four work item. The CCS 2017 verdict is the one worth doing first: the guard added today formally depends on it, and it has no row in the borderline-verdicts table |
| Verified clean, by re-derivation: the role table, venue split, question table by row and by every period column, the 27-row sampling-word split, 146+44+8=198, 73.7%, 0.55 pp, the 49 citekeys — and that the H2 guard does what the page says and fails on both halves | no change needed |
What this pass says about the previous four. Six of its twelve findings are in material written the same day, by me, while closing the previous pass's deferred list: the stale 64, the overstated needle claim, the overstated external-check summary, the Carnus changelog, and two more. A correction is new, unreviewed text. The pass that reviews a fix is worth as much as the pass that found the defect, and this log had not been run that way before today.
Deferred findings, closed 2026-09-22
The generic pass of 2026-09-02 accepted findings it did not have time to fix and
recorded them as carried by an ext-generic-review work item. That item was
never filed. They survived only because the two rows above recorded them, which
is the argument for writing a deferred finding down in the same detail as a fixed
one: for three weeks this log was the entire backing store.
They are two rows but six findings, which is itself worth noting — a deferred row that bundles three things is three things that can be lost together. All six reproduced exactly as described, and all six are now closed.
| Deferred finding | What it was | How it is closed |
|---|---|---|
Check 1 of external_checks_browser_extensions.sh was vacuous | It grepped the raw HTML for a sentence, so the tag attributes broke its [^.] run and it matched nothing; it then printed its own -- expected: the Web Store API publishes/manages YOUR OWN item comment. The re-run on 2026-09-22 reproduced it: the whole of check 1 was two lines, one of them the comment. An assertion with no fetched evidence under it, in the script whose header promises the opposite | The check strips tags before matching, and an expect helper prints the located sentence or records a failure. It now prints Google's own “The Chrome Web Store API lets you programmatically publish and manage your Chrome Web Store items”, and separately probes for a listing endpoint rather than asserting there is none |
Check 9 probed an obsolete constant and printed NOT FOUND | MAX_NUMBER_OF_DYNAMIC_AND_SESSION_RULES is the pre-Chrome-120 constant and is gone from the reference page. The check printed NOT FOUND as an ordinary output line inside a run that exited 0 | The two constants that replaced it, MAX_NUMBER_OF_DYNAMIC_RULES and MAX_NUMBER_OF_SESSION_RULES, are probed instead, plus MAX_GETMATCHEDRULES_CALLS_PER_INTERVAL for the getMatchedRules quota the page publishes. A missing constant now prints FAILED and exits non-zero. The page's figures were right; only the script's name for one of them was stale |
The MDN webRequestBlocking quote was fetched by no check | The page's footnote at “Firefox supports both, and keeps blocking webRequest” quotes MDN verbatim. Chrome's side of that comparison was check 21; the Firefox side was checked by nothing, so the whole Firefox half of the MV2/MV3 divergence rested on an unverified quote | New check 26 fetches the MDN page and locates the sentence. It is there verbatim: “To use the 'blocking' feature, the extension must also have the 'webRequestBlocking' API permission”. The check also reports whether MDN has begun mentioning Manifest V3 on that page, which is the way this claim would go stale |
| The Carnus row labelled three things and gave two numbers | “Intra- and inter-browser messaging, and outgoing HTTP” against “450 and 859 extensions respectively”. Reading the paper makes it a worse error than a missing number: Carnus's inter-communication is the outgoing-HTTP vector — “two novel fingerprinting techniques … namely outgoing HTTP requests and intra-browser message exchanges” — so the row invented a third vector the paper does not have | The row names the two vectors, says in terms that they are two and not three, and adds Table I's Unique column (105 and 181) with the paper's own gloss, “unique to each technique (i.e., cannot be detected by any other technique)” — flagged explicitly as not the sense of unique used in the identifiability rows above it. Two needles were added to verify_extension_figures.mjs so both the caption and the two-not-three claim are checked against the paper |
| The people claim had no guard | “Seven of the 68 papers study the humans rather than the code, and all but one are from 2021 onwards.” Nothing computed either half | New report section H2 prints the seven papers with their years and asserts both halves. Mutation-tested in both directions: moving the cut-off to 2022 fails and names the two papers, changing the expected count fails. The claim is true — 2017, 2021, 2022, 2023, 2024, 2025, 2025 |
| This log named one freeze when there were two | The opening paragraph said all four passes were handed the same frozen snapshot and named out/ext/freeze/, when the generic pass had been handed freeze2. A finding is only attributable to a reviewer if you know which text it saw | The paragraph names all three freezes, says which pass saw which, and dates them. This pass's own snapshot is out/ext/freeze3/ |
And one thing the deferred list did not contain. Closing the Carnus row and the people guard changed the needle count, and that exposed a number this page had been publishing three different ways for the same quantity: “60 of 60” in Quotes and per-paper figures, “the 64 needles” in the Pass 2 row below, and “the 70-needle per-paper check” in the 2026-09-21 quote-check refresh. The check has never been 70. 60 went stale the moment the citations pass added four needles, and nothing pointed at it, because the sentence that has to change is in a different section from the fix that changes it. All three are corrected, the figure is 66, and the sentence now says to re-run the script rather than quote it.
A process failure worth more than any of the findings. The prose file this
provenance page is built from, pages/provenance_privacy_browser_extensions.txt,
was three weeks stale: the markup sweep of 2026-09-17 and the quote-check
refresh of 2026-09-21 were both edited straight onto the live page and never back-
ported. Rebuilding with build_provenance_browser_extensions.py would have
deleted both sections silently — the builder checks that no slot is empty, but it
cannot check for content that only exists downstream of it. The prose file was
reconstructed from the live page and the rebuild was then proved byte-identical to
what was published before any of today's edits were applied. If you edit one of
these pages in the browser, back-port it the same day, or the next build eats it.
What no reviewer caught
Three errors survived all of the focused passes and were caught by re-reading the page against the corpus afterwards. They are the same shape, and the shape is worth naming: an absolute negative that no figure and no citation contradicts. A figures pass checks that every number traces to the report; a citation pass checks that every claim attached to a key is supported by that key's paper. Neither can see a sentence that says nobody, because there is no figure and no citation to check it against.
| What the page said | What is true | How it was caught |
|---|---|---|
| “whether the vector is closed is a measurement, not a documentation question. Nobody has published it.” | [5Agarwal, 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)] published exactly that measurement: “we only observe 109 fingerprintable extensions using this option”. The page's own measurement is a second, later one over a different population — which is a better claim than the one it replaced | by running the corpus probe for use_dynamic_url and reading the two hits instead of counting them. The probe was already in the report; only its two contexts had not been read |
| “Nothing in the corpus measures Edge or Opera add-ons” | Opera was measured twice — 2,790 extensions [6Chen, 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,523 [1Somé, Dolière Francis (2019): "EmPoWeb: Empowering Web Applications with Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] — both as a side-corpus to a Chrome study, and not since 2019 | by listing every non-Chrome sourceList in the in-scope population, which took one query and had not been run because no figure on the page needed it |
| “Report the extension set… Nobody does.” | Unknowable from this corpus: the extraction schema has no field for the extension set of a crawl profile. The page now says that, which is the honest version and also the more useful one | by asking what query would support the sentence, and finding there is none |
The general rule this run ends with: before a page ships, grep it for nobody, nothing, never, no one, the only and at all, and for each one name the query that supports it or scope the word. Every one of the three above would have been caught by that grep. None of them was caught by three reviewers.
That grep was then run over the whole page and produced seventeen hits. Nine were left alone because they are explicitly corpus-scoped and the report supports them (“the only paper in this corpus written from inside the store”; “nothing since asks whether extensions request more permission than they use”). Eight were rewritten, and one of those eight was a second real error of the same kind: “Edge … is currently the only mainstream store where the old platform is still live” — Firefox is the other, which the same page says two sections earlier. That is the “two sections meet” failure exactly, and it survived a figures pass, a citation pass and a currency pass because neither half of it is wrong on its own.
TODOs this page leaves open
- The
use_dynamic_urlsample is 200 items. A store-wide figure is one crawl away and would settle whether extension enumeration is a live threat or a historical one. - The store-composition figure rests on a 200-item CRX sample (146 of 198 served are extensions). A larger draw would tighten the on the order of 260,000 estimate, which is currently an order-of-magnitude statement and is written as one.
- Nobody has re-measured what an installed extension does to a crawl since IMC 2016. That is the gap this page most wants filled.
- The
object/object-partialline is single-label and eleven papers sit close to it; a second reader would move some.
See also
- corpus — the dataset-wide provenance page: venue scope, selection funnel, extraction error rates
- browser_protection and requests — the two nearest neighbours, and the reason this page's boundary is drawn where it is
References
- [1]
- Somé, Dolière Francis (2019): "EmPoWeb: Empowering Web Applications with Browser Extensions", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)
- [2]
- Sjö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)
- [3]
- Nguyen, 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)
- [4]
- El 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)
- [5]
- Agarwal, 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)
- [6]
- Chen, Quan; Kapravelos, Alexandros (2018): "Mystique: Uncovering Information Leakage from Browser Extensions", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
Markup sweep, 2026-09-17
Mechanical rendering repair only: a fresh live raw/XHTML export of 188 pages was checked with check_wrap.mjs and check_typography.mjs. Affected plugin tags, CLI flags and heading markup were repaired; no figures or substantive prose were changed. The resulting source and rendered DOM were re-checked after saving.
Quote-check refresh, 2026-09-21
The 2026-09-04 cols-vs-PDF audit on corpus showed that 73.1% of evidence quotes that cannot be located in paper.cols.txt are present in an independent pypdf rendering of the same paper.pdf. ext_quotecheck.mjs already carried the fallback; the verdict table in The extraction's own quotes was copied from a pre-fallback run. Re-run, unedited:
$ node scripts/ext_quotecheck.mjs
extension-corpus population tuples with a quote: 74
exact: 54
partial (>=60% of 5-word windows): 15
rescued from the PDF: 4
below threshold in BOTH renderings: 1 <- read these, do not count them as fabrications
IEEE-SP/2011/verified-security-for-browser-extensions n=1139 cov=0.56
We conducted a simple analysis of manifests for over 1,139 popular Chrome extensions
| Figure | Was | Is | Why |
|---|---|---|---|
| tuples with a quote | 74 | 74 | population unchanged |
| exact | 54 | 54 | unchanged |
| partial (≥60% of 5-word windows) | 15 | 15 | unchanged |
| rescued from the PDF | — | 4 | these were inside the old 5 |
| below threshold | 5 | 1 (in both renderings) | 5 = 4 + 1 exactly |
What the refresh does not change. All five were already read by hand and all five are present, so no claim on browser_extensions rested on the 5. What it changes is the figure a reader would quote as this page's quote-failure rate: 1 of 74, not 5 of 74. And it exposes something worth stating — two of the four rescued are the two tuples the hand read calls paraphrases, so rescued is a statement about the rendering, not a warrant that the extraction is verbatim.
Scope of this edit. The verdict table and the paragraph under it. The per-paper needle check (64 needles on that date, not the 70 this sentence originally said), the store-size table, the population figures and every citation stand as published; report_browser_extensions.mjs was not re-run. browser_extensions carries no quote-check figure, so it was not edited.
| Item | Value |
|---|---|
| Date | 2026-09-21, unsupervised |
| Command | node scripts/ext_quotecheck.mjs |
| Artifact | out/qc0921b/ext.txt |
| Script changes | none |
| Reviewers | one sonnet figures-vs-script pass |
| Pages saved | this page only |
