This is an old revision of the document!
Table of Contents
Provenance: Classifying Cookies
Working notes behind Classifying Cookies: every query with its denominator, the folds with their residues, the quotes that were checked, the external sources that were verified and the ones that were rejected, and the judgement calls. Corpus-level caveats — how the corpus was built, which venue-years are provisional, how stable each extracted field is — are on Corpus and are not restated here.
This is a working log, not prose. It is for someone checking a number.
1. The run
| Date | 2026-09-10 |
| Corpus | data/extract/run1/extractions.jsonl, 5,859 papers, 7 venues (CCS, IMC, NDSS, PoPETs, USENIX Security, TheWebConf, IEEE S&P), 2010–2026 |
| Full text | data/fulltext/<year>/<venue>/<slug>/paper.cols.txt, 5,869 readable files |
| Page before | 20,196 bytes, no corpus figure, no “Use in Publications”, no methodology section, no provenance page |
| Page after | 68,264 bytes |
| Scripts written | scripts/report_cookies.mjs (§3–§9 below), scripts/cookiepedia_coverage.py (§7) |
| Scripts re-used | scripts/report_llm_currency.mjs for the per-target LLM table, so this page and Website classification cannot disagree |
| Bibliography | one entry added: cahn2016_cookies. No key collision, no DOI collision (§10) |
| Model | Claude Opus 5 for the writing and the queries; Sonnet and Fable sub-agents for review (§12) |
Frozen prior work, deliberately not reused. Three dossiers in /workspace/drain/bench-{a,b,c} were written against this page as an effort benchmark on the 4,322-paper corpus. Their structure informed this page (the four-question split and the “which methods are current” table are descendants of bench-b's and bench-c's classification sections). None of their figures was carried over: every number on the page was re-derived against the 5,859-paper run, and the denominators moved — the old corpus gave 90 cookie-measuring papers and 40 cookie-classifying papers, this one gives 117 and 53.
2. Scope: why this page and not a broader one
The item this run was given asked explicitly whether the page should broaden past “Classifying Cookies”. Decision: no. Recorded here because a reasonable person would have decided otherwise.
The argument for broadening is that the page id is privacy:cookies and a reader landing there expects cookies. The argument against, which won, is that four neighbouring pages already own the adjacent material in more depth than a broadened page could give it, and every one of them was read before deciding:
| Material | Already on | Evidence it is covered there |
|---|---|---|
| Non-cookie client-side stores, and what a crawler's readout actually captures | Browser storage | Measured six-store × three-readout-method table; its own opening says “What you do with the cookies themselves is Cookies” |
| Identifier heuristics — length, lifetime, tokenisation, entropy — and their supersession | Cookie syncing | Reproduces the eleven-row heuristic comparison from [1Calzavara, Stefano; Casarin, Samuele; Squarcina, Marco; Maffei, Matteo (2026): "From Syntactic Matching to Taint Tracking and Back: A Comparative Study of Web Tracking Detection Techniques", in: Proceedings on Privacy Enhancing Technologies. (Link)] and has a “What is genuinely superseded” section covering exactly the length/expiry-to-entropy move |
| Third-party-cookie deprecation, its reversal, CHIPS, partitioning | Privacy sandbox, Browser protection | Dated timeline with primary sources through 2025-10-17 |
| Consent state at crawl time | Consent, Interaction | Consent-action decision tree and verification |
So the page keeps the narrow title and adds an explicit scope table pointing at each. The two places where the boundary is soft, and what was done:
- Identifier detection. 16 of the 53 papers do it, so it cannot be absent from this page. Resolution: this page reports who does it and how the family moved over time and links to the neighbour for the heuristics themselves. No heuristic table is duplicated.
- The party question. 2 of 53 papers state a rule. Kept here, in one paragraph, because it is a feature of the classifiers this page is about.
3. Populations and denominators
Two sources, never combined into one figure.
| Tag | Source | Answers |
|---|---|---|
[E] | extractions.jsonl, 5,859 papers | “how many papers reported doing X” |
[F] | paper.cols.txt, 5,869 readable files | “how many papers mention X” |
3.1 The page population
[E] classification[].target == "cookie" -> 53 papers, 79 tuples <-- THE page population
[E] detection[].phenomenon matches /cookie/i -> 113 papers
[E] ...minus three phenomena that are not HTTP cookies -> excluded by name, listed below
[E] union (classify a cookie OR a cookie detection tuple) -> 117 papers (2.0% of 5,859)
[E] of the 53, ran a crawl -> 49 (92.5%)
[E] of the 53, have a crawlConfig object -> 49
denominator for every classification comparison: 4,439 papers that classified anything
denominator for every crawl comparison: 1,080 crawling papers with a crawlConfig
Excluded by name from the wider set, so the exclusion can be argued line by line rather than being buried in a regex: DNS cookie usage (a DNS protocol nonce), SYN-cookie feedback (TCP), Bloom-cookie communication overhead (a replacement primitive, not an HTTP cookie).
Why an enum and not a keyword sweep. classification.target is a closed enum and is among the more stable fields in the schema; detection.phenomenon is free text with ~20% run-to-run agreement on exact strings. A full-text regex population was considered and rejected: on the sibling page Cookie syncing the same approach needed a hand-adjudicated candidate list of over a hundred papers, and the resulting set is explicitly one person's judgement. The enum gives a set that a corpus refresh reproduces.
What that costs, stated on the page. A paper that classifies cookies as an incidental step inside a study about something else can have that step filed under target: other with a targetDetail, and would be missing from the 53. The population is a floor. The gap between 53 and 117 is mostly papers that count or track cookies without labelling them, but not entirely.
3.2 Every other denominator used on the page
| Figure on the page | Denominator | Why that one |
|---|---|---|
| Question split (purpose 18, identifier 16, tracking 11, security 5, party 2, other 12) | 53 | Papers, multi-valued; a paper answering two questions is in two rows |
| Label-source families | 53 | Same |
| Bucketed family table | 7 / 7 / 17 / 10 / 12 per bucket | Printed as its own row in the table so no column is read against the wrong base |
| Taxonomy fold (ICC 11 of 29) | 29, the papers that name a scheme at all | Using 53 would count the 24 papers that named nothing as having named something else |
| Ground truth 56.6%, validation 32.1% | 53, against 4,439 | Both columns printed side by side |
| Crawl-config table | 49 (papers with a crawlConfig object), against 1,080 | crawlConfig is nullable; 53 and 1,120 would understate both |
| consentAction split | 49 | Same |
| Venue shares | Papers in each venue (510, 843, 990, 638, 1,410, 701, 767) | A count of 17 at PoPETs and 2 at IEEE S&P is meaningless without the venue sizes |
| Year shares | Papers that classified anything that year | Controls for the corpus roughly quintupling across the window |
| LLM per-target table | Papers classifying that target (53 for cookie, 102 for privacy-policy, …) | Not 5,859, and not 177 |
| Full-text probe rows | 5,869 readable files | Stated in the output next to every row |
4. The fold, and why it throws instead of leaving a residue
resourceName on a cookie classification tuple is free text: 61 distinct strings across 79 tuples, of which 27 begin with the word “custom” and 34 contain it (computed by the script — a hand count of this got 31). Aggregating by exact string gives a largest row of 14 (Cookiepedia) and a long tail of ones.
The fold is a hand-keyed map in report_cookies.mjs (the RESOURCE constant) from the exact string to two values: a label-source family and the question the classifier answers. The script throws if a tuple's resourceName is not in the map. That is deliberate and is the opposite of the usual residue convention:
- A pattern-based fold with a printed residue lets a corpus refresh silently drop a paper into the residue bucket, where nobody looks. This map cannot: the next run fails loudly and names the string.
- The cost is that the residue is zero by construction, which proves nothing about whether the judgements are right. The compensating artifact is §2.2 of the report output, which prints every tuple with the family and question it landed in, grouped by family. That listing is the thing to check.
4.1 Judgement calls in the map, listed so they can be rejected
CookieGraphandWebGraphare filed under questiontracking, notpurpose, because their label is binary ATS-vs-not.CookieBlockispurpose(four ICC categories). Papers that compare all three are comparing classifiers that answer different questions; the page says so.- The five syncing detectors that the extractor filed as cookie classification (
custom cookie-synchronization rules,custom Cookie Synchronization heuristics,cookie-syncing heuristic (custom),cookie-syncing script (adapted),custom cross-domain attribution and exfiltration pipeline) are filed as questionother. They decide “did this value travel”, which is the Cookie syncing page's question. They are kept visible in the family listing rather than dropped. custom regular expressions(TheWebConf 2022, Google publisher IDs) is filed asidentifier. It extracts publisher IDs from cookie values, which is identifier detection with a known target rather than a generic predicate. Defensible either way.zxcvbnis filed as anidentifier-heuristicfamily even though it is a named third-party library rather than a custom rule, because the family is about what the label means, not about who wrote the code.functional tree (FT)(TheWebConf 2014) isown-supervised-ml/security: it is a trained model, and it classifies authentication cookies.Evidon Global Opt-out, DAA Protect My Choice, and Google Keep My Opt-Outsis its own family (opt-out-registry) with questionother, rather than being folded into the filter-list family. Opt-out registries and tracker blocklists are different artifacts with different maintainers.custom ad-blocker name search lists(IMC 2016) isown-rule/other: it searches for ad-blocker cookie names, which is neither tracking provenance nor a purpose.
4.2 The taxonomy fold, which does leave a residue
Pattern-based, first match wins, over the 29 papers that name a scheme on a cookie tuple.
| Family | Papers | Share of 29 |
|---|---|---|
| ICC (UK) four purposes, or a restatement | 11 | 37.9% |
| Identifier vs non-identifier | 7 | 24.1% |
| Advertising-and-tracking vs not | 4 | 13.8% |
| First vs third party | 2 | 6.9% |
| Lifetime buckets | 1 | 3.4% |
| Security attributes | 1 | 3.4% |
| Four GDPR-framed purposes | 1 | 3.4% |
| Cookiepedia categories | 1 | 3.4% |
Unmapped residue: 7 tuples, printed in full (also in §5 of the report output):
2020 PETS :: tracking cookie criteria 2020 WWW :: first-party, advertising, analytics, content, social, and other 2024 PETS :: functional, marketing, and other cookie categories 2025 IMC :: exfiltration, overwriting, and deletion 2025 PETS :: set after accepted banner, then sent before banner rejection 2025 USENIX :: trackers, location, IP address, language, unlikely personal information 2025 USENIX :: Ignored Cookie Rejection, Undeclared Cookies, Wrong Cookie Category
The ICC merge is a judgement and both counts are published. Strict — the string names the ICC or the International Chamber of Commerce — is 8 papers. Folded, including the four that write the four categories out without naming the scheme, is 11. The page quotes 11 and says the strict count in the same sentence. Every contributing string is printed in §5 of the report output. The row this run is least confident about is TheWebConf 2016's Cookiepedia categories: strictly necessary, performance, functionality, targeting/advertising, unknown — Cookiepedia is OneTrust's implementation of the ICC scheme plus “unknown”, so it folds, but a reader who wanted the strict count would remove it.
Unfolded, the largest exact-string row is 2 papers. Folded it is 11 — an 82% undercount if exact strings are counted. Same failure mode as the vantage 280 → 498 example in the site's own brief.
5. Quote spot-checks
report_cookies.mjs §11 matches the evidence.quote behind every figure the page states as a finding against paper.cols.txt, whitespace- and case-insensitively, falling back to the longest run of 8 consecutive words (the grading the dataset itself uses).
Result: 18 figures checked — 13 exact, 5 via an 8-word run, 0 not found.
Beyond the automated pass, four claims were read out of the source by hand, and two of them changed what the page says:
- [2Bollinger, Dino; Kubicek, Karel; Cotrini, Carlos; Basin, David (2022): "Automating Cookie Consent and GDPR Violation Detection", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2893-2910. USENIX Association, Boston, MA. (Link)]'s Cookiepedia accuracy. The extraction and a first draft both had a single number. The paper states it twice and inconsistently: §4.4 prose says “Cookiepedia achieves a mean balanced accuracy (i.e., macro-recall) of 83.4%”, while Table 3, the §4.5 summary and the abstract all say 84.7%. The page quotes the table and flags the discrepancy in a footnote rather than picking silently.
- Cookiepedia's coverage, 79.2%. Not in the extraction at all; read out of Table 3 by hand (“Cookie coverage: 79.2%” against XGBoost's “Cookie coverage: 100%”). This became the page's central methodological point and would have been missed by a purely extraction-driven pass.
- [3Munir, Shaoor; Siby, Sandra; Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair; Troncoso, Carmela (2023): "CookieGraph: Understanding and Detecting First-Party Tracking Cookies", pp. 3490–3504. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)]'s 89.86%. The extraction's
prevalencereads “89.86% of the 20K websites”. The paper's own crawl is 20,000 sites but it is a stratified sample of the Tranco top million, and the 89.86% comes from a separate deployment on a 20% sample of the top million — which is why the paper's abstract says “top-million”. The page says top-million. This was previously got wrong on a sibling page and is recorded here so it is not got wrong again. - [4Lin, Maxwell; Lin, Shihan; Wu, Helen; Wang, Karen; Yang, Xiaowei (2024): "Browsing without Third-Party Cookies: What Do You See?", in: Proceedings of the ACM Internet Measurement Conference. (DOI)]'s 28.50% unclassified. Read out of Table 1 with its caption (“The total, mean, and median of cookie counts in each category identified by Cookie-Script across 255 websites”), because the extraction had the percentage without the instrument or the population.
6. What the ''[F]'' full-text probes say, and what they do not
Denominator 5,869 readable files. A name match is a mention — it counts reference-list entries and related-work sentences — and is never added to an [E] count. Generic words (entropy, LLM names) are only counted within 150 characters of the word “cookie”, because unqualified they match password and DNS papers.
| Probe | Papers | Read individually? |
|---|---|---|
Cookiepedia | 19 | no |
CookieGraph | 20 | no |
CookieBlock | 16 | no |
Cookie Script / Cookie-Script | 11 | yes — see below |
cookiedatabase.org | 2 | yes |
Open Cookie Database | 0 | n/a |
cookiesearch | 0 | n/a |
International Chamber of Commerce | 13 | no |
zxcvbn near “cookie” | 2 | yes |
entropy near “cookie” | 35 | no |
| an LLM name near “cookie” | 5 | yes |
- The
Cookie Scriptprobe over-matches and the page does not use its count. Of the 11 matches, several are the string appearing in a CMP inventory table (NDSS 2019), a table column header reading “Cookie Script Org.” (CCS 2023 CookieGraph), or a dark-patterns interview (PoPETs 2022). The page's claim of 4 papers using Cookie-Script as a label source comes from the[E]side, not from this probe. - The LLM-near-cookie probe returns 5 and only 1 is real. Two are reference-list entries (“Olejnik, Acar, Claude Castelluccia” — a person's first name, and a GPT-4o citation next to a CookieGraph citation), one is a deceptive-patterns paper using Gemini on screenshots, one is a GDPR right-of-access paper, and one is a web-agent benchmark whose task categories happen to include cookie banners. Only [5Chen, Baiqi; Lyu, Jiawei; Wu, Tingmin; Chhetri, Mohan Baruwal; Bai, Guangdong (2025): "Semantics-Aware Cookie Purpose Compliance", in: Proceedings of the ACM Web Conference. (DOI)] classifies cookies with an LLM. This is why the page's LLM claim rests on the
[E]enum (classification.method == “llm”withtarget == “cookie”), not on the probe. - Both
zxcvbnhits were read in context and both are genuine: IEEE S&P 2022 pre-filters cookies “unlikely to be identifiable” with zxcvbn, and USENIX Security 2025 “leverages the zxcvbn library for measuring the entropy of the cookie values (to discover tracking UIDs)”. The page reports 2 and 35 side by side precisely so nobody reads the library name as the method's reach. - The two zero rows are the page's most load-bearing negative results and they are negative results from a probe, which the site's own rules say to distrust. They are reported as “zero mentions in these seven venues” and never as “nobody uses it”. Positive control: the same probe finds 19 papers for Cookiepedia and 20 for CookieGraph in the same sweep, so the sweep is not silently failing.
7. External and industry sources
Every load-bearing external claim was read at a primary source on 2026-09-10. No SEO listicle, vendor comparison page or blog summary reached the page.
| Claim on the page | Primary source, and how it was checked |
|---|---|
| Cookiepedia holds 42,020,489 cookies; benchmark split 1% / 5% / 3% / 58% / 32% unknown | cookiepedia.co.uk front page, read directly. Labelled on the page as a vendor self-report |
Cookiepedia rejects plain curl | cookiepedia.co.uk/cookies/user_id returns 403 to curl with a desktop User-Agent while the site root returns 200 |
The CookieGraph Cookiepedia name table: 45,785 names, 39.3% categorised, 35.1% Error, 25.6% Unknown | Computed from the released artifact by scripts/cookiepedia_coverage.py (§7.1). Reproducible in one curl |
The CookieGraph cookiepedia.csv is 917,551 rows of CMP-declared labels in the CookieBlock consent_data schema, not Cookiepedia classifications | Downloaded and counted; header is ,id,browser_id,visit_id,name,domain,cat_id,cat_name,purpose,expiry,type_name,type_id. Confirmed against labelling_scripts/cookiepedia.py, which merges it with tranco.csv into a declared_label |
Its cat_name takes 1,158 distinct values | Counted over the same file |
cookie_categories_updated.csv in the CookieGraph repo is a saved wget log, not a CSV | Both files fetched: 3,260 bytes beginning –2023-03-02 20:52:33– https://raw.githubusercontent.com/… versus 1,656,432 bytes with header ,cookie_key,category |
| Open Cookie Database: 2,266 cookies, last commit 2026-08-21, actively maintained | The CSV itself (2,267 lines with header) and the GitHub API (pushed_at 2026-08-21T07:57:05Z, archived false, 311 stars) |
| cookiedatabase.org publishes no cookie count; 501 service Data Passports | cookiedatabase.org/wp-json/wp/v2/service?per_page=1 returns x-wp-total: 501. No cookie post type exists in /wp-json/wp/v2/types |
| cookiesearch.org is run by CookieYes, “100,000+ cookies”, six categories | cookiesearch.org front page and footer, read directly |
| Cookie-Script is a CMP with a name-keyed category database using the ICC four plus “unclassified” | [4Lin, Maxwell; Lin, Shihan; Wu, Helen; Wang, Karen; Yang, Xiaowei (2024): "Browsing without Third-Party Cookies: What Do You See?", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] §2 describes exactly this; the vendor site confirms the scanner exists |
| CookieBlock is gone from the Chrome Web Store | Three independent checks, §7.2 |
| CookieBlock is Manifest V2 | manifest.json on dibollinger/CookieBlock declares “manifest_version”: 2 |
| Chrome disabled MV2 everywhere with Chrome 138 on 2025-07-24, removed in 139 | developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline, quoted verbatim on the page |
| CookieBlock is still public on Firefox: v1.1.0, updated 2022-08-04, 1,980 daily users | AMO API addons.mozilla.org/api/v5/addons/addon/cookieblock/: status: public |
| CookieBlock is still listed on Edge at v1.1.0 | Edge product-details API, lastUpdateDate 2022-08-04. Flagged on the page as unverified for actual function, because Edge is Chromium |
| Firefox still distributes MV2 | Mozilla Extension Workshop migration guide, “Distribute Manifest V2 and V3 extensions” |
| The offline classifier's input format and its two caveats | CookieBlock-Consent-Classifier README, quoted |
| Third-party cookies not discontinued, 2025-04-22 | Privacy Sandbox blog post, already on the page and re-checked 2026-09-10 |
7.1 The coverage script and its output
Committed as scripts/cookiepedia_coverage.py. Real, unedited output:
- cookiepedia_coverage-output.txt
source: https://raw.githubusercontent.com/cookiegraph/CookieGraph/main/Feature%20Extraction%20and%20Classifier/code/labelling_scripts/cookies_categories_updated.csv distinct cookie NAMES looked up in Cookiepedia: 45785 <-- denominator category returned names share ---------------------------------------------- Error 16093 35.1% Unknown 11701 25.6% Performance 5540 12.1% Targeting/Advertising 4961 10.8% Functionality 3874 8.5% Strictly Necessary 3616 7.9% returned one of Cookiepedia's four categories: 17991 of 45785 = 39.3% returned no usable category: 27794 of 45785 = 60.7% non-answer values, printed in full so none is silently folded away: ['Error', 'Unknown']
- cookiepedia_coverage.py
#!/usr/bin/env python3 """How much of a cookie-name lookup does Cookiepedia actually answer? The question a purpose-classification paper has to answer before it quotes a category distribution: what fraction of the cookies it looked up came back with a category at all. Cookiepedia publishes a self-reported benchmark on its front page; this script measures the same thing from an independent, released artifact -- the Cookiepedia name-to-category table that CookieGraph {shaoor2023cookiegraph} ships in its repository. Two files, both from the CookieGraph artifact: cookies_categories_updated.csv 45,785 rows, one per cookie NAME, with the category Cookiepedia returned for that name. This is the lookup-coverage measurement. cookiepedia.csv 917,551 rows, one per observed cookie, with the CMP-DECLARED category (the CookieBlock consent-crawler `consent_data` schema: cat_id 0-3 = the ICC UK four). Despite the filename these are website operators' own declarations, not Cookiepedia classifications. Run: python3 scripts/cookiepedia_coverage.py Output committed as scripts/cookiepedia_coverage-output.txt. """ import collections import csv import io import sys import urllib.request BASE = ( "https://raw.githubusercontent.com/cookiegraph/CookieGraph/main/" "Feature%20Extraction%20and%20Classifier/code/labelling_scripts/" ) UA = {"User-Agent": "measuretheweb-wiki-bot (contact karel.kubicek@vaultjs.com)"} # Cookiepedia's own four categories. Everything else in the released table is a # non-answer, and the two non-answers are DIFFERENT: "Unknown" is Cookiepedia # saying it has the name and cannot categorise it; "Error" is the lookup itself # failing. Collapsing them would hide which of the two dominates. REAL = {"Strictly Necessary", "Performance", "Functionality", "Targeting/Advertising"} def fetch(name): req = urllib.request.Request(BASE + name, headers=UA) with urllib.request.urlopen(req, timeout=180) as r: return r.read().decode("utf8", errors="replace") def main(): text = fetch("cookies_categories_updated.csv") reader = csv.reader(io.StringIO(text)) header = next(reader) if header != ["", "cookie_key", "category"]: raise SystemExit(f"unexpected header {header!r}: the artifact changed, re-read it") counts = collections.Counter() n = 0 for row in reader: n += 1 counts[row[2]] += 1 print(f"source: {BASE}cookies_categories_updated.csv") print(f"distinct cookie NAMES looked up in Cookiepedia: {n} <-- denominator\n") print(f"{'category returned':28} {'names':>8} {'share':>8}") print("-" * 46) for cat, c in counts.most_common(): print(f"{cat:28} {c:8d} {100 * c / n:7.1f}%") answered = sum(c for cat, c in counts.items() if cat in REAL) print( f"\nreturned one of Cookiepedia's four categories: {answered} of {n} " f"= {100 * answered / n:.1f}%" ) print( f"returned no usable category: {n - answered} of {n} " f"= {100 * (n - answered) / n:.1f}%" ) unseen = set(counts) - REAL print(f"\nnon-answer values, printed in full so none is silently folded away: {sorted(unseen)}") if __name__ == "__main__": sys.exit(main())
7.2 The CookieBlock removal, in full
This is the page's biggest single correction — the previous version's step-by-step crawler recipe began “Install the CookieBlock extension in Chrome” — so it was checked three ways, with controls.
- The store page. Fetched with Playwright's own Chromium (a plain
curlgets a JS shell, and the Swiss vantage gets a Google consent interstitial that has to be cleared with aSOCScookie first). Final URLchromewebstore.google.com/detail/fbhiolckidkciamgcobkokpelckgnnol/error, title “Item not available”, body “This item is not available”. - The CRX endpoint, with a positive and a negative control.
clients2.google.com/service/update2/crx?response=redirect&prodversion=151.0&acceptformat=crx2,crx3&x=id%3D<id>%26uc:
ddkjiahejlhfcafbddmgiahcphecmpfh (uBlock Origin Lite, live) crx: 200, 9,635,198 bytes fbhiolckidkciamgcobkokpelckgnnol (CookieBlock) crx: 204, 0 bytes aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa (invented id) crx: 404, status="error-unknownApplication"
- The update check.
status=“ok”withstatus=“noupdate”for CookieBlock — Google knows the id and will not serve a package, which is what an unpublished item looks like and is distinguishable from the invented id'serror-unknownApplication.
What is claimed on the page, and what is not. The page says CookieBlock is not installable from the Chrome Web Store and that Chrome disabled MV2 in Chrome 138. It does not claim Google removed it because of MV2 — that is a plausible inference from the two verified facts and is left as one. It also does not claim the Edge listing is broken, only that Edge is Chromium and the listing was not functionally tested.
7.3 Sources considered and rejected
cookie-script.com/cookie-database, the URL a search suggests for the Cookie-Script database — 404. The vendor's public entry point is the scanner, not a browsable database, so the page describes it as “a name-keyed category database exposed through its cookie scanner” and cites [4Lin, Maxwell; Lin, Shihan; Wu, Helen; Wang, Karen; Yang, Xiaowei (2024): "Browsing without Third-Party Cookies: What Do You See?", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] for the category scheme rather than a vendor page.- cookiedatabase.org's “15.5k cookies”, which the previous version of this page carried. Not verifiable anywhere on the current site; the WordPress API has no cookie post type. Removed rather than refreshed, with a sentence on the page saying it was removed and why. Refreshing an unverifiable number to a new unverifiable number is how a stale figure survives a rewrite.
- Any comparison-of-cookie-databases article. Several exist; all are CMP vendor marketing. Nothing from them is on the page. The coverage table is built from four measurements in peer-reviewed papers plus one reproducible computation plus one clearly-labelled vendor self-report.
- A GitHub search for a maintained CookieBlock successor. Run (
cookieblock in:name, sorted by push date) and returned nothing relevant: the top hits are unrelated hobby extensions with 0–1 stars.dibollinger/CookieBlockat 226 stars, last pushed 2023-12-08, is still the project. Recorded because “the maintained code may live under a renamed scope” is a real failure mode and it was checked, not assumed.
8. Currency: how each status was decided
The page's “Which Methods Are Current” table carries a status per family. The rule applied: a superseded judgement needs a named, dated source that supersedes the thing it retires; a current judgement needs presence in the 2025–2026 slice and an argument, because that slice is provisional.
| Family | Status | What it rests on |
|---|---|---|
| Purpose database lookup | current, the default | 8 of the 10 papers in 2023–2024 and 5 of 12 in 2025–2026 |
| Own hand-written rule | current, largest family, mostly unreusable | 20 of 53; 27 of 61 distinct resourceName strings begin “custom”, 34 contain it |
| Identifier heuristic | current, but the predicate changed | Family present 2014→2026 including 3 papers in 2025–2026; the supersession of length/expiry thresholds by entropy is argued on the sibling page from [6Randall, Audrey; Snyder, Peter; Ukani, Alisha; Snoeren, Alex C.; Voelker, Geoffrey M.; Savage, Stefan; Schulman, Aaron (2022): "Measuring UID smuggling in the wild", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] and [1Calzavara, Stefano; Casarin, Samuele; Squarcina, Marco; Maffei, Matteo (2026): "From Syntactic Matching to Taint Tracking and Back: A Comparative Study of Web Tracking Detection Techniques", in: Proceedings on Privacy Enhancing Technologies. (Link)], not from a corpus count |
| Filter list provenance | current and growing | 8 papers, 3 of them 2025. Small numbers, said so on the page |
| Published classifier reused | current, newest family | 0 before 2023, then 3 and 2. Mechanically explained: the classifiers did not exist |
| Own supervised model | stopped | 3 papers, most recent 2023. Stated as “nobody has trained a new one since”, which is a claim about these seven venues only |
| LLM | not established practice | 1 paper of 53 (1.9%), 2025 only |
8.1 The LLM claim, and the retraction it repairs
The brief for this run flagged that IP classification once asserted “LLM methods are now routine” for cookie classification, and that this was retracted. This run re-derived the figure independently and confirms the retraction: 1 of 53.
report_cookies.mjs §7 recomputes the per-target LLM table from scratch and it agrees with report_llm_currency.mjs row for row. Both are in the committed outputs. The page publishes the table so the comparison — privacy-policy 11.8%, cookie 1.9% — is visible rather than asserted, and links to which_task_though so the four pages tell one story.
The honest framing, recorded because it was a judgement: the corpus-wide LLM curve is steep (2 papers in 2023, 27 in 2024, 77 in 2025, 71 in a partial 2026), so “cookies will get there” is a reasonable prediction. It is not on the page, because a prediction is not a finding.
9. What could not be established
- Whether the 53 is missing papers filed under
target: other. The wider 117-paper set was inspected for obvious misses but not exhaustively adjudicated paper by paper, which is what the sibling syncing page did. This is the single biggest weakness of the population and it is stated on the page. - Whether Cookiepedia's accuracy has changed since 2022. Nobody has re-measured it. The page's accuracy figures are all from one paper on one dataset, said so at the point of use, and listed as the first open question.
- Whether the Edge CookieBlock listing still functions. The listing exists at v1.1.0; whether current Edge runs an MV2 extension was not tested (it would need an Edge build and a profile). The page says “unverified” rather than guessing.
- cookiedatabase.org's size. No public endpoint. See §7.3.
- What fraction of the corpus's cookie papers report coverage. The page argues coverage is the number to report and gives four papers that do. It does not give “N of 53 report coverage”, because coverage is not a schema field and establishing it would need all 53 papers read by hand. A TODO worth closing: it would turn a rhetorical point into a measurement.
- Whether
no-interactionat 44.9% means what it says. A prior audit found this value unsupported by the paper in a large minority of cases across the whole corpus. It was not re-audited for these 49 papers. The page marks it as an upper bound and derives its stronger claim (“at most 9 of 49 report more than one consent state”) from the values that are not sentinels, which does not depend on the audit. - Anything about the six absent venues. SOUPS and CHI in particular: the usable-privacy half of this literature reaches the corpus only when it happens to appear at PoPETs.
10. Bibliography
One entry added, generated by node scripts/bibgen.mjs WWW/2016/an-empirical-study-of-web-cookies from the corpus index (real DOI, real authors):
cahn2016_cookies -- Cahn, Alfeld, Barford, Muthukrishnan
"An Empirical Study of Web Cookies", TheWebConf 2016
doi 10.1145/2872427.2882991
Checked before saving: the key does not exist in literature:bibliography (0 matches), the DOI does not exist (0 matches), and scripts/bib_dedup_scan.py reports no new A/B/C/D candidate pair involving it. The one pre-existing [C] pair the scan reports (bratton2019_replication / sumner2014_exaggeration) is unrelated to this run and untouched.
Every other citekey on the page already existed. 20 distinct keys, 62 markers carrying 65 citekey instances, 130 rendered bibtex_citekey spans (the plugin emits two per instance) and 20 references.
The new key did not render until the bibliography's own cache was purged. Immediately after saving, privacy:cookies showed 128 spans and 19 references: cahn2016_cookies resolved to nothing, with no warning of any kind. ?purge=true on privacy:cookies alone did not fix it; ?purge=true on literature:bibliography did. Anyone adding a key should count rendered references against distinct source keys after purging both pages, not assume a save is enough.
11. The report script and its output
scripts/report_cookies.mjs, run against data/extract/run1 on 2026-09-10. Real, unedited output. The script throws rather than continuing if a cookie classification tuple's resourceName is not in the hand-keyed map (§4).
- report_cookies-output.txt
============================================================================== 1. POPULATIONS ============================================================================== Population Papers Share of 5,859 ------------------------------------------------------------------------- ------ -------------- corpus (7 venues, 2010-2026) 5859 100% classified or labelled something 4439 75.8% ran an automated web crawl 1120 19.1% >=1 classification tuple with target == "cookie" <-- the page population 53 0.9% measures something about cookies (classify OR a cookie detection tuple) 117 2.0% The page's headline denominator is 53, NOT 5,859 and NOT 1,120. 53 of 4439 papers that classified anything = 1.2%. 49 of the 53 ran a crawl. Excluded by name from the wider set (not HTTP cookies): DNS cookie usage; SYN-cookie feedback; Bloom-cookie communication overhead ============================================================================== 1.1 THE 53 PAPERS, BY YEAR AND VENUE ============================================================================== Year Cookie-classifying papers Papers classifying anything Share Corpus that year ----- ------------------------- --------------------------- ----- ---------------- 2010 0 77 0.0% 119 2011 1 83 1.2% 116 2012 0 108 0.0% 151 2013 0 96 0.0% 125 2014 3 126 2.4% 166 2015 3 140 2.1% 190 2016 3 144 2.1% 182 2017 1 185 0.5% 231 2018 1 192 0.5% 254 2019 2 293 0.7% 402 2020 6 315 1.9% 404 2021 4 296 1.4% 379 2022 7 412 1.7% 546 2023 2 503 0.4% 719 2024 8 529 1.5% 690 2025* 11 607 1.8% 770 2026* 1 333 0.3% 415 * 2025-2026 are provisional venue-years: CCS 2026 and IMC 2026 have not been held, and IEEE S&P / WWW 2026 are incompletely selected. Those rows are floors. Venue Cookie-classifying papers Papers in venue Share of venue ------- ------------------------- --------------- -------------- PETS 17 510 3.3% WWW 13 843 1.5% CCS 8 990 0.8% IMC 5 638 0.8% USENIX 5 1410 0.4% NDSS 3 701 0.4% IEEE-SP 2 767 0.3% ============================================================================== 1.2 THE 53 PAPERS, LISTED ============================================================================== 2011 CCS fortifying-web-based-applications-automatically 2014 CCS the-web-never-forgets-persistent-tracking-mechanisms-in-the-wild 2014 CCS your-online-interests-pwned-a-pollution-attack-against-targeted-advertising 2014 WWW quite-a-mess-in-my-cookie-jar-leveraging-machine-learning-to-protect-web-authent 2015 IMC affiliate-crookies-characterizing-affiliate-marketing-abuse 2015 NDSS upgrading-https-in-mid-air-an-empirical-study-of-strict-transport-security-and-k 2015 WWW cookies-that-give-you-away-the-surveillance-implications-of-web-tracking 2016 CCS online-tracking-a-1-million-site-measurement-and-analysis 2016 IMC ad-blockers-global-prevalence-and-impact 2016 WWW an-empirical-study-of-web-cookies 2017 PETS cross-device-tracking-measurement-and-disclosures 2018 WWW the-cost-of-digital-advertisement-comparing-user-and-advertiser-views 2019 IEEE-SP postcards-from-the-post-http-world-amplification-of-https-vulnerabilities-in-the 2019 PETS 4-years-of-eu-cookie-law-results-and-lessons-learned 2020 PETS a-comparative-measurement-study-of-web-tracking-on-mobile-and-desktop-environmen 2020 PETS in-depth-evaluation-of-redirect-tracking-and-link-usage 2020 PETS inferring-tracker-advertiser-relationships-in-the-online-advertising-ecosystem-u 2020 PETS missed-by-filter-lists-detecting-unknown-third-party-trackers-with-invisible-pix 2020 WWW beyond-the-front-page-measuring-third-party-dynamics-in-the-field 2020 WWW stop-tracking-me-bro-differential-tracking-of-user-demographics-on-hyper-partisa 2021 NDSS reining-in-the-webs-inconsistencies-with-site-policy 2021 USENIX can-i-take-your-subdomain-exploring-same-site-attacks-in-the-modern-web 2021 WWW cookie-swap-party-abusing-first-party-cookies-for-web-tracking 2021 WWW user-tracking-in-the-post-cookie-era-how-websites-bypass-gdpr-consent-to-track-u 2022 CCS do-opt-outs-really-opt-me-out 2022 IEEE-SP journey-to-the-center-of-the-cookie-ecosystem-unraveling-actors-roles-and-relati 2022 PETS my-cookie-is-a-phoenix-detection-measurement-and-lawfulness-of-cookie-respawning 2022 USENIX automating-cookie-consent-and-gdpr-violation-detection 2022 WWW et-tu-brute-privacy-analysis-of-government-websites-and-mobile-apps 2022 WWW leveraging-googles-publisher-specific-ids-to-detect-website-administration 2022 WWW measuring-the-privacy-vs-compatibility-trade-off-in-preventing-third-party-state 2023 CCS cookiegraph-understanding-and-detecting-first-party-tracking-cookies 2023 IMC thou-shalt-not-reject-analyzing-accept-or-pay-cookie-banners-on-the-web 2024 CCS blocking-tracking-javascript-at-the-function-granularity 2024 IMC browsing-without-third-party-cookies-what-do-you-see 2024 PETS supporting-informed-choices-about-browser-cookies-the-impact-of-personalised-coo 2024 PETS the-devil-is-in-the-details-detection-measurement-and-lawfulness-of-server-side 2024 PETS a-large-scale-study-of-cookie-banner-interaction-tools-and-their-impact-on-users 2024 USENIX automated-large-scale-analysis-of-cookie-notice-compliance 2024 USENIX purl-safe-and-effective-sanitization-of-link-decoration 2024 WWW a-study-of-gdpr-compliance-under-the-transparency-and-consent-framework 2025 CCS layered-overlapping-and-inconsistent-a-large-scale-analysis-of-the-multiple-priv 2025 IMC cookieguard-characterizing-and-isolating-the-first-party-cookie-jar 2025 NDSS duumviri-detecting-trackers-and-mixed-trackers-with-a-breakage-detector 2025 PETS johnny-can-t-revoke-consent-either-measuring-compliance-of-consent-revocation-on 2025 PETS intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and 2025 PETS empirically-measuring-data-localization-in-the-eu 2025 PETS tracking-without-borders-studying-the-role-of-webviews-in-bridging-mobile-and-we 2025 PETS unmasking-the-shadows-a-cross-country-study-of-online-tracking-in-illegal-movie 2025 PETS understanding-regional-filter-lists-efficacy-and-impact 2025 USENIX navigating-cookie-consent-violations-across-the-globe 2025 WWW semantics-aware-cookie-purpose-compliance 2026 PETS from-syntactic-matching-to-taint-tracking-and-back-a-comparative-study-of-web-tr ============================================================================== 2. PASS B — FOLD OF resourceName, WITH ITS RESIDUE ============================================================================== 79 cookie classification tuples across 53 papers, 61 distinct resourceName strings, all mapped (residue 0 by construction — the script throws instead of silently dropping one). The listing in section 2.2 is what to check: a zero residue means the map is complete, not that every judgement in it is right. Of the 61 distinct strings, 27 BEGIN with the word "custom" and 34 contain it anywhere. That is the reusability finding: these are one-off predicates written for one paper. ============================================================================== 2.1 LABEL-SOURCE FAMILY (papers, of 53 that classify a cookie) ============================================================================== Family Papers Share of 53 Tuples ------------------------------------------------------------------------ ------ ----------- ------ Other hand-written rule 20 37.7% 21 Cookie purpose database (Cookiepedia, Cookie-Script, cookiedatabase.org) 17 32.1% 19 Identifier heuristic over the cookie value 15 28.3% 15 Filter list / tracker database (provenance of the setter) 8 15.1% 8 A published cookie classifier, reused off the shelf 5 9.4% 6 Own supervised model trained for the paper 3 5.7% 3 Hand labelling by the authors 2 3.8% 2 First/third-party rule over the domain 2 3.8% 2 Opt-out tool registry 1 1.9% 1 Large language model 1 1.9% 2 Multi-valued: a paper using Cookiepedia AND its own heuristic is in both rows. ============================================================================== 2.1b THE QUESTION BEING ANSWERED (papers, of 53) ============================================================================== Question Papers Share of 53 Tuples -------------------------------------------------------------------------- ------ ----------- ------ Purpose: which category (necessary / functional / analytics / advertising) 18 34.0% 28 Tracking: does this cookie belong to a tracker 11 20.8% 12 Identifier: does the value carry a per-user ID 16 30.2% 16 Party: first or third party 2 3.8% 2 Security role: authentication or session cookie, attribute correctness 5 9.4% 7 Something else (named in the residue listing) 12 22.6% 14 ============================================================================== 2.1c NAMED LABEL SOURCES, PAPER-COUNTED — the figures the prose quotes ============================================================================== resourceName (exact string) Papers Tuples Family Question --------------------------------------- ------ ------ -------------------- ---------- Cookiepedia 14 14 purpose-database purpose CookieBlock 4 4 published-classifier purpose Cookie-Script 2 2 purpose-database purpose justdomains blocklist 2 2 filter-list tracking Cookie Database 1 1 purpose-database purpose Cookie Script 1 1 purpose-database purpose Cookiedatabase.org 1 1 purpose-database purpose CookieGraph 1 1 published-classifier tracking Disconnect.me 1 1 filter-list tracking EasyList 1 1 filter-list tracking EasyList/EasyPrivacy 1 1 filter-list tracking Ghostery 1 1 filter-list tracking Ghostery and Disconnect intersection 1 1 filter-list tracking GPT-3.5 1 1 llm purpose GPT-3.5-turbo (custom fine-tuned model) 1 1 llm purpose justdomains 1 1 filter-list tracking Public Suffix List 1 1 party-rule party WebGraph 1 1 published-classifier tracking zxcvbn 1 1 identifier-heuristic identifier Merged across spellings (papers, not tuples): Cookiepedia 14 Cookie-Script / "Cookie Script" 3 cookiedatabase.org 2 CookieBlock 4 any purpose database 17 justdomains (both spellings) 3 ============================================================================== 2.2 EVERY TUPLE, GROUPED BY FAMILY — the listing to argue with ============================================================================== --- filter-list: Filter list / tracker database (provenance of the setter) 2014 CCS q=tracking m=curated-database used "Ghostery" 2019 PETS q=tracking m=blocklist used "Ghostery and Disconnect intersection" 2020 WWW q=tracking m=curated-database used "Disconnect.me" 2022 WWW q=tracking m=blocklist used "EasyList/EasyPrivacy" 2023 IMC q=tracking m=blocklist used "justdomains blocklist" 2025 PETS q=tracking m=blocklist used "justdomains" 2025 CCS q=tracking m=blocklist used "EasyList" 2025 PETS q=tracking m=blocklist used "justdomains blocklist" --- identifier-heuristic: Identifier heuristic over the cookie value 2014 CCS q=identifier m=heuristic-rules produced "custom persistent-identifier detection rules" 2015 WWW q=identifier m=heuristic-rules produced "custom identifier-cookie detection algorithm" 2016 CCS q=identifier m=heuristic-rules produced "custom ID-cookie heuristic" 2019 IEEE-SP q=identifier m=heuristic-rules used "heuristic proposed in previous work" 2020 PETS q=identifier m=heuristic-rules produced "custom ID-cookie criteria" 2020 PETS q=identifier m=heuristic-rules produced "BehaviorTrack" 2021 WWW q=identifier m=heuristic-rules produced "custom tracking-ID detection algorithm" 2021 WWW q=identifier m=heuristic-rules produced "custom unique-ID parser" 2022 CCS q=identifier m=heuristic-rules produced "unique-ID cookie heuristics (custom)" 2022 WWW q=identifier m=heuristic-rules produced "potentially identifying cookie flow heuristic (custom)" 2022 IEEE-SP q=identifier m=heuristic-rules used "zxcvbn" 2024 PETS q=identifier m=heuristic-rules produced "custom ID-detection algorithm" 2025 IMC q=identifier m=heuristic-rules produced "custom identifier detection pipeline" 2025 PETS q=identifier m=heuristic-rules produced "custom ID-cookie heuristics" 2026 PETS q=identifier m=heuristic-rules produced "custom identifier-detection heuristic" --- llm: Large language model 2025 WWW q=purpose m=llm produced "GPT-3.5-turbo (custom fine-tuned model)" 2025 WWW q=purpose m=llm used "GPT-3.5" --- manual: Hand labelling by the authors 2014 WWW q=security m=manual-labelling produced "custom gold-set labeling procedure" 2025 WWW q=purpose m=manual-labelling used "three independent authors" --- opt-out-registry: Opt-out tool registry 2022 CCS q=other m=curated-database used "Evidon Global Opt-out, DAA Protect My Choice, and Google Keep My Opt-Outs" --- own-rule: Other hand-written rule 2011 CCS q=security m=heuristic-rules produced "custom Zan credential-cookie classifier" 2014 WWW q=security m=heuristic-rules used "hand-coded rules for SessionShield, Serene, CookiExt, and Zan" 2014 CCS q=other m=heuristic-rules produced "custom cookie-synchronization rules" 2015 IMC q=other m=regex-or-signature used "custom affiliate-cookie parser (AffTracker)" 2015 NDSS q=security m=heuristic-rules produced "custom cookie-scope checks" 2016 IMC q=other m=heuristic-rules used "custom ad-blocker name search lists" 2017 PETS q=other m=regex-or-signature used "cookie-syncing script (adapted)" 2018 WWW q=other m=heuristic-rules produced "custom Cookie Synchronization heuristics" 2020 PETS q=tracking m=heuristic-rules produced "custom cookie classification algorithm" 2020 PETS q=other m=heuristic-rules used "cookie-syncing heuristic (custom)" 2020 WWW q=other m=heuristic-rules produced "custom" 2021 NDSS q=security m=heuristic-rules produced "custom cookie inconsistency checks" 2021 USENIX q=security m=heuristic-rules used "random session-cookie heuristic" 2022 CCS q=other m=heuristic-rules produced "matching patterns (custom)" 2022 USENIX q=purpose m=heuristic-rules produced "custom keyword mapping" 2022 WWW q=identifier m=regex-or-signature produced "custom regular expressions" 2025 IMC q=other m=heuristic-rules produced "custom cross-domain attribution and exfiltration pipeline" 2025 PETS q=other m=heuristic-rules produced "custom intractable-cookie definition" 2025 USENIX q=other m=heuristic-rules produced "custom personal-information detector" 2025 USENIX q=other m=heuristic-rules produced "ConsentChk rule-based violation classifier" 2025 WWW q=other m=heuristic-rules produced "custom segmentation rules" --- own-supervised-ml: Own supervised model trained for the paper 2014 WWW q=security m=supervised-ml produced "functional tree (FT)" 2022 USENIX q=purpose m=supervised-ml produced "XGBoost (custom)" 2023 CCS q=tracking m=supervised-ml produced "random forest (custom)" --- party-rule: First/third-party rule over the domain 2016 WWW q=party m=heuristic-rules produced "custom host-domain comparison" 2025 PETS q=party m=heuristic-rules used "Public Suffix List" --- published-classifier: A published cookie classifier, reused off the shelf 2023 CCS q=purpose m=other compared "CookieBlock" 2023 CCS q=tracking m=graph-analysis compared "WebGraph" 2024 CCS q=tracking m=graph-analysis used "CookieGraph" 2024 USENIX q=purpose m=supervised-ml used "CookieBlock" 2025 PETS q=purpose m=curated-database used "CookieBlock" 2025 WWW q=purpose m=supervised-ml compared "CookieBlock" --- purpose-database: Cookie purpose database (Cookiepedia, Cookie-Script, cookiedatabase.org) 2016 WWW q=purpose m=curated-database used "Cookiepedia" 2020 WWW q=purpose m=curated-database used "Cookiepedia" 2022 PETS q=purpose m=curated-database used "Cookiepedia" 2022 USENIX q=purpose m=curated-database compared "Cookiepedia" 2023 CCS q=purpose m=curated-database used "Cookiepedia" 2024 IMC q=purpose m=curated-database used "Cookie-Script" 2024 PETS q=purpose m=third-party-service used "Cookie-Script" 2024 PETS q=purpose m=curated-database used "Cookiepedia" 2024 USENIX q=purpose m=curated-database used "Cookiepedia" 2024 USENIX q=purpose m=curated-database used "Cookiedatabase.org" 2024 PETS q=purpose m=curated-database used "Cookiepedia" 2024 USENIX q=purpose m=curated-database used "Cookiepedia" 2024 WWW q=purpose m=curated-database used "Cookiepedia" 2025 NDSS q=purpose m=curated-database used "Cookiepedia" 2025 PETS q=purpose m=curated-database used "Cookie Database" 2025 USENIX q=purpose m=curated-database used "Cookiepedia" 2025 WWW q=purpose m=curated-database compared "Cookiepedia" 2025 WWW q=purpose m=curated-database compared "Cookie Script" 2025 PETS q=purpose m=third-party-service used "Cookiepedia" ============================================================================== 3. DATING THE METHODS — label-source family by year ============================================================================== Family Papers First Most recent Years it appears in ------------------------------------------------------------------------ ------ ----- ----------- ------------------------------------------------- Other hand-written rule 20 2011 2025 2011 2014 2015 2016 2017 2018 2020 2021 2022 2025 Cookie purpose database (Cookiepedia, Cookie-Script, cookiedatabase.org) 17 2016 2025 2016 2020 2022 2023 2024 2025 Identifier heuristic over the cookie value 15 2014 2026 2014 2015 2016 2019 2020 2021 2022 2024 2025 2026 Filter list / tracker database (provenance of the setter) 8 2014 2025 2014 2019 2020 2022 2023 2025 A published cookie classifier, reused off the shelf 5 2023 2025 2023 2024 2025 Own supervised model trained for the paper 3 2014 2023 2014 2022 2023 Hand labelling by the authors 2 2014 2025 2014 2025 First/third-party rule over the domain 2 2016 2025 2016 2025 Large language model 1 2025 2025 2025 Opt-out tool registry 1 2022 2022 2022 ============================================================================== 3.1 THE SAME SPLIT IN FOUR-YEAR BUCKETS ============================================================================== Family 2010-2015 2016-2019 2020-2022 2023-2024 2025-2026* ------------------------------------------------------------------------ --------- --------- --------- --------- ---------- Other hand-written rule 5 3 8 0 4 Cookie purpose database (Cookiepedia, Cookie-Script, cookiedatabase.org) 0 1 3 8 5 Identifier heuristic over the cookie value 2 2 7 1 3 Filter list / tracker database (provenance of the setter) 1 1 2 1 3 A published cookie classifier, reused off the shelf 0 0 0 3 2 Own supervised model trained for the paper 1 0 1 1 0 Hand labelling by the authors 1 0 0 0 1 First/third-party rule over the domain 0 1 0 0 1 Large language model 0 0 0 0 1 Opt-out tool registry 0 0 1 0 0 (denominator) cookie-classifying papers in bucket 7 7 17 10 12 ------------------------------------------------- - - -- -- -- ============================================================================== 4. THE PURPOSE-CLASSIFICATION SUBSET, PAPER BY PAPER ============================================================================== 18 papers answer the purpose question, of 53 that classify a cookie. 2023 CCS cookiegraph-understanding-and-detecting-first-party-tracking-cookies Cookiepedia [used]; CookieBlock [compared] 2024 IMC browsing-without-third-party-cookies-what-do-you-see Cookie-Script [used] 2025 NDSS duumviri-detecting-trackers-and-mixed-trackers-with-a-breakage-detector Cookiepedia [used] 2022 PETS my-cookie-is-a-phoenix-detection-measurement-and-lawfulness-of-cookie-respawning Cookiepedia [used] 2024 PETS a-large-scale-study-of-cookie-banner-interaction-tools-and-their-impact-on-users Cookiepedia [used] 2024 PETS supporting-informed-choices-about-browser-cookies-the-impact-of-personalised-coo Cookie-Script [used] 2024 PETS the-devil-is-in-the-details-detection-measurement-and-lawfulness-of-server-side Cookiepedia [used] 2025 PETS empirically-measuring-data-localization-in-the-eu Cookie Database [used] 2025 PETS johnny-can-t-revoke-consent-either-measuring-compliance-of-consent-revocation-on CookieBlock [used] 2025 PETS understanding-regional-filter-lists-efficacy-and-impact Cookiepedia [used] 2022 USENIX automating-cookie-consent-and-gdpr-violation-detection custom keyword mapping [produced]; XGBoost (custom) [produced]; Cookiepedia [compared] 2024 USENIX automated-large-scale-analysis-of-cookie-notice-compliance CookieBlock [used]; Cookiepedia [used]; Cookiedatabase.org [used] 2024 USENIX purl-safe-and-effective-sanitization-of-link-decoration Cookiepedia [used] 2025 USENIX navigating-cookie-consent-violations-across-the-globe Cookiepedia [used] 2016 WWW an-empirical-study-of-web-cookies Cookiepedia [used] 2020 WWW beyond-the-front-page-measuring-third-party-dynamics-in-the-field Cookiepedia [used] 2024 WWW a-study-of-gdpr-compliance-under-the-transparency-and-consent-framework Cookiepedia [used] 2025 WWW semantics-aware-cookie-purpose-compliance GPT-3.5-turbo (custom fine-tuned model) [produced]; three independent authors [used]; Cookiepedia [compared]; Cookie Script [compared]; CookieBlock [compared]; GPT-3.5 [used] ============================================================================== 5. TAXONOMY — which category scheme, folded ============================================================================== 29 of 53 papers name a category scheme on at least one cookie tuple (54.7%). The denominator below is 29, not 53. Scheme (folded) Papers Share of 29 --------------------------------------------------------------------------- ------ ----------- ICC (UK) four purposes, or a restatement of them 11 37.9% Identifier vs non-identifier 7 24.1% Advertising-and-tracking vs not 4 13.8% First vs third party 2 6.9% Lifetime buckets 1 3.4% Security attributes 1 3.4% Four GDPR-framed purposes (necessary / preference / statistics / marketing) 1 3.4% Cookiepedia categories (= the ICC four plus "unknown") 1 3.4% UNMAPPED RESIDUE (7 tuples) — printed in full: 2020 PETS :: tracking cookie criteria 2020 WWW :: first-party, advertising, analytics, content, social, and other 2024 PETS :: functional, marketing, and other cookie categories 2025 IMC :: exfiltration, overwriting, and deletion 2025 PETS :: set after accepted banner, then sent before banner rejection 2025 USENIX :: trackers, location, IP address, language, unlikely personal information 2025 USENIX :: Ignored Cookie Rejection, Undeclared Cookies, Wrong Cookie Category Strict count, exact string naming the ICC or its author: 8 papers. Folded count including the restatements: 11 papers. Both are printed because the merge is a judgement. Every contributing string: 2016 WWW "Cookiepedia categories: strictly necessary, performance, functionality, targeting/advertising, unknown" 2020 WWW "ICC UK four cookie purposes" 2022 PETS "ICC UK four purposes: Strictly Necessary, Performance, Functionality, Targeting" 2022 USENIX "ICC UK four purposes plus unclassified and other" 2022 USENIX "ICC UK four purposes" 2022 USENIX "ICC UK four purposes" 2023 CCS "strictly necessary, functional, analytics, advertising/tracking" 2024 IMC "ICC UK four categories plus unclassified" 2024 PETS "ICC UK four cookie purposes" 2024 USENIX "UK ICC: necessary, functional, analytics, advertising" 2024 PETS "International Chamber of Commerce UK four cookie categories" 2024 USENIX "strictly necessary, functional, analytics, advertising/tracking" 2024 WWW "UK International Chamber of Commerce: strictly necessary, functionality, performance, targeting/advertising" ============================================================================== 6. GROUND TRUTH AND VALIDATION — where the field goes quiet ============================================================================== Field Cookie papers stating it of 53 Whole classified corpus of 4439 ----------------------------------------------------------- ------------------------ ----- ----------------------- ------- names a ground-truth source 30 56.6% 3234 72.9% reports any validation (not none-reported / not-applicable) 17 32.1% 3111 70.1% ============================================================================== 6.1 VALIDATION, PER VALUE (cookie tuples; enum, so publishable as a percentage) ============================================================================== validation Papers Share of 53 -------------------------- ------ ----------- none-reported 27 50.9% not-applicable 16 30.2% manual-validation 13 24.5% comparison-to-other-method 4 7.5% cross-validation 3 5.7% held-out-test-set 3 5.7% ============================================================================== 6.2 METHOD ENUM, PER VALUE (cookie tuples), against the classified corpus ============================================================================== classification.method Cookie papers of 53 Corpus papers of 4439 --------------------- ------------- ----- ------------- ------- heuristic-rules 30 56.6% 2183 49.2% curated-database 19 35.8% 649 14.6% blocklist 6 11.3% 189 4.3% supervised-ml 5 9.4% 1087 24.5% regex-or-signature 3 5.7% 310 7.0% manual-labelling 2 3.8% 1381 31.1% third-party-service 2 3.8% 643 14.5% graph-analysis 2 3.8% 154 3.5% other 1 1.9% 394 8.9% llm 1 1.9% 177 4.0% dynamic-analysis 0 0.0% 300 6.8% unsupervised-ml 0 0.0% 320 7.2% static-analysis 0 0.0% 472 10.6% ============================================================================== 7. LLM CLASSIFICATION OF COOKIES — the currency check ============================================================================== Classification target Papers classifying it (used/produced) ...of which an LLM LLM share <-- PUBLISHED (any/any numerator) (any/any denominator) (any/any share) --------------------- ------------------------------------- ------------------ ------------------------ ------------------- --------------------- --------------- privacy-policy 102 12 11.8% 12 102 11.8% dark-pattern 13 1 7.7% 1 13 7.7% user-generated-text 419 30 7.2% 32 419 7.6% consent-notice 39 2 5.1% 2 39 5.1% other 2592 115 4.4% 116 2594 4.5% mobile-app 280 6 2.1% 6 282 2.1% email-message 54 1 1.9% 1 54 1.9% cookie 53 1 1.9% 1 53 1.9% vulnerability 880 14 1.6% 14 883 1.6% website-category 424 5 1.2% 5 424 1.2% domain 351 3 0.9% 3 351 0.9% network-traffic 382 2 0.5% 2 383 0.5% web-request 258 1 0.4% 1 262 0.4% ip-address 295 1 0.3% 1 295 0.3% malware 159 0 0.0% 0 160 0.0% sdk-or-library 77 0 0.0% 0 77 0.0% javascript 44 0 0.0% 0 44 0.0% fingerprinting-script 31 0 0.0% 0 32 0.0% website-popularity 15 0 0.0% 0 16 0.0% The PUBLISHED column is columns 2-4 and matches report_llm_currency.mjs row for row. Rows where the two definitions disagree: user-generated-text: used/used 30/419 = 7.2% any/any 32/419 = 7.6% other: used/used 115/2592 = 4.4% any/any 116/2594 = 4.5% The cookie row is the one privacy:cookies and design:website_classification must agree on. LLM + cookie: WWW/2025/semantics-aware-cookie-purpose-compliance ============================================================================== 8. HOW THE COOKIE PAPERS CRAWL ============================================================================== 49 of the 53 cookie papers have a crawlConfig object; 1080 of the 1120 crawling papers do. crawlConfig field Cookie papers stating it of 49 All crawling papers of 1080 ----------------- ------------------------ ----- ------------------- ------- consentAction 32 65.3% 349 32.3% statefulness 36 73.5% 219 20.3% interactionDepth 47 95.9% 841 77.9% browsers 40 81.6% 529 49.0% headless 10 20.4% 140 13.0% authentication 42 85.7% 779 72.1% ============================================================================== 8.1 consentAction, per value (cookie papers with a crawlConfig) ============================================================================== consentAction Papers Share of 49 -------------------------------------- ------ ----------- no-interaction 22 44.9% not-stated (sentinel — not an answer) 17 34.7% accept-and-reject 7 14.3% reject-all 2 4.1% accept-all 1 2.0% ============================================================================== 9. PASS C — full-text probes for what the schema cannot see ============================================================================== readable paper.cols.txt files: 5869 <-- denominator for every row below Probe Papers matching Share of 5869 ----------------------------------------- --------------- ------------- Cookiepedia (name) 19 0.3% CookieBlock (name) 16 0.3% CookieGraph (name) 20 0.3% "Cookie Script" / Cookie-Script 11 0.2% cookiedatabase.org 2 0.0% Open Cookie Database 0 0.0% cookiesearch.org 0 0.0% ICC / International Chamber of Commerce 13 0.2% zxcvbn, within 150 chars of "cookie" 2 0.0% entropy, within 150 chars of "cookie" 35 0.6% an LLM name, within 150 chars of "cookie" 5 0.1% A name match is a MENTION, not a use: it counts reference-list entries and related-work sentences. It is never added to a PASS A count. The rows worth reading individually are the small ones: --- zxcvbn, within 150 chars of "cookie" (2) IEEE-SP/2022/journey-to-the-center-of-the-cookie-ecosystem-unraveling-actors-roles-and-relati ...entifier cookies and therefore, construct the cookie tree only for those. We pre-filter all cookies that are unlikely to be identifiable by using the zxcvbn technique proposed in a recent work [26]. B. Mapping Domains to... USENIX/2025/navigating-cookie-consent-violations-across-the-globe ...ex patterns (e.g., IP address, GPS location, and common tracker formats), as well as decoding Base64-encoded cookies. The detector also leverages the zxcvbn [69] library for measuring the entropy of the cookie values (to... --- an LLM name, within 150 chars of "cookie" (5) CCS/2025/automatically-detecting-online-deceptive-patterns ...ominent than the other, leading to an AutoBot to inform developers of potential deceptive patterns on incorrect classification of "nudge". Similarly, Gemini 2.5 Pro incor- their sites, integrating directly into developer... IMC/2025/from-voice-to-ads-auditing-commercial-smart-speakers-for-targeted-advertising-ba ...first-party tracking cookies. In Proceedings of the 2023 ACM SIGSAC Conference on Computer and Communications Security. 3490-3504. [79] OpenAI. 2023. GPT-4o. https://platform.openai.com/docs/models#gpt-4o. [80] Pi-hole. ... WWW/2025/semantics-aware-cookie-purpose-compliance ...er features such as expiration time. Coover decomposes the cookie value into primitive segments representing minimal semantic units, and fine-tunes a GPT-3.5 model to automatically interpret their value-inferred semantic... IEEE-SP/2026/setting-the-course-but-forgetting-to-steer-analyzing-compliance-with-gdprs-right ...Amit Zac, Carlos Cotrini, and David Basin. Automated large-scale analysis of cookie notice compliance. In USENIX Security, 2024. [8] Google DeepMind. Gemini 2.0: A multimodal ai model. https:// deepmind.google/technologi... PETS/2026/websp-eval-evaluating-web-agents-on-website-security-and-privacy-tasks ...Safety & Content Moderation (25 out of 31). Similar to performance breakdown on websites, here too we notice that specific models perform better than Gemini-3-Pro on some task categories. For example, Claude-Sonnet-4.5 a... --- "Cookie Script" / Cookie-Script (11) NDSS/2019/we-value-your-privacy-now-take-some-cookies-measuring-the-gdprs-impact-on-web-privacy ...(name match)... CCS/2021/sugarcoat-programmatically-generating-privacy-preserving-web-compatible-resource ...(name match)... IEEE-SP/2022/the-state-of-the-samesite-studying-the-usage-effectiveness-and-adequacy-of-sames ...(name match)... PETS/2022/on-dark-patterns-and-manipulation-of-website-publishers-by-cmps ...(name match)... USENIX/2022/automating-cookie-consent-and-gdpr-violation-detection ...(name match)... CCS/2023/cookiegraph-understanding-and-detecting-first-party-tracking-cookies ...(name match)... IMC/2024/browsing-without-third-party-cookies-what-do-you-see ...(name match)... PETS/2024/supporting-informed-choices-about-browser-cookies-the-impact-of-personalised-coo ...(name match)... CCS/2025/layered-overlapping-and-inconsistent-a-large-scale-analysis-of-the-multiple-priv ...(name match)... IMC/2025/cookieguard-characterizing-and-isolating-the-first-party-cookie-jar ...(name match)... WWW/2025/semantics-aware-cookie-purpose-compliance ...(name match)... --- cookiedatabase.org (2) USENIX/2024/automated-large-scale-analysis-of-cookie-notice-compliance ...(name match)... PETS/2025/empirically-measuring-data-localization-in-the-eu ...(name match)... --- Open Cookie Database (0) --- cookiesearch.org (0) ============================================================================== 10. MEASURED RESULTS — detection[].prevalence on cookie phenomena ============================================================================== 210 prevalence strings across 107 of the 117 cookie-measuring papers. metric/prevalence names the unit, often not the population: every figure the page publishes has had its denominator read out of the paper by hand (section 11). 2010 CCS cookie stealing 48 same-origin, 38 same-domain, and 17 whitelist violations 2010 WWW Cookie overestimation 991 cookies for 171 participants, producing 5.8x inflation 2010 WWW Cookie clearing 43% admitted clearing cookies at least once a week or more often 2010 IEEE-SP DOM-cookie interplay 81% read document.cookie; 76% wrote cookie values; 46% changed cookie domains 2010 IEEE-SP HttpOnly cookie access via XMLHttpRequest 0.30% manipulated cookie headers in XMLHttpRequest responses 2011 CCS HttpOnly credential-cookie adoption Zan applied HttpOnly automatically to 110 of 136 websites; it correctly identified authentication cookies for 103. 2011 IMC cookie replay attacks One cookie appeared across 666,429 different 24-bit IP subnets and 236 publisher accounts 2012 NDSS Cookie churn 47.86% of first-day cookie IDs never returned within the month 2012 NDSS Returning users behind churned cookies 88.03% corresponded to returning users 2012 NDSS Cookie-forwarding attack Over 75,000 Hotmail accounts were associated with the suspicious source IP 2014 WWW authentication-cookie identification 103 of 327 cookies 2014 WWW authentication-cookie detector performance Existing detectors produced 8–55 false negatives and 37–105 false positives. 2014 WWW machine-learned cookie classification FT achieved 81% sensitivity and 85% specificity. 2014 CCS Flash-cookie HTTP respawning 33 Flash cookies respawned 355 cookies on 107 first-party domains 2014 CCS IndexedDB evercookie 20 out of 100,000 sites used IndexedDB; one matched a Flash cookie 2014 CCS cookie synchronization Disabling third-party cookies reduced synced IDs and parties by nearly a factor of two 2014 CCS respawned-ID cookie syncing At least 11% of a user's history could be tracked over time 2015 USENIX TLS secure-cookie recovery More than 94% with 9 · 227 ciphertexts and 223 candidates. 2015 USENIX TLS cookie recovery in practice A 16-byte cookie was recovered after 52 hours and 6.2 · 227 ciphertexts. 2015 USENIX Shared-domain cookie injection 12 of 13 tested shared domains; 72KB cookies successfully triggered DoS 2015 USENIX Secure cookies over HTTP 152 of 48,039 responding domains (0.32%) 2015 USENIX Duplicate-name cookies 570 of 48,039 responding domains (1.19%) 2015 USENIX Browser cookie implementation flaws All major browsers exhibited at least some documented inconsistencies or vulnerabilities 2015 USENIX Real-world cookie injection attacks Included Google, Amazon, eBay, Apple, Bank of America, BitBucket, and open-source applications 2015 USENIX Secure-cookie extension compatibility One broken case among Alexa top 40 websites 2015 WWW cookie-based traffic clustering 62.4±3.2% after 200 sites for AOL profiles; 72.9±1% for Alexa profiles 2015 IMC affiliate cookie stuffing 12,033 affiliate cookies from 11.7K domains 2015 IMC cookie-stuffing prevalence over 475K domains visited 2015 NDSS HSTS cookie exposure 23.8% (182/765) preloaded; 47.8% (2,460/5,099) dynamic 2015 NDSS Pinned cookie exposure 75 cookies on 5 pinned domains; 63 (84.0%) lacked secure 2015 WWW cookie stealing Attack worked for both Twitter and Facebook in Layar 2016 CCS ID cookies 32 sites contained ID cookies when third-party cookies were blocked. 2016 CCS cookie syncing 45 of the top 50 and 85 of the top 100 third parties synced cookies with at least one other party. 2016 USENIX cookie-based tracking the first cookie-based tracker in the dataset appeared in 1996 2016 USENIX Cookie matching forward matching 69%, backward matching 55% before clustering 2016 USENIX Heuristic cookie-matching detection failures 31% of cookie-matching partners were missed; 4.1% of chains were completely missed 2016 WWW first- and third-party cookie prevalence 68.0% (63.42%) of cookies were third-party; 94.6% (94.38%) of successfully visited sites set cookies. 2016 WWW cookie attribute characteristics 99.64% (99.76%) of cookies lacked the Secure flag. 2016 WWW maximal-permission cookies 80.6% (79.73%) of harvested cookies had maximal permissions. 2016 WWW cookie information leakage Google, Twitter, Yahoo, YouTube, and Rubiconproject showed different leakage rates across synthetic user profiles. 2016 IEEE-SP HTTP cookie hijacking exposure 282,459 accounts exposing cookies during 30 days 2016 IEEE-SP Campus HTTP cookie exposure 282,459 vulnerable accounts; 29,908,099 HTTP requests to audited domains 2016 IEEE-SP Browser-component cookie exposure Every Firefox extension tested exposed the required cookies. 2016 IEEE-SP Mobile-app cookie exposure Both platforms had official apps using unencrypted connections. 2017 PETS Cookie syncing 106 domains transmitted cookie IDs to 210 different domains 2017 PETS Cross-device reuse of cookie values No third parties were detected using the same cookie values across multiple devices 2018 USENIX Facebook cookie exposure 5,729 unique vulnerable cookies over four months 2018 USENIX third-party cookie-policy bypasses virtually every browser and extension policy could be bypassed 2018 USENIX same-site cookie bypasses incorrect behaviors were detected for Chrome, Opera and Edge 2018 WWW Cookie Synchronization 97% of regular users were exposed at least once 2019 IEEE-SP cookie access 8 Chrome extensions 2019 IEEE-SP session cookie confidentiality 412 websites (10.2%) 2019 IEEE-SP session cookie integrity 543 websites (13.5%) 2019 IMC third-party HTTP cookies 72% of the corpus contained third-party cookies 2019 IMC cookie synchronization 2,867 websites, covering 58% of the top-100 popular sites 2019 IMC cookie consent banners 4.41% in the EU and 3.76% in the USA 2019 NDSS cookie-consent notices 62.1% in May 2018, up from 46.1% in January 2019 NDSS cookies about 5.4 third-party cookies on average 2019 NDSS cookie-consent library prevalence 15.4% in July 2018 2019 PETS Third-party cookie installation 74% of websites install third-party cookies 2019 PETS Profiling-cookie installation 49% of popular websites 2019 PETS Consent-related cookie installation 80.5% install profiling cookies before consent 2019 PETS Longitudinal cookie-law violations 57% in January 2015 and 56% in November 2018 2019 WWW Cookie synchronization 97% of regular web users were exposed to CSync 2019 WWW Cookie synchronization 263,635 synchronization events involving 22,329 unique synced userIDs 2019 WWW Cookie identifier leakage The median userID was leaked to 3.5 different domains 2020 CCS authentication-cookie exposure over HTTP 12,014 domains (48.43%) initially; 10,921 remained vulnerable after HSTS analysis 2020 CCS JavaScript cookie stealing exposure 5,680 domains (22.9%) lacked httpOnly protection; 5,099 also embedded third-party scripts 2020 CCS personal-data leakage after cookie hijacking 9,324 domains according to the abstract; Table 3 reports 9,122 (78%) 2020 CCS authentication-cookie combinations 1.21 authentication cookies and 1.1 combinations on average 2020 PETS HTTP cookie tracking 5,574 different trackers: 695 mobile-specific, 1,536 desktop-specific, and 3,343 shared 2020 PETS client-side cookie syncing 4 of 15 inferred relationships detected by client-side cookie syncing 2020 PETS identifier cookies 5,295 cookie instances (0.87%) had an identifier cookie as key 2020 PETS first-to-third-party cookie syncing 67.96% of visited domains 2020 USENIX Invalid cookie computation Four implementations incorrectly computed cookie values 2020 WWW subsite cookie usage Subsites set 36% more cookies: 78 versus 55 on landing pages. 2020 WWW cookie purposes 99% of classified cookies were used for tracking or targeted advertising. 2020 WWW undesired cookies around 95% of domains created undesired cookies 2020 WWW first-party cookie bypass 87% resulted in at least one new domain being added to the whitelist 2020 WWW third-party cookie tracking right-leaning websites tracked users with up to 25% more cookies 2020 WWW cookie synchronization right-leaning websites facilitated up to 50% more synchronizations 2020 IEEE-SP TCF cookie banners 1,426 of 22,949 websites (6.2%) 2020 IEEE-SP Shared-cookie consent reuse 62 of 1,426 websites (4.3%) returned the same consent string 2021 NDSS cookie security-attribute inconsistency 1,546 (4%) cookies from 642 (9%) sites 2021 NDSS cookie leakage through HSTS inconsistencies 1,783 sites, or 33% of sites activating HSTS 2021 PETS Cookie notices and consent prompts 5% overall error rate in manual checking 2021 PETS Cookie leaks in HTTP headers 95% of sites with an identified CNAME tracker had one or more cookie leaks. 2021 PETS Cookie leaks in POST bodies 166 cookies on 94 distinct sites were sent in POST bodies. 2021 PETS Cookie leaks in request URLs 1,899 cookie leaks on 1,295 distinct sites. 2021 PETS cookie syncing 8 Cookie_sync predicates for the Top 50 domains. 2021 USENIX cookie confidentiality and integrity 3,390 (14%) confidentiality-affected and 24,689 (99%) integrity-affected session cookies 2021 WWW external first-party cookies 9,772 of 10,000 websites (97.72%) 2021 WWW tracking-ID cookies 4,212 of 13,323 (31.61%) 2021 WWW tainted cookie leakage 3,256 of 4,212 (77.30%) 2021 WWW cross-domain cookie sharing 5,635 of 9,772 websites (57.66%) 2021 WWW fingerprinting-generated cookies 3 cookies highly indicative of fingerprinting 2022 CCS opt-out cookies 84% F1, 97% precision, and 74% recall on the test set 2022 CCS Cookie security-attribute modification 31 extensions modified the security properties of cookies 2022 NDSS cookie manipulation 736 vulnerabilities 2022 PETS cookie syncing 7 advertisers engaged in cookie syncing; 4 synced with an Alphabet-owned tracker. 2022 PETS Cookie-notice practices Only 14 websites presented Accept without highlighting it; three allowed rejection as easily as acceptance. 2022 PETS cookie respawning with browser fingerprinting 1,150 of 30,000 websites (3.83%) 2022 PETS third-party tracking in Cookie Script report 41 third-party requests and 6 cookies were deposited without prior consent 2022 USENIX Incorrect cookie purposes 36.4% had at least one incorrectly assigned cookie purpose 2022 USENIX Unclassified or undeclared cookies 85.8% had at least one cookie with a missing declaration or missing purpose 2022 USENIX Cookie expiration discrepancies 9.1% showed at least one discrepancy 2022 USENIX Cookies before consent 69.7% of examined websites 2022 USENIX Cookies despite negative consent 21.3% of 29,398 websites 2022 USENIX Multiple cookie labels 2.3% of examined websites 2022 USENIX Cookie-purpose majority outliers 30.9% contained at least one outlier assignment 2022 USENIX cookie syncing More than half of request chains in most configurations participated in cookie syncing. 2022 USENIX Cross-browser cookie synchronization 256 random bits transmitted without error 2022 USENIX cookie security inconsistencies 16 intra-test; 167 inter-test inconsistencies 2022 WWW persistent tracking cookies 13% (19,566) contained YouTube cookies expiring in 9999 2022 IEEE-SP Session-cookie leakage Recovered an HttpOnly session cookie after Chrome developer tools copied it into renderer memory. 2022 IEEE-SP SameSite cookie adoption 18.94% of sites adopted one of the three valid policies by March 2021 2022 IEEE-SP Cookie persistence after login 48% accepted cookies 40 days after the initial login 2022 IEEE-SP cookie creation 137,997,677 cookies 2022 IEEE-SP identifier cookies 48% (66.7M) classified as identifiers 2022 IEEE-SP cookie sharing 8.97M events over 387K websites 2022 IEEE-SP cookie collisions 184,377 collision events 2022 IEEE-SP cookie ghostwriting 28M ghosted cookies versus 7.6M primary first-party cookies 2022 IEEE-SP risky cookie actors 9% of safe websites included a dangerous domain 2023 CCS First-party tracking cookies 89.86% of the 20K websites 2023 CCS Third-party ghostwriting of first-party cookies 96.61% were set by third-party embedded scripts 2023 CCS Fingerprinting scripts setting cookies 1,908 out of 20K sites 2023 CCS CookieGraph classification 90.18% accuracy, 90.07% precision, and 92.09% recall 2023 IMC cookie measurement variance 42% of cookies appear in only one profile 2023 PETS Persistent-session cookie-like storage Mobile Opera and Samsung Internet retained sessions after cookie and site-data deletion. 2023 USENIX Cookie-stealing resistance 10 of 46 blocking sites blocked access after cookie copying 2023 IMC cookiewall prevalence 280 of 45,222 websites (0.6%) 2023 IMC cookiewall detection 98.2% precision; 100% precision and recall on 1,000 sampled websites 2023 IMC tracking cookies Cookiewalls sent 43 versus 1 tracking cookie; 42 times more 2023 IMC Cookie syncing 41 advertisers synced cookies with Amazon and 247 other third parties 2023 USENIX cookie notice detection 986 of 2000 domains identified as having cookie notices; 16 false negatives and 2 false positives 2023 USENIX end-to-end cookie enforcement 93.7% (937/1000 websites) 2023 USENIX cookie notices at scale 52.7% of 85,473 analyzed websites 2023 USENIX multiple-view cookie notices 35.4% had multiple views; 64.6% had a single-view notice 2023 PETS third-party cookies without consent 7.34% of sites with at least one privacy issue 2023 PETS third-party cookies before consent 7.57% of sites with at least one privacy issue 2023 USENIX cookie churn merge misses detected in only 29 groups 2023 PETS Cookie-banner prevalence 2,220 websites (35.74%) had a cookie banner. 2023 USENIX cookie-prefix and nameless-cookie adoption More than 70% of top-1K origins used Secure cookies; 133 origins used __Host- in 2022 versus 77 in 2021. 2023 IEEE-SP Cookie rejection handling 52.38% detected rejection; 40.48% traced to the correct UI element; 7.14% neither 2023 IEEE-SP cookie acceptance inference 749 additional sites vulnerable, reaching 34% overall 2024 IMC Cookie-notice tracking-cookie violations 100 (39%) of 255 websites continued using tracking cookies after rejection 2024 IMC Third-party-cookie rendering effects More than 90% of domains exhibited less than a 10% screenshot difference 2024 CCS First-party tracking cookies 150 of 14,867 ghost first-party cookies 2024 CCS Third-party-cookie re-identification Analysts using third-party cookies always succeeded on average. 2024 PETS website breakage from cookie misclassification 18 of 40 participants required a prompt 2024 PETS CookieBlock mental-model accuracy Before: 15 inaccurate, 11 partially accurate, 14 accurate; After: 14, 12, and 12 2024 PETS Cookie-category comprehension 7% correctly identified the original functional-cookie definition; 67% identified anonymous analytics. 2024 PETS USP String cookie storage 358 of 10,000 publishers (3.6%) stored a USP String in a cookie 2024 PETS cookie acceptance PPA mean 76, PA mean 99, and control mean 173 accepted cookies 2024 PETS cookie-banner usability PPA mean 83, PA mean 72, and control mean 63 SUS scores 2024 PETS cookie-banner dark nudges Four out of seven pages used dark nudges 2024 PETS identifier-cookie tracking 474 of 996 cloaked domains (47.59%) 2024 PETS cookie-purpose distribution 35% 2024 USENIX Cookie notice detection 100.0% precision and 86.9% recall 2024 USENIX Cookie consent violations 72.2% of successfully crawled websites contained at least one privacy violation 2024 USENIX Undeclared cookie purposes 26.1% of 48,843 websites 2024 USENIX cookie consent banner presence 37% (1511) of the total dataset included a cookie consent notice 2024 USENIX cookie changes after rejection Only 7 local and 2 remote websites rejected cookies successfully on all tested websites 2024 USENIX cookie-banner text complexity Italian banners were most complex and UK banners least complex 2024 PETS Cookie setting and persistence 415k cookies per profile on average 2024 PETS Cookie-purpose categories 57% of all cookies classified; 38% of distinct cookies 2024 PETS Cookie-banner interaction Extensions interacted with 12 banners on average, or 65% 2024 PETS First-party cookie effects fbp did not improve effectiveness; fbc increased effectiveness from 20% to 23% 2024 PETS Client-side cookie syncing Cookie syncing persisted after opt-out across CMPs and jurisdictions. 2024 USENIX Plaintext cookie storage only two apps encrypted cookies; 66 stored sensitive information 2024 USENIX Cookie-consent decisions 17% on the control banner; 34% for highlighted decline; 47% for consequences 2024 USENIX Cookie knowledge and privacy preferences 62% knew browsing history was collected; 61% knew cookies personalize ads. 2024 WWW Tracking cookies after consent rejection Compliant, non-empty-TC domains averaged 1.28 tracking cookies; all domains averaged 1.11. 2024 USENIX Cookie and Mixed Content flaws Five invariants had at least one violation 2024 WWW Private-mode cookie storage 17 browsers, 34.7% 2024 WWW Third-party cookie persistence 4 browsers, 8.2% 2024 WWW Cookie sharing across modes 16 browsers, 32.7% 2025 IMC cross-domain cookie exfiltration 55.7% of websites; 5.9% of 82,000 unique cookie pairs 2025 IMC cross-domain cookie overwriting 31.5% of websites; 2.72% of unique cookie pairs 2025 IMC cross-domain cookie deletion 6.3% of websites; 1.8% of unique cookie pairs 2025 IMC CookieGuard access-control effectiveness 82.2% overwriting, 86.2% deletion, and 83.2% exfiltration reductions 2025 IMC CookieGuard runtime overhead average overhead of 0.3 seconds across 8,171 paired sites 2025 PETS AA cookies after revocation 57.5% (69/120) retained AA cookies after revocation 2025 PETS intractable cookie transmission around 50% of websites; approximately 45%–55% across runs 2025 PETS cookie persistence nearly 60% of cookies have expiration exceeding 10 days 2025 PETS cookie synchronization 76 of 2,545 unique intractable cookies, or 3% 2025 PETS partitioned-cookie adoption 40 of 3,177 tracking cookies, or 1.3%; 26 had accompanying non-partitioned cookies 2025 PETS tracking cookies 236 websites loaded 9,885 cookies containing 1,153 unique identifiers 2025 WWW trackers and third-party cookies Developed sites averaged approximately nine trackers; developing sites averaged six 2025 WWW Set-Cookie misconfiguration Over 98% set the Path directive to '/,' making cookies accessible across all directories. 2025 WWW Third-party cookie accesses 89.84% of all cookie accesses 2025 WWW Host-created cookies accessed by third parties 20.14% of all cookies accessed by third-party scripts 2025 USENIX cookie consent violations 96.18% in the EU to 97.72% in the US 2025 USENIX ignored cookie rejection 43.12% of cookies ignored consent rejections 2025 USENIX undeclared cookies 47.35% of personal information cookies 2025 USENIX wrong cookie category 3.13% of websites 2025 USENIX personal information in cookies roughly 75% likely involve personal information 2025 WWW potential cookie purpose violation 44.1% of websites suffer from potential purpose violation 2025 WWW potentially non-compliant cookies 3,759 of 10,846 observed cookies (34.7%) 2025 WWW unclaimed cookie usage 511 cookies among 289 websites 2025 WWW missing cookie declarations 25.4% of websites fail to provide cookie declarations 2025 WWW cookie-purpose classification F1 score of 0.95 2025 CCS Third-party cookies 1,454 banks (70.1%) allowed third-party cookies; 1,252 (60.4%) had marketing cookies 2025 PETS Cookies and impression pixels 39.3% of WebViews ran JavaScript setting at least one cookie or impression pixel 2025 PETS Cookie synchronization and ID bridging Cookie synchronization involved 1,190 SLDs, with 44% associated with ATSes 2025 PETS Third-party tracking cookies less than 51% across all locations 2025 PETS cookie setting 20,526,074 distinct cookies; 22% of all cookies 2025 WWW cookie restoration 378 restoration instances across 90 unique cookie-key and host pairs. 2025 USENIX Credential versus session-cookie sharing 87.9% credentials and 12.1% session cookies 2026 PETS gclid cookie persistence 64 of 76 advertisers stored gclid as a first-party cookie ============================================================================== 11. QUOTE SPOT-CHECK — every figure the page states, against paper.cols.txt ============================================================================== 8-word run 2023 CCS :: First-party tracking cookies prevalence: 89.86% of the 20K websites quote: We find that 89.86% of sites deploy at least one first-party ATS cookie. 8-word run 2023 CCS :: Third-party ghostwriting of first-party cookies prevalence: 96.61% were set by third-party embedded scripts quote: The vast majority (96.61%) of the first-party ATS cookies are set by third-party embedded scripts served from a total of 2,099 unique domains. 8-word run 2023 CCS :: CookieGraph classification prevalence: 90.18% accuracy, 90.07% precision, and 92.09% recall quote: The classifier has 90.07% precision and 92.09% recall, with an overall accuracy of 90.18%. EXACT 2022 USENIX :: Incorrect cookie purposes prevalence: 36.4% had at least one incorrectly assigned cookie purpose quote: In 36.4%, we found at least one cookie with an incorrectly assigned purpose EXACT 2022 USENIX :: Unclassified or undeclared cookies prevalence: 85.8% had at least one cookie with a missing declaration or missing purpose quote: in 85.8%, there was at least one cookie with a missing declaration or missing purpose. EXACT 2022 USENIX :: Cookie-purpose majority outliers prevalence: 30.9% contained at least one outlier assignment quote: We find that 30.9% of websites contain at least one third-party cookie with a purpose that disagrees with a corresponding two-thirds majority. EXACT 2025 WWW :: cookie-purpose classification prevalence: F1 score of 0.95 quote: Coover demonstrates a superior performance, achieving an F1 score of 0.95 EXACT 2025 WWW :: potential cookie purpose violation prevalence: 44.1% of websites suffer from potential purpose violation quote: It finds that 44.1% of the websites potential suffer from purpose noncompliance issues. EXACT 2025 WWW :: missing cookie declarations prevalence: 25.4% of websites fail to provide cookie declarations quote: Around 25.4% of websites fail to provide the cookie declaration for users. EXACT 2024 IMC :: Cookie-notice tracking-cookie violations prevalence: 100 (39%) of 255 websites continued using tracking cookies after rejection quote: we found that 100 (39%) of them do not respect the user's choice and continue to use tracking cookies even after the user opts out EXACT 2024 PETS :: Cookie-purpose categories prevalence: 57% of all cookies classified; 38% of distinct cookies quote: Using Cookiepedia, we could classify 57% of all cookies and 38% of all distinct cookies in our dataset EXACT 2024 USENIX :: Undeclared cookie purposes prevalence: 26.1% of 48,843 websites quote: We found that 26.1% of the 48,843 websites with cookie notices use AA cookies without declaring AA purposes in the initial text of the cookie notice. EXACT 2025 USENIX :: wrong cookie category prevalence: 3.13% of websites quote: We also detected contradictory cookie banners on 3.13% of websites (Wrong Cookie Category Violation). EXACT 2025 USENIX :: personal information in cookies prevalence: roughly 75% likely involve personal information quote: Roughly 75% of all cookies found in our measurement likely involve personal information. 8-word run 2025 PETS :: partitioned-cookie adoption prevalence: 40 of 3,177 tracking cookies, or 1.3%; 26 had accompanying non-partitioned cookies quote: among 3,177 unique tracking cookies, only 40 (1.3%) are partitioned, of which 26 are accompanied by non-partitioned tracking cookies EXACT 2025 WWW :: Third-party cookie accesses prevalence: 89.84% of all cookie accesses quote: In total, 89.84%, 90.98%, and 72.49% of all cookies, localstorage, and IndexedDB accesses (both read and write), respectively, are done by third-party JS. 8-word run 2024 PETS :: Cookie-category comprehension prevalence: 7% correctly identified the original functional-cookie definition; 67% identified anonymous analytics. quote: Only 7% of participants were able to correctly identify the definition for the original term "functional" cookies. ... 67% selected the correct definition for the candidate term "anonymous analytics" EXACT 2022 IEEE-SP :: identifier cookies prevalence: 48% (66.7M) classified as identifiers quote: we classify 48% (66.7M) as identifiers with the zxcvbn technique described in Section III-A. 18 figures checked: 13 exact, 5 via an 8-word run, 0 not found. ============================================================================== DONE ==============================================================================
- report_cookies.mjs
#!/usr/bin/env node // Report script for the wiki page privacy:cookies ("Classifying Cookies"). // // node scripts/report_cookies.mjs # full audit trail // node scripts/report_cookies.mjs --wiki # the tables the page carries // // Every figure prints its own denominator. Counts are of PAPERS, never tuples. // Sentinels (not-stated / none-mentioned / not-applicable) are never counted as // answers. Free-text names are folded before aggregating and every fold prints // its unmapped residue. // // Three passes: // // PASS A structured extraction, data/extract/run1/extractions.jsonl. // The page's core population is `classification[].target == "cookie"`, // which is an enum and therefore stable. The wider "measures // something about cookies" set adds detection[].phenomenon matches // and is used only for context and for the measured-results table. // // PASS B a hand-keyed map from the free-text `resourceName` of every cookie // classification tuple to (a) the FAMILY of label source and (b) the // QUESTION the classifier answers. The script THROWS if a tuple's // resourceName is not in the map, so a corpus refresh cannot silently // drop a paper out of a family. This is the split the page publishes; // it is produced here, not written by hand into the prose. // // PASS C full text, data/fulltext/<year>/<venue>/<slug>/paper.cols.txt. // Whitespace collapsed and end-of-line hyphenation joined first: a // PDF line break inside "Cookie-\npedia" otherwise undercounts. // Used for the label sources that are not in the schema at all // (Open Cookie Database, cookiesearch.org) and for the entropy and // LLM probes, and to spot-check evidence quotes. import fs from 'node:fs'; import path from 'node:path'; import { dataRoot, loadExtractions, isSentinel, table, wikiTable, pct } from './lib.mjs'; const WIKI = process.argv.includes('--wiki'); const ROOT = dataRoot(); const FT = path.join(ROOT, 'fulltext'); const h = (s) => console.log('\n' + '='.repeat(78) + '\n' + s + '\n' + '='.repeat(78)); const T = (head, rows) => console.log(WIKI ? wikiTable(head, rows) : table(head, rows)); const key = (p) => `${p.venue}/${p.year}/${p.slug}`; const rows = loadExtractions(); // --------------------------------------------------------------------------- // PASS B — the hand-keyed map. Keyed on the exact resourceName string of a // cookie-target classification tuple. // // family: what kind of thing does the labelling. // question: WHAT is being decided about the cookie. The literature routinely // conflates four different questions under "cookie classification", // and mixing them is how a page ends up saying the field classifies // cookie purposes when a third of it is detecting identifiers. // // purpose which of N purpose categories (necessary / functional / // analytics / advertising, or the CMP's own scheme) // tracking binary: does this cookie belong to a tracker, decided from the // provenance of the resource that set it (filter list, tracker DB) // identifier binary: does the VALUE carry a per-user identifier // party first vs third party, eTLD+1, who ghostwrote it // security authentication/session role, or correctness of the attributes // other anything else, named individually below // --------------------------------------------------------------------------- const RESOURCE = { // --- purpose: the commercial and community cookie databases 'Cookiepedia': ['purpose-database', 'purpose'], 'Cookie-Script': ['purpose-database', 'purpose'], 'Cookie Script': ['purpose-database', 'purpose'], 'Cookiedatabase.org': ['purpose-database', 'purpose'], 'Cookie Database': ['purpose-database', 'purpose'], // --- purpose: published cookie classifiers, reused off the shelf 'CookieBlock': ['published-classifier', 'purpose'], 'CookieGraph': ['published-classifier', 'tracking'], 'WebGraph': ['published-classifier', 'tracking'], // --- purpose: the paper trains its own supervised model 'XGBoost (custom)': ['own-supervised-ml', 'purpose'], 'random forest (custom)': ['own-supervised-ml', 'tracking'], 'functional tree (FT)': ['own-supervised-ml', 'security'], // --- purpose: LLM 'GPT-3.5-turbo (custom fine-tuned model)': ['llm', 'purpose'], 'GPT-3.5': ['llm', 'purpose'], // --- purpose: humans 'three independent authors': ['manual', 'purpose'], 'custom gold-set labeling procedure': ['manual', 'security'], 'custom keyword mapping': ['own-rule', 'purpose'], // --- tracking: filter lists and tracker databases (provenance of the setter) 'EasyList': ['filter-list', 'tracking'], 'EasyList/EasyPrivacy': ['filter-list', 'tracking'], 'justdomains': ['filter-list', 'tracking'], 'justdomains blocklist': ['filter-list', 'tracking'], 'Ghostery': ['filter-list', 'tracking'], 'Ghostery and Disconnect intersection': ['filter-list', 'tracking'], 'Disconnect.me': ['filter-list', 'tracking'], 'custom ad-blocker name search lists': ['own-rule', 'other'], 'Evidon Global Opt-out, DAA Protect My Choice, and Google Keep My Opt-Outs': ['opt-out-registry', 'other'], // --- identifier: value-based heuristics 'zxcvbn': ['identifier-heuristic', 'identifier'], 'custom ID-cookie heuristic': ['identifier-heuristic', 'identifier'], 'custom ID-cookie heuristics': ['identifier-heuristic', 'identifier'], 'custom ID-cookie criteria': ['identifier-heuristic', 'identifier'], 'custom ID-detection algorithm': ['identifier-heuristic', 'identifier'], 'custom identifier-cookie detection algorithm': ['identifier-heuristic', 'identifier'], 'custom identifier-detection heuristic': ['identifier-heuristic', 'identifier'], 'custom identifier detection pipeline': ['identifier-heuristic', 'identifier'], 'custom unique-ID parser': ['identifier-heuristic', 'identifier'], 'unique-ID cookie heuristics (custom)': ['identifier-heuristic', 'identifier'], 'custom tracking-ID detection algorithm': ['identifier-heuristic', 'identifier'], 'custom persistent-identifier detection rules': ['identifier-heuristic', 'identifier'], 'potentially identifying cookie flow heuristic (custom)': ['identifier-heuristic', 'identifier'], 'BehaviorTrack': ['identifier-heuristic', 'identifier'], 'custom personal-information detector': ['own-rule', 'other'], 'custom regular expressions': ['own-rule', 'identifier'], 'matching patterns (custom)': ['own-rule', 'other'], 'custom affiliate-cookie parser (AffTracker)': ['own-rule', 'other'], 'heuristic proposed in previous work': ['identifier-heuristic', 'identifier'], // --- syncing detectors that the extractor filed as cookie classification. // They decide "did this value travel", which is the cookie_syncing page's // question, not this page's. Kept visible rather than dropped. 'custom cookie-synchronization rules': ['own-rule', 'other'], 'custom Cookie Synchronization heuristics': ['own-rule', 'other'], 'cookie-syncing heuristic (custom)': ['own-rule', 'other'], 'cookie-syncing script (adapted)': ['own-rule', 'other'], 'custom cross-domain attribution and exfiltration pipeline': ['own-rule', 'other'], // --- party / scope 'Public Suffix List': ['party-rule', 'party'], 'custom host-domain comparison': ['party-rule', 'party'], // --- security role and attribute correctness 'custom Zan credential-cookie classifier': ['own-rule', 'security'], 'hand-coded rules for SessionShield, Serene, CookiExt, and Zan': ['own-rule', 'security'], 'random session-cookie heuristic': ['own-rule', 'security'], 'custom cookie-scope checks': ['own-rule', 'security'], 'custom cookie inconsistency checks': ['own-rule', 'security'], // --- other, each named so it is visible rather than absorbed 'custom': ['own-rule', 'other'], // lifetime buckets 'custom cookie classification algorithm': ['own-rule', 'tracking'], 'custom intractable-cookie definition': ['own-rule', 'other'], 'custom segmentation rules': ['own-rule', 'other'], // input to the LLM 'ConsentChk rule-based violation classifier': ['own-rule', 'other'], }; const FAMILY_LABEL = { 'purpose-database': 'Cookie purpose database (Cookiepedia, Cookie-Script, cookiedatabase.org)', 'published-classifier': 'A published cookie classifier, reused off the shelf', 'own-supervised-ml': 'Own supervised model trained for the paper', llm: 'Large language model', manual: 'Hand labelling by the authors', 'filter-list': 'Filter list / tracker database (provenance of the setter)', 'opt-out-registry': 'Opt-out tool registry', 'identifier-heuristic': 'Identifier heuristic over the cookie value', 'party-rule': 'First/third-party rule over the domain', 'own-rule': 'Other hand-written rule', }; const QUESTION_LABEL = { purpose: 'Purpose: which category (necessary / functional / analytics / advertising)', tracking: 'Tracking: does this cookie belong to a tracker', identifier: 'Identifier: does the value carry a per-user ID', party: 'Party: first or third party', security: 'Security role: authentication or session cookie, attribute correctness', other: 'Something else (named in the residue listing)', }; // --------------------------------------------------------------------------- // PASS A — populations // --------------------------------------------------------------------------- const cookieTuples = (p) => (p.classification ?? []).filter((c) => c.target === 'cookie'); const CLASSIFY = rows.filter((p) => cookieTuples(p).length > 0); // The wider set: anything whose detection[].phenomenon names a cookie. Three of // those are not HTTP cookies at all, and are excluded by name rather than by a // regex, so the exclusion is arguable line by line. const NOT_A_WEB_COOKIE = new Set([ 'DNS cookie usage', 'SYN-cookie feedback', 'Bloom-cookie communication overhead', ]); const detCookie = (p) => (p.detection ?? []).filter( (d) => /cookie/i.test(d.phenomenon) && !NOT_A_WEB_COOKIE.has(d.phenomenon) ); const MEASURES = rows.filter((p) => detCookie(p).length > 0 || cookieTuples(p).length > 0); const CRAWLED = rows.filter( (p) => p.crawlConfig !== null || p.studyTypes.includes('automated-web-crawl') ); const CLASSIFIED = rows.filter((p) => (p.classification ?? []).length > 0); h('1. POPULATIONS'); T( ['Population', 'Papers', 'Share of 5,859'], [ ['corpus (7 venues, 2010-2026)', rows.length, '100%'], ['classified or labelled something', CLASSIFIED.length, pct(CLASSIFIED.length, rows.length)], ['ran an automated web crawl', CRAWLED.length, pct(CRAWLED.length, rows.length)], ['>=1 classification tuple with target == "cookie" <-- the page population', CLASSIFY.length, pct(CLASSIFY.length, rows.length)], ['measures something about cookies (classify OR a cookie detection tuple)', MEASURES.length, pct(MEASURES.length, rows.length)], ] ); console.log(` The page's headline denominator is ${CLASSIFY.length}, NOT 5,859 and NOT 1,120. ${CLASSIFY.length} of ${CLASSIFIED.length} papers that classified anything = ${pct(CLASSIFY.length, CLASSIFIED.length)}. ${CLASSIFY.filter((p) => CRAWLED.includes(p)).length} of the ${CLASSIFY.length} ran a crawl. Excluded by name from the wider set (not HTTP cookies): ${[...NOT_A_WEB_COOKIE].join('; ')}`); h('1.1 THE ' + CLASSIFY.length + ' PAPERS, BY YEAR AND VENUE'); const years = [...new Set(rows.map((p) => p.year))].sort(); T( ['Year', 'Cookie-classifying papers', 'Papers classifying anything', 'Share', 'Corpus that year'], years.map((y) => { const c = CLASSIFY.filter((p) => p.year === y).length; const d = CLASSIFIED.filter((p) => p.year === y).length; return [y + (y >= 2025 ? '*' : ''), c, d, pct(c, d), rows.filter((p) => p.year === y).length]; }) ); console.log('* 2025-2026 are provisional venue-years: CCS 2026 and IMC 2026 have not been held,\n and IEEE S&P / WWW 2026 are incompletely selected. Those rows are floors.'); const venues = [...new Set(rows.map((p) => p.venue))].sort(); T( ['Venue', 'Cookie-classifying papers', 'Papers in venue', 'Share of venue'], venues .map((v) => { const c = CLASSIFY.filter((p) => p.venue === v).length; return [v, c, rows.filter((p) => p.venue === v).length, pct(c, rows.filter((p) => p.venue === v).length)]; }) .sort((a, b) => b[1] - a[1]) ); h('1.2 THE ' + CLASSIFY.length + ' PAPERS, LISTED'); for (const p of [...CLASSIFY].sort((a, b) => a.year - b.year || a.venue.localeCompare(b.venue))) { console.log(` ${p.year} ${p.venue.padEnd(8)} ${p.slug}`); } // --------------------------------------------------------------------------- h('2. PASS B — FOLD OF resourceName, WITH ITS RESIDUE'); // --------------------------------------------------------------------------- const allTuples = []; const unmapped = new Set(); for (const p of CLASSIFY) { for (const c of cookieTuples(p)) { const m = RESOURCE[c.resourceName]; if (!m) unmapped.add(`${key(p)} :: ${c.resourceName}`); else allTuples.push({ p, c, family: m[0], question: m[1] }); } } if (unmapped.size) { console.error(`\nUNMAPPED resourceName values (${unmapped.size}):`); for (const u of unmapped) console.error(' ' + u); throw new Error( `${unmapped.size} cookie classification tuple(s) have a resourceName not in the ` + `hand-keyed RESOURCE map. Add them rather than letting the paper fall out of every family.` ); } const distinct = new Set(allTuples.map((t) => t.c.resourceName)); // Computed, not counted by hand: the page states this figure and a hand count // of it was wrong by four on the first attempt. const customPrefixed = [...distinct].filter((n) => /^custom\b/i.test(n)); const customAnywhere = [...distinct].filter((n) => /custom/i.test(n)); console.log( `\n${allTuples.length} cookie classification tuples across ${CLASSIFY.length} papers, ` + `${distinct.size} distinct resourceName strings, all mapped (residue 0 by construction —\n` + `the script throws instead of silently dropping one). The listing in section 2.2 is what to\n` + `check: a zero residue means the map is complete, not that every judgement in it is right.\n\n` + `Of the ${distinct.size} distinct strings, ${customPrefixed.length} BEGIN with the word "custom" and ` + `${customAnywhere.length} contain it anywhere.\nThat is the reusability finding: these are one-off ` + `predicates written for one paper.` ); const byPaper = (pred) => new Set(allTuples.filter(pred).map((t) => key(t.p))).size; h('2.1 LABEL-SOURCE FAMILY (papers, of ' + CLASSIFY.length + ' that classify a cookie)'); const fams = [...new Set(allTuples.map((t) => t.family))]; T( ['Family', 'Papers', 'Share of ' + CLASSIFY.length, 'Tuples'], fams .map((f) => [ FAMILY_LABEL[f], byPaper((t) => t.family === f), pct(byPaper((t) => t.family === f), CLASSIFY.length), allTuples.filter((t) => t.family === f).length, ]) .sort((a, b) => b[1] - a[1]) ); console.log('Multi-valued: a paper using Cookiepedia AND its own heuristic is in both rows.'); h('2.1b THE QUESTION BEING ANSWERED (papers, of ' + CLASSIFY.length + ')'); const qs = ['purpose', 'tracking', 'identifier', 'party', 'security', 'other']; T( ['Question', 'Papers', 'Share of ' + CLASSIFY.length, 'Tuples'], qs.map((q) => [ QUESTION_LABEL[q], byPaper((t) => t.question === q), pct(byPaper((t) => t.question === q), CLASSIFY.length), allTuples.filter((t) => t.question === q).length, ]) ); h('2.1c NAMED LABEL SOURCES, PAPER-COUNTED — the figures the prose quotes'); // Every named (non-"custom") label source with its distinct-paper count, so no // sentence on the page has to hand-count one. Two hand counts were wrong on the // first attempt: Cookiepedia (12, really 14) and Cookie-Script (4, really 3 — // one paper contributes two tuples). const NAMED = /^(Cookiepedia|Cookie[- ]?Script|Cookiedatabase\.org|Cookie Database|CookieBlock|CookieGraph|WebGraph|EasyList|EasyList\/EasyPrivacy|justdomains|justdomains blocklist|Ghostery|Ghostery and Disconnect intersection|Disconnect\.me|zxcvbn|Public Suffix List|GPT-3\.5|GPT-3\.5-turbo \(custom fine-tuned model\))$/; const namedRows = new Map(); for (const t of allTuples) { if (!NAMED.test(t.c.resourceName)) continue; if (!namedRows.has(t.c.resourceName)) namedRows.set(t.c.resourceName, new Set()); namedRows.get(t.c.resourceName).add(key(t.p)); } T( ['resourceName (exact string)', 'Papers', 'Tuples', 'Family', 'Question'], [...namedRows.entries()] .map(([n, set]) => { const ts = allTuples.filter((t) => t.c.resourceName === n); return [n, set.size, ts.length, ts[0].family, ts[0].question]; }) .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])) ); // The two spellings of Cookie-Script and of justdomains are one product each. const merge = (re) => new Set(allTuples.filter((t) => re.test(t.c.resourceName)).map((t) => key(t.p))).size; console.log(` Merged across spellings (papers, not tuples): Cookiepedia ${merge(/^Cookiepedia$/)} Cookie-Script / "Cookie Script" ${merge(/^Cookie[- ]?Script$/i)} cookiedatabase.org ${merge(/^(Cookiedatabase\.org|Cookie Database)$/i)} CookieBlock ${merge(/^CookieBlock$/)} any purpose database ${merge(/^(Cookiepedia|Cookie[- ]?Script|Cookiedatabase\.org|Cookie Database)$/i)} justdomains (both spellings) ${merge(/^justdomains( blocklist)?$/i)}`); h('2.2 EVERY TUPLE, GROUPED BY FAMILY — the listing to argue with'); for (const f of fams.sort()) { console.log(`\n--- ${f}: ${FAMILY_LABEL[f]}`); for (const t of allTuples .filter((x) => x.family === f) .sort((a, b) => a.p.year - b.p.year)) { console.log( ` ${t.p.year} ${t.p.venue.padEnd(8)} q=${t.question.padEnd(10)} ` + `m=${t.c.method.padEnd(18)} ${t.c.usedOrMentioned.padEnd(9)} "${t.c.resourceName}"` ); } } // --------------------------------------------------------------------------- h('3. DATING THE METHODS — label-source family by year'); // --------------------------------------------------------------------------- // This is the table the page's currency claims rest on. Papers, per year, per // family. `first`/`last` are the first and last year the family appears at all. const famYear = fams.map((f) => { const ps = allTuples.filter((t) => t.family === f); const ys = [...new Set(ps.map((t) => t.p.year))].sort(); return { f, ys, n: byPaper((t) => t.family === f) }; }); T( ['Family', 'Papers', 'First', 'Most recent', 'Years it appears in'], famYear .map((r) => [FAMILY_LABEL[r.f], r.n, r.ys[0], r.ys[r.ys.length - 1], r.ys.join(' ')]) .sort((a, b) => b[1] - a[1]) ); h('3.1 THE SAME SPLIT IN FOUR-YEAR BUCKETS'); const BUCKETS = [ ['2010-2015', (y) => y <= 2015], ['2016-2019', (y) => y >= 2016 && y <= 2019], ['2020-2022', (y) => y >= 2020 && y <= 2022], ['2023-2024', (y) => y >= 2023 && y <= 2024], ['2025-2026*', (y) => y >= 2025], ]; T( ['Family', ...BUCKETS.map((b) => b[0])], fams .map((f) => [ FAMILY_LABEL[f], ...BUCKETS.map(([, pred]) => byPaper((t) => t.family === f && pred(t.p.year))), ]) .sort((a, b) => b.slice(1).reduce((x, y) => x + y, 0) - a.slice(1).reduce((x, y) => x + y, 0)) ); T( ['(denominator) cookie-classifying papers in bucket', ...BUCKETS.map(([, pred]) => CLASSIFY.filter((p) => pred(p.year)).length)], [] ); // --------------------------------------------------------------------------- h('4. THE PURPOSE-CLASSIFICATION SUBSET, PAPER BY PAPER'); // --------------------------------------------------------------------------- const PURPOSE = [...new Set(allTuples.filter((t) => t.question === 'purpose').map((t) => key(t.p)))]; console.log(`${PURPOSE.length} papers answer the purpose question, of ${CLASSIFY.length} that classify a cookie.\n`); for (const k of PURPOSE.sort()) { const ts = allTuples.filter((t) => key(t.p) === k); const p = ts[0].p; const srcs = ts.filter((t) => t.question === 'purpose').map((t) => `${t.c.resourceName} [${t.c.usedOrMentioned}]`); console.log(` ${p.year} ${p.venue.padEnd(8)} ${p.slug}\n ${srcs.join('; ')}`); } // --------------------------------------------------------------------------- h('5. TAXONOMY — which category scheme, folded'); // --------------------------------------------------------------------------- // `classification.taxonomy` is free text and nullable. The denominator is the // papers that NAME a scheme on a cookie tuple, never all 53: reporting // "ICC UK: 12 of 53" would count the papers that named nothing as having named // something else. const TAX_FOLD = [ [/ICC|International Chamber of Commerce|strictly necessary|Strictly Necessary|necessary, functional, analytics|functionality, performance/i, 'ICC (UK) four purposes, or a restatement of them'], [/GDPR-defined purposes|necessary, preference, statistics/i, 'Four GDPR-framed purposes (necessary / preference / statistics / marketing)'], [/Cookiepedia (categories|purposes)/i, 'Cookiepedia categories (= the ICC four plus "unknown")'], [/identifier|ID cook|tracking ID|high-entropy/i, 'Identifier vs non-identifier'], [/first-party versus third-party|first party|third-party/i, 'First vs third party'], [/HttpOnly|Secure|SameSite/i, 'Security attributes'], [/lifetime|Session, Short, Persistent/i, 'Lifetime buckets'], [/ATS versus Non-ATS|advertising-related|Advertising or Analytics|advertising-tracker/i, 'Advertising-and-tracking vs not'], ]; const taxTuples = allTuples.filter((t) => !isSentinel(t.c.taxonomy)); const taxPapers = new Set(taxTuples.map((t) => key(t.p))); const taxCounts = new Map(); const taxResidue = []; for (const t of taxTuples) { const m = TAX_FOLD.find(([re]) => re.test(t.c.taxonomy)); if (!m) { taxResidue.push(`${t.p.year} ${t.p.venue} :: ${t.c.taxonomy}`); continue; } if (!taxCounts.has(m[1])) taxCounts.set(m[1], new Set()); taxCounts.get(m[1]).add(key(t.p)); } console.log( `${taxPapers.size} of ${CLASSIFY.length} papers name a category scheme on at least one cookie tuple ` + `(${pct(taxPapers.size, CLASSIFY.length)}).\nThe denominator below is ${taxPapers.size}, not ${CLASSIFY.length}.\n` ); T( ['Scheme (folded)', 'Papers', 'Share of ' + taxPapers.size], [...taxCounts.entries()] .map(([k, v]) => [k, v.size, pct(v.size, taxPapers.size)]) .sort((a, b) => b[1] - a[1]) ); console.log(`\nUNMAPPED RESIDUE (${taxResidue.length} tuples) — printed in full:`); for (const r of taxResidue) console.log(' ' + r); console.log(`\nStrict count, exact string naming the ICC or its author: ` + `${new Set(taxTuples.filter((t) => /ICC|International Chamber of Commerce/i.test(t.c.taxonomy)).map((t) => key(t.p))).size} papers.` + `\nFolded count including the restatements: ${(taxCounts.get('ICC (UK) four purposes, or a restatement of them') ?? new Set()).size} papers.` + `\nBoth are printed because the merge is a judgement. Every contributing string:`); for (const t of taxTuples.filter((t) => /ICC|International Chamber of Commerce|strictly necessary|Strictly Necessary|necessary, functional, analytics|functionality, performance/i.test(t.c.taxonomy))) console.log(` ${t.p.year} ${t.p.venue.padEnd(8)} "${t.c.taxonomy}"`); // --------------------------------------------------------------------------- h('6. GROUND TRUTH AND VALIDATION — where the field goes quiet'); // --------------------------------------------------------------------------- const statedGT = new Set(allTuples.filter((t) => !isSentinel(t.c.groundTruthSource)).map((t) => key(t.p))); const statedVal = new Set( allTuples.filter((t) => !isSentinel(t.c.validation) && t.c.validation !== 'none-reported').map((t) => key(t.p)) ); // Corpus comparison, same fields, over every paper that classified anything. const allClsTuples = CLASSIFIED.flatMap((p) => (p.classification ?? []).map((c) => ({ p, c }))); const cGT = new Set(allClsTuples.filter((t) => !isSentinel(t.c.groundTruthSource)).map((t) => key(t.p))); const cVal = new Set( allClsTuples.filter((t) => !isSentinel(t.c.validation) && t.c.validation !== 'none-reported').map((t) => key(t.p)) ); T( ['Field', 'Cookie papers stating it', 'of ' + CLASSIFY.length, 'Whole classified corpus', 'of ' + CLASSIFIED.length], [ ['names a ground-truth source', statedGT.size, pct(statedGT.size, CLASSIFY.length), cGT.size, pct(cGT.size, CLASSIFIED.length)], ['reports any validation (not none-reported / not-applicable)', statedVal.size, pct(statedVal.size, CLASSIFY.length), cVal.size, pct(cVal.size, CLASSIFIED.length)], ] ); h('6.1 VALIDATION, PER VALUE (cookie tuples; enum, so publishable as a percentage)'); const vals = [...new Set(allTuples.map((t) => t.c.validation))]; T( ['validation', 'Papers', 'Share of ' + CLASSIFY.length], vals .map((v) => [v, byPaper((t) => t.c.validation === v), pct(byPaper((t) => t.c.validation === v), CLASSIFY.length)]) .sort((a, b) => b[1] - a[1]) ); h('6.2 METHOD ENUM, PER VALUE (cookie tuples), against the classified corpus'); const methods = [...new Set(allClsTuples.map((t) => t.c.method))]; T( ['classification.method', 'Cookie papers', 'of ' + CLASSIFY.length, 'Corpus papers', 'of ' + CLASSIFIED.length], methods .map((m) => { const a = byPaper((t) => t.c.method === m); const b = new Set(allClsTuples.filter((t) => t.c.method === m).map((t) => key(t.p))).size; return [m, a, pct(a, CLASSIFY.length), b, pct(b, CLASSIFIED.length)]; }) .sort((a, b) => b[1] - a[1]) ); // --------------------------------------------------------------------------- h('7. LLM CLASSIFICATION OF COOKIES — the currency check'); // --------------------------------------------------------------------------- // Cross-checks scripts/report_llm_currency.mjs, which computes the same figure // for every target and backs design:website_classification, privacy:javascript // and design:ip_classification. If these two disagree, one of them is wrong and // two published pages contradict each other. // // That script's per-target share is USED/USED: numerator = papers that used or // produced LLM labels at that target, denominator = papers that used or // produced ANY classifier at that target. `compared` and `mentioned` tuples are // a baseline the authors argued against; counting them as adoption overstates // it, and counting them in the denominator but not the numerator understates // the share. This script reproduces that definition EXACTLY so the pages cannot // drift, and prints the naive any/any variant beside it so the difference is // visible rather than hidden. The rows where they disagree are listed by the // script itself under the table rather than being asserted in this comment, // because a comment does not get re-derived when the corpus moves. The cookie // row is 1/53 under both definitions. const USED_OM = new Set(['used', 'produced']); const setAdd = (m, k, v) => { if (!m.has(k)) m.set(k, new Set()); m.get(k).add(v); }; const llmUsedByTarget = new Map(); const llmAnyByTarget = new Map(); const usedByTarget = new Map(); const anyByTarget = new Map(); for (const p of CLASSIFIED) { for (const c of p.classification) { if (isSentinel(c.target)) continue; setAdd(anyByTarget, c.target, key(p)); if (c.method === 'llm') setAdd(llmAnyByTarget, c.target, key(p)); if (USED_OM.has(c.usedOrMentioned)) { setAdd(usedByTarget, c.target, key(p)); if (c.method === 'llm') setAdd(llmUsedByTarget, c.target, key(p)); } } } const sz = (m, t) => (m.get(t) ?? new Set()).size; T( [ 'Classification target', 'Papers classifying it (used/produced)', '...of which an LLM', 'LLM share <-- PUBLISHED', '(any/any numerator)', '(any/any denominator)', '(any/any share)', ], [...anyByTarget.keys()] .map((t) => [ t, sz(usedByTarget, t), sz(llmUsedByTarget, t), pct(sz(llmUsedByTarget, t), sz(usedByTarget, t)), sz(llmAnyByTarget, t), sz(anyByTarget, t), pct(sz(llmAnyByTarget, t), sz(anyByTarget, t)), ]) .sort((a, b) => parseFloat(b[3]) - parseFloat(a[3]) || b[1] - a[1]) ); console.log(` The PUBLISHED column is columns 2-4 and matches report_llm_currency.mjs row for row. Rows where the two definitions disagree:`); for (const t of anyByTarget.keys()) { const a = pct(sz(llmUsedByTarget, t), sz(usedByTarget, t)); const b = pct(sz(llmAnyByTarget, t), sz(anyByTarget, t)); if (a !== b) console.log(` ${t}: used/used ${sz(llmUsedByTarget, t)}/${sz(usedByTarget, t)} = ${a} any/any ${sz(llmAnyByTarget, t)}/${sz(anyByTarget, t)} = ${b}`); } console.log('\n The cookie row is the one privacy:cookies and design:website_classification must agree on.'); for (const k of llmAnyByTarget.get('cookie') ?? []) console.log(' LLM + cookie: ' + k); // --------------------------------------------------------------------------- h('8. HOW THE COOKIE PAPERS CRAWL'); // --------------------------------------------------------------------------- const cc = (set) => set.filter((p) => p.crawlConfig !== null); const ccCookie = cc(CLASSIFY); const ccAll = cc(CRAWLED); const ccField = (set, f) => { const stated = set.filter((p) => !isSentinel(p.crawlConfig[f])); return [stated.length, pct(stated.length, set.length)]; }; console.log(`${ccCookie.length} of the ${CLASSIFY.length} cookie papers have a crawlConfig object; ${ccAll.length} of the ${CRAWLED.length} crawling papers do.\n`); T( ['crawlConfig field', 'Cookie papers stating it', 'of ' + ccCookie.length, 'All crawling papers', 'of ' + ccAll.length], ['consentAction', 'statefulness', 'interactionDepth', 'browsers', 'headless', 'authentication'].map((f) => { const a = f === 'browsers' ? [ccCookie.filter((p) => (p.crawlConfig.browsers ?? []).some((b) => !isSentinel(b))).length] : ccField(ccCookie, f); const b = f === 'browsers' ? [ccAll.filter((p) => (p.crawlConfig.browsers ?? []).some((x) => !isSentinel(x))).length] : ccField(ccAll, f); return [f, a[0], pct(a[0], ccCookie.length), b[0], pct(b[0], ccAll.length)]; }) ); h('8.1 consentAction, per value (cookie papers with a crawlConfig)'); const caVals = [...new Set(ccCookie.map((p) => p.crawlConfig.consentAction))]; T( ['consentAction', 'Papers', 'Share of ' + ccCookie.length], caVals.map((v) => { const n = ccCookie.filter((p) => p.crawlConfig.consentAction === v).length; return [v + (isSentinel(v) ? ' (sentinel — not an answer)' : ''), n, pct(n, ccCookie.length)]; }).sort((a, b) => b[1] - a[1]) ); // --------------------------------------------------------------------------- h('9. PASS C — full-text probes for what the schema cannot see'); // --------------------------------------------------------------------------- const files = []; for (const y of fs.readdirSync(FT).sort()) { const yd = path.join(FT, y); if (!fs.statSync(yd).isDirectory()) continue; for (const v of fs.readdirSync(yd).sort()) { const vd = path.join(yd, v); if (!fs.statSync(vd).isDirectory()) continue; for (const s of fs.readdirSync(vd).sort()) { const f = path.join(vd, s, 'paper.cols.txt'); if (fs.existsSync(f)) files.push({ year: +y, venue: v, slug: s, f }); } } } console.log(`readable paper.cols.txt files: ${files.length} <-- denominator for every row below`); // A generic word is only counted within 150 characters of "cookie": a bare // /entropy/ matches password and DNS papers, and reporting it unqualified // would make entropy look like a mainstream cookie method. const nearCookie = (t, re, w = 150) => { for (const m of t.matchAll(re)) { const c = t.slice(Math.max(0, m.index - w), m.index + w); if (/cookie/i.test(c)) return c; } return null; }; // The figures the page publishes as findings, each with the tuple it comes from // (used by section 11). Declared here because the streaming probe below keeps // only these papers' text in memory. const CHECK = [ ['CCS/2023/cookiegraph-understanding-and-detecting-first-party-tracking-cookies', 'First-party tracking cookies'], ['CCS/2023/cookiegraph-understanding-and-detecting-first-party-tracking-cookies', 'Third-party ghostwriting of first-party cookies'], ['CCS/2023/cookiegraph-understanding-and-detecting-first-party-tracking-cookies', 'CookieGraph classification'], ['USENIX/2022/automating-cookie-consent-and-gdpr-violation-detection', 'Incorrect cookie purposes'], ['USENIX/2022/automating-cookie-consent-and-gdpr-violation-detection', 'Unclassified or undeclared cookies'], ['USENIX/2022/automating-cookie-consent-and-gdpr-violation-detection', 'Cookie-purpose majority outliers'], ['WWW/2025/semantics-aware-cookie-purpose-compliance', 'cookie-purpose classification'], ['WWW/2025/semantics-aware-cookie-purpose-compliance', 'potential cookie purpose violation'], ['WWW/2025/semantics-aware-cookie-purpose-compliance', 'missing cookie declarations'], ['IMC/2024/browsing-without-third-party-cookies-what-do-you-see', 'Cookie-notice tracking-cookie violations'], ['PETS/2024/a-large-scale-study-of-cookie-banner-interaction-tools-and-their-impact-on-users', 'Cookie-purpose categories'], ['USENIX/2024/automated-large-scale-analysis-of-cookie-notice-compliance', 'Undeclared cookie purposes'], ['USENIX/2025/navigating-cookie-consent-violations-across-the-globe', 'wrong cookie category'], ['USENIX/2025/navigating-cookie-consent-violations-across-the-globe', 'personal information in cookies'], ['PETS/2025/intractable-cookie-crumbs-unveiling-the-nexus-of-stateful-banner-interaction-and', 'partitioned-cookie adoption'], ['WWW/2025/least-privilege-access-for-persistent-storage-mechanisms-in-web-browsers', 'Third-party cookie accesses'], ['PETS/2024/crumbling-cookie-categories-deconstructing-common-cookie-categories-to-create-ca', 'Cookie-category comprehension'], ['IEEE-SP/2022/journey-to-the-center-of-the-cookie-ecosystem-unraveling-actors-roles-and-relati', 'identifier cookies'], ]; const QUOTE_CHECK_KEYS = new Set(CHECK.map(([k]) => k)); const PROBES = { 'Cookiepedia (name)': [/cookiepedia/gi, false], 'CookieBlock (name)': [/cookieblock/gi, false], 'CookieGraph (name)': [/cookiegraph/gi, false], '"Cookie Script" / Cookie-Script': [/cookie[\s-]?script/gi, false], 'cookiedatabase.org': [/cookiedatabase\.org/gi, false], 'Open Cookie Database': [/open[\s-]?cookie[\s-]?database/gi, false], 'cookiesearch.org': [/cookiesearch/gi, false], 'ICC / International Chamber of Commerce': [/international chamber of commerce/gi, false], 'zxcvbn, within 150 chars of "cookie"': [/zxcvbn/gi, true], 'entropy, within 150 chars of "cookie"': [/\bentrop/gi, true], 'an LLM name, within 150 chars of "cookie"': [/\b(?:GPT-[0-9o]|ChatGPT|LLaMA|Llama|Gemini|Claude-|large language model)/g, true], }; // Streamed: 5,869 full texts do not fit in memory at once. Only the papers the // quote check needs are retained (`texts`), the rest are read, probed, dropped. const probeHits = Object.fromEntries(Object.keys(PROBES).map((k) => [k, []])); const texts = new Map(); for (const it of files) { const k = `${it.venue}/${it.year}/${it.slug}`; const t = fs.readFileSync(it.f, 'utf8').replace(/-\n/g, '').replace(/\s+/g, ' '); for (const [name, [re, needNear]] of Object.entries(PROBES)) { re.lastIndex = 0; const hit = needNear ? nearCookie(t, re) : (re.test(t) ? '(name match)' : null); if (hit) probeHits[name].push([k, hit]); } if (QUOTE_CHECK_KEYS.has(k)) texts.set(k, t); } T( ['Probe', 'Papers matching', 'Share of ' + files.length], Object.entries(probeHits).map(([n, v]) => [n, v.length, pct(v.length, files.length)]) ); console.log(` A name match is a MENTION, not a use: it counts reference-list entries and related-work sentences. It is never added to a PASS A count. The rows worth reading individually are the small ones:`); for (const n of ['zxcvbn, within 150 chars of "cookie"', 'an LLM name, within 150 chars of "cookie"', '"Cookie Script" / Cookie-Script', 'cookiedatabase.org', 'Open Cookie Database', 'cookiesearch.org']) { console.log(`\n--- ${n} (${probeHits[n].length})`); for (const [k, c] of probeHits[n]) console.log(` ${k}\n ...${c.slice(0, 220)}...`); } // --------------------------------------------------------------------------- h('10. MEASURED RESULTS — detection[].prevalence on cookie phenomena'); // --------------------------------------------------------------------------- const prevRows = []; for (const p of MEASURES) { for (const d of detCookie(p)) { if (d.prevalence === null) continue; prevRows.push({ p, d }); } } console.log( `${prevRows.length} prevalence strings across ` + `${new Set(prevRows.map((r) => key(r.p))).size} of the ${MEASURES.length} cookie-measuring papers.\n` + `metric/prevalence names the unit, often not the population: every figure the page\n` + `publishes has had its denominator read out of the paper by hand (section 11).\n` ); for (const r of prevRows.sort((a, b) => a.p.year - b.p.year)) { console.log(` ${r.p.year} ${r.p.venue.padEnd(8)} ${r.d.phenomenon}\n ${r.d.prevalence}`); } // --------------------------------------------------------------------------- h('11. QUOTE SPOT-CHECK — every figure the page states, against paper.cols.txt'); // --------------------------------------------------------------------------- // The figures the page publishes as findings, each with the tuple it comes from. // Matching is whitespace- and case-insensitive, falling back to the longest run // of 8 consecutive words — the grading the dataset itself uses. const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim(); let exact = 0, run8 = 0, missing = 0; for (const [k, phen] of CHECK) { const p = rows.find((r) => key(r) === k); if (!p) { console.log(` MISSING PAPER ${k}`); missing++; continue; } const d = (p.detection ?? []).find((x) => x.phenomenon === phen); if (!d) { console.log(` MISSING TUPLE ${k} :: ${phen}`); missing++; continue; } const text = texts.get(k); if (!text) { console.log(` NO FULLTEXT ${k}`); missing++; continue; } const q = norm(d.evidence.quote); const t = norm(text); let verdict; if (t.includes(q)) { verdict = 'EXACT'; exact++; } else { const w = q.split(' '); let best = 0; for (let i = 0; i + 8 <= w.length; i++) if (t.includes(w.slice(i, i + 8).join(' '))) best = 8; if (best === 8) { verdict = '8-word run'; run8++; } else { verdict = 'NOT FOUND'; missing++; } } console.log(` ${verdict.padEnd(11)} ${p.year} ${p.venue} :: ${phen}\n prevalence: ${d.prevalence}\n quote: ${d.evidence.quote.slice(0, 200)}`); } console.log(`\n ${CHECK.length} figures checked: ${exact} exact, ${run8} via an 8-word run, ${missing} not found.`); h('DONE');
12. Review log
Four passes were planned: three focused (Sonnet) and one generic (Fable). The figures-vs-script pass returned and was acted on. The citations-and-quotes, external-currency and generic passes were still running when this page was saved; their findings are not yet reflected here. That is recorded rather than hidden: treat §5 and §7 as verified by the author only, and re-read this section before trusting the review coverage.
Self-audit, before any reviewer returned
Found by re-deriving figures the prose had hand-counted. All were live on the page for roughly one hour and are now fixed. Every one is a case of the same mistake — a number in the prose that the report script did not produce — so the script was extended to compute each of them (§2.1c and §2 of its output).
| Figure | Published | Correct | How it was caught |
|---|---|---|---|
Distinct resourceName strings beginning “custom” | 31 | 27 (34 contain it) | Recounted with grep -c '^custom' |
| Papers naming Cookiepedia | 12 | 14 | Paper-counted with jq |
| Papers naming Cookie-Script | 4 | 3 | Same; one paper contributes two tuples |
| Papers reporting more than one consent state | “at most 9 of 49” | 7 of 49 state accept-and-reject; 3 more state a single interaction | reject-all and accept-all are single states, so 7+2+1 is the wrong sum |
| Cookie papers vs field on crawl reporting | “two to three times” | 2.0x on consent action, 3.5x on statefulness | Divided the two columns |
| PoPETs vs IEEE S&P publication rate | “eight times” | more than ten times, on n=2 | Divided the two shares |
LLM share for user-generated-text | 7.6% | 7.2% | See below |
| “Nobody has trained a new cookie purpose model since 2023” | — | false | [5Chen, Baiqi; Lyu, Jiawei; Wu, Tingmin; Chhetri, Mohan Baruwal; Bai, Guangdong (2025): "Semantics-Aware Cookie Purpose Compliance", in: Proceedings of the ACM Web Conference. (DOI)] fine-tuned one in 2025; the llm family is separate from own-supervised-ml in the fold, and the prose read the latter as if it covered both |
The LLM one is the interesting failure. The page's per-target table is
supposed to be the same table as which_task_though,
“generated once so the pages cannot drift”. This script re-derived it
independently and got 32/419 = 7.6% for user-generated-text where the
published cell says 7.2%. Neither was stale: report_llm_currency.mjs
computes the share as used/used — numerator and denominator both restricted
to usedOrMentioned in {used, produced} — while the re-derivation used
any/any. The two definitions differ for exactly two targets
(user-generated-text 30/419 vs 32/419, other 115/2592 vs 116/2594) and
agree everywhere else, including the cookie row, which is why it nearly went
unnoticed. report_cookies.mjs §7 now reproduces the used/used definition
exactly, prints the any/any variant beside it, and prints the rows where the two
disagree. Sharing a script is a claim about provenance, not about definition.
Also caught: the new citekey silently did not render. See §10.
Pass 1 — figures against the script (Sonnet)
Given the page text, both scripts, both committed outputs, the provenance draft and the dataset; asked to re-run everything and hunt for stale or mis-scoped numbers.
- Re-ran both scripts and diffed against the committed outputs: byte-identical. Also ran the
curl | python3one-liner the page publishes and reproduced 39.3% / 35.1% / 25.6%. - Accepted — “31 of 61 begin with custom” is 27. Independently recomputed, and correctly noted that the script did not print the figure at all, which is why it was never checked. Already fixed by the self-audit; the script now prints it.
- Accepted — the span arithmetic in §10 was 128/64 and the live page has 130 spans. Its diagnosis is better than the self-audit's: the plugin emits two spans per key instance, not per marker, and three markers on this page are multi-key. §10 now states 62 markers / 65 instances / 130 spans.
- Rejected — “at most 9 of 49 is correct, being 7 accept-and-reject plus 2 reject-all”. A paper that rejects on every visit measured one consent state, not more than one. The sentence was already being corrected to “7 state accept-and-reject, 3 more state a single interaction”, which is what the enum supports; the reviewer's arithmetic reproduces the wrong claim rather than testing it.
- Noted, no change — the CookieGraph 89.86%. It flagged that the figure is over a 20% sample of the top million rather than the whole top million, then concluded the page's wording matches the paper's own abstract. Agreed: the page says “top-million”, which is the paper's framing, and the provenance page records the distinction from the 20K stratified crawl.
- Everything else it checked — every cell of the methods, questions, categories, population, venue, year, coverage, quiet, crawl-config, consentAction and LLM tables, the 82% / 16pp / 38pp / “8 of the 10” arithmetic, sentinel handling, paper-vs-tuple counting, and the fold's throw-on-unmapped behaviour — reproduced exactly. It also independently re-verified six external sources.
Passes 2, 3 and 4 — not yet returned
Citations and quotes (Sonnet), external currency (Sonnet) and the generic pass (Fable) were still running at save time. This section is incomplete and the page should be re-reviewed. The specific things they were asked to test and that therefore remain unconfirmed by a second reader:
- every
[key]resolving, and every quoted figure appearing verbatim in the cited paper (the author checked 18 automatically and 4 by hand — §5); - every external URL and vendor claim as of 2026-09-10, including the CookieBlock removal, the Chrome MV2 timeline, and the four label-source sites (the author checked these with controls — §7 — but they have not been independently re-fetched);
- whatever a reader without a checklist would notice.
13. Related
- Classifying Cookies — the page these notes are for.
- Corpus — how the corpus was built, and every caveat that is not specific to this page.
- cookie_syncing — the sibling page's notes; the identifier-heuristic material lives there.
- website_classification — the per-target LLM table is shared with that page.
References
- [1]
- Calzavara, Stefano; Casarin, Samuele; Squarcina, Marco; Maffei, Matteo (2026): "From Syntactic Matching to Taint Tracking and Back: A Comparative Study of Web Tracking Detection Techniques", in: Proceedings on Privacy Enhancing Technologies. (Link)
- [2]
- Bollinger, Dino; Kubicek, Karel; Cotrini, Carlos; Basin, David (2022): "Automating Cookie Consent and GDPR Violation Detection", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2893-2910. USENIX Association, Boston, MA. (Link)
- [3]
- Munir, Shaoor; Siby, Sandra; Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair; Troncoso, Carmela (2023): "CookieGraph: Understanding and Detecting First-Party Tracking Cookies", pp. 3490–3504. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)
- [4]
- Lin, Maxwell; Lin, Shihan; Wu, Helen; Wang, Karen; Yang, Xiaowei (2024): "Browsing without Third-Party Cookies: What Do You See?", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
- [5]
- Chen, Baiqi; Lyu, Jiawei; Wu, Tingmin; Chhetri, Mohan Baruwal; Bai, Guangdong (2025): "Semantics-Aware Cookie Purpose Compliance", in: Proceedings of the ACM Web Conference. (DOI)
- [6]
- Randall, Audrey; Snyder, Peter; Ukani, Alisha; Snoeren, Alex C.; Voelker, Geoffrey M.; Savage, Stefan; Schulman, Aaron (2022): "Measuring UID smuggling in the wild", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
