This is an old revision of the document!
Table of Contents
Analysing and Classifying JavaScript
Most of what a privacy measurement wants to know about a page is decided by its JavaScript. The cookie was written by a script. The request was issued by a script. The fingerprint was taken by a script. So sooner or later a study has to answer a question of the form which of the eighty scripts this page ran did that, and what else does it do — and that is a different measurement problem from the ones on the neighbouring pages, with its own units, its own ground truth and its own failure modes.
This page is about that problem. Requests covers classifying network requests by URL, which is where filter lists work; Cookies covers the storage a script writes; Fingerprinting covers one specific behaviour a script can exhibit. This page covers the code: how to observe it, how to label it, and what unit to count it in. For the instrumentation itself — which patched browser to build, which crawler to drive — see Crawler, which compares the tools in detail; this page starts where that page stops, at the point where you have a trace and have to decide what it means.
The single most consequential decision on this page is the unit of analysis, and it is usually made by accident. Domain, hostname, script URL, script content, function: each gives a different answer to “is this tracking”, and the literature contains headline numbers at every level that are routinely compared as if they were the same measurement. A finding that 0.67% of scripts are obfuscated [1Skolka, Philippe; Staicu, Cristian-Alexandru; Pradel, Michael (2019): "Anything to Hide? Studying Minified and Obfuscated Code in the Web", in: Proceedings of the ACM Web Conference. (DOI)] and a finding that 95.90% of domains with script data load at least one obfuscated script [2Sarker, Shaown; Jueckstock, Jordan; Kapravelos, Alexandros (2020): "Hiding in Plain Site: Detecting JavaScript Obfuscation through Concealed Browser API Usage", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] are both true, one year apart, on overlapping crawls — different unit, different definition, no contradiction. Fix your unit before you fix your method.
The Unit Problem
The granularity ladder
TrackerSift [3Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] is the clearest statement of the problem because it measures the same 100K-site crawl at four granularities and reports how much of the traffic each one can cleanly separate into “tracking” or “functional”. Requests that cannot be assigned either way are mixed: the same unit serves both purposes.
| Granularity | Tracking | Functional | Mixed | Separated at this level | Cumulative |
|---|---|---|---|---|---|
| Domain | 755,784 | 566,810 | 1,129,109 | 54% | 54% |
| Hostname | 161,604 | 106,542 | 860,963 | 24% | 65% |
| Script | 235,157 | 490,295 | 135,511 | 84% | 94% |
| Method | 23,819 | 74,223 | 37,469 | 72% | 98% |
Counts are script-initiated network requests on a crawl of 100K websites; more than 17% of domains, 48% of hostnames, 6% of scripts and 9% of methods observed were mixed [3Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)].
Read the last column as the ceiling on any blocking or attribution method that stops at that row. A domain-level method leaves nearly half the traffic ambiguous. This is why filter lists — which mostly operate on URL patterns, i.e. rows one and two — have the residual error they do, and it is the entire motivation for the script- and function-level literature below.
Mixed scripts are the normal case, not the tail
Three measurements, three different denominators, one finding. They are not independent — all three come from Shafiq's group with Amjad as first author — but nobody has contradicted them:
| Study | Population | What is mixed |
|---|---|---|
| TrackerSift, IMC 2021 [3Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] | 100K websites | 6% of scripts, 9% of methods |
| Amjad et al., PoPETs 2023 [4Amjad, Abdul Haddi; Shafiq, Zubair; Gulzar, Muhammad Ali (2023): "Blocking JavaScript Without Breaking the Web: An Empirical Investigation", in: Proceedings on Privacy Enhancing Technologies. (DOI)] | 100K sites sampled from Tranco top 1M | 14.6% of 256K scripts in 2022, up from 12.8% of 220K in 2021 — and a 14% increase in the number of websites carrying a mixed script over that year |
| NoT.js, CCS 2024 [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] | Tranco top 10K | 13.4% of scripts; 62.3% of websites carry at least one mixed script, and 70.6% of those mixed scripts are third-party |
The practical consequence is measured directly on a crawl of 100K sites sampled from the Tranco top million [4Amjad, Abdul Haddi; Shafiq, Zubair; Gulzar, Muhammad Ali (2023): "Blocking JavaScript Without Breaking the Web: An Empirical Investigation", in: Proceedings on Privacy Enhancing Technologies. (DOI)]. Blocking every script cuts tracking requests from 1,175,033 to 265,101 — 22.6% remain — but leaves 75.9% of functional requests standing and breaks legitimate functionality on roughly two-thirds (66%) of tested webpages.1) Blocking only a curated set of tracking scripts is the better trade: 95% of functional requests persist while 30% of tracking requests survive, or per webpage a 57% reduction in tracking requests for an 11% reduction in functional ones. But the mixed scripts are exactly the ones it cannot touch, and dropping to method granularity reduces major breakage by 3.8× at the same level of tracking prevention.
NoT.js [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] is the current state of the art at that granularity. It builds a graph from the dynamic execution context of each function call — the call stack and calling context, collected by its own Chrome extension over the DevTools Protocol's Runtime.CallFrame and Debugger.Scope — trains a random forest on it, and then generates surrogate scripts that neutralise the tracking functions while leaving the rest of the file intact. Surrogate generation is not new — SugarCoat [6Smith, Michael; Snyder, Peter; Livshits, Benjamin; Stefan, Deian (2021): "SugarCoat: Programmatically Generating Privacy-Preserving, Web-Compatible Resource Replacements for Content Blocking", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] already patched scripts automatically, producing replacements for 231 unique in-the-wild scripts, and ships some of them in Brave — but its input set is whatever a filter-list author has already written an exception rule for. NoT.js picks the functions itself. It reports 94% precision and 98% recall on function-level tracking detection over the top 10K, classifying 32.1% of the 2,088K JavaScript functions in its dataset as tracking, and 0% major / 8% minor breakage in a 50-site manual study against WebGraph's 6% / 10%.2)
Sub-script blocking is already industry practice — just not automatically. uBlock Origin's scriptlet library operates well below whole-script granularity: json-prune intercepts JSON.parse() and Response.json() and removes properties from the parsed result, trusted-replace-node-text rewrites text inside specific DOM nodes.3) AdGuard's Scriptlets library does the same. Brave's farbling modifies API return values rather than blocking scripts. What none of them do is derive the sub-script target automatically — every scriptlet is hand-written against a specific vendor's code. That gap is what the function-granularity literature is trying to close, and as of 2026-08-06 no browser or major list has adopted an automated version of it.
A script is not a URL
The convenient identity “one script = one URL” is wrong often enough to change results, and it is getting worse:
- Inline scripts have no URL at all. They are the ordinary case, not an edge case, and every URL-keyed pipeline silently drops them. Key them by content hash plus document URL plus position, or by the CDP
scriptIdwithhasSourceURLrecorded, and report how many you had. - Dynamic injection. On the median page in HTTP Archive's 2024 crawl, 24% of scripts (desktop; 21% mobile) are injected by
createElement/innerHTMLrather than present in the served HTML — at the 90th percentile it is 70–73%.4) Those have no static load-time URL. - Dynamic generation. Lekies et al. [7Lekies, Sebastian; Stock, Ben; Wentzel, Martin; Johns, Martin (2015): "The Unexpected Dangers of Dynamic JavaScript", in: Proceedings of the USENIX Security Symposium. (Link)] studied the 150 Alexa top-500 domains offering free registration and found 81 of them serving scripts whose content differs per request; on 49 domains (209 scripts) the content depended on the user's session state, so the same URL is a different program per visitor.
- Bundling. Rack and Staicu [8Rack, Jeremy; Staicu, Cristian-Alexandru (2023): "Jack-in-the-box: An Empirical Study of JavaScript Bundling on the Web and its Security Implications", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] detected 1,086,368 bundles crawling the Tranco top 1M and found at least one bundle on 40% of crawled websites. A bundle is many libraries in one URL, which is precisely the mixed-script problem baked in at build time — and most of what it contains never runs: Muzeel [9Kupoluyi, Jesutofunmi; Chaqfeh, Moumena; Varvello, Matteo; Coke, Russell; Hashmi, Waleed; Subramanian, Lakshmi; Zaki, Yasir (2022): "Muzeel: assessing the impact of JavaScript dead code elimination on mobile web performance", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] found that half of the 300,000 JavaScript files on 15,000 popular pages have at least 70% unused functions, accounting for about 55% of file size.
- Self-deletion. Li et al. [10Wang, Xinzhe; Zhuang, Zeyang; Meng, Wei; Cheng, James (2024): "Detecting and Understanding Self-Deleting JavaScript Code", in: Proceedings of the ACM Web Conference. (DOI)] found script-deletion behaviour on 42.44% of 870,734 reachable websites: a script removes its own
<script>element after running. If you enumerate the DOM after load, those scripts are simply not there. - Content stability. SICILIAN [11Soni, Pratik; Budianto, Enrico; Saxena, Prateek (2015): "The SICILIAN Defense: Signature-based Whitelisting of Web JavaScript", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] found 93% of 33,302 observed scripts static over the observation window and 669 (28.9% of the changed ones) with code-introducing updates — so hashing works, but a hash is a snapshot, not an identity.
What to do: record, per script, all of the URL, a content hash, the Debugger.scriptParsed scriptId and its hasSourceURL/isModule flags, and the initiator. Then say in the paper which of those you counted. A “number of scripts” with no stated identity rule is not reproducible.
What This Literature Actually Is
Everything in this section comes from a structured extraction over 5,859 full-text papers from CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf and IEEE S&P, 2010–2026, one record per paper with a verbatim evidence quote per claim. The 2025 and 2026 venue-years are provisional — CCS and IMC 2026 have not been held and two more 2026 venue-years are incompletely selected. The population here is 206 papers that analyse or classify JavaScript running in a browser — how that population was built, and what it misses, is at Reproducing These Figures.
Neither obvious search handle finds it
If you come from program analysis and search that way, you will not find this field.
| Handle | Papers | Of which measure the web | Share |
|---|---|---|---|
Used or produced a program-analysis tool | 1,385 | 299 | 21.6% |
studyTypes includes code-or-binary-analysis | 1,484 | 347 | 23.4% |
And the program-analysis tools these venues actually use are not JavaScript tools: LLVM (101 papers), Soot (81), IDA Pro (62), FlowDroid (48), angr (39), Z3 (38), Ghidra (38), CodeQL (34), Androguard (33), Apktool (30). Esprima, at 23 papers, is the highest-ranked JavaScript parser in the whole corpus. In a broad security corpus, “program analysis” means binaries, Android apps and smart contracts; web-script analysis is a small minority inside it, and shares almost no toolchain with the rest.
Where the 206 papers are
| Venue | Corpus papers | JS-analysis papers | Share of venue |
|---|---|---|---|
| USENIX Security | 1,410 | 44 | 3.1% |
| TheWebConf | 843 | 41 | 4.9% |
| CCS | 990 | 33 | 3.3% |
| IMC | 638 | 24 | 3.8% |
| IEEE S&P | 767 | 22 | 2.9% |
| NDSS | 701 | 21 | 3.0% |
| PETS | 510 | 21 | 4.1% |
| Period | Corpus papers | JS-analysis papers | Per 1,000 corpus papers |
|---|---|---|---|
| 2010–2013 | 511 | 14 | 27.4 |
| 2014–2017 | 769 | 34 | 44.2 |
| 2018–2021 | 1,439 | 61 | 42.4 |
| 2022–2024 | 1,955 | 68 | 34.8 |
| 2025–2026 (provisional) | 1,185 | 29 | 24.5 |
The field grew sharply into the mid-2010s and has been declining as a share of these venues since. That is not a decline in importance — it is the topic being absorbed into tracking, fingerprinting and supply-chain papers that no longer describe themselves as JavaScript analysis.
What they are about
Ranked, not measured: detection.phenomenon is one of the least reproducible fields in the extraction (~20% exact-string agreement between independent runs), so this is a ranking with a printed residue, never a set of percentages.
| Research family | Papers | Share of 206 |
|---|---|---|
| Tracking- and advertising-script classification | 29 | 14.1% |
| Fingerprinting-script detection | 24 | 11.7% |
| Malicious-script and cloaking detection | 24 | 11.7% |
| Third-party libraries, inclusion and supply chain5) | 24 | 11.7% |
| Web-API usage measurement | 22 | 10.7% |
| Client-side vulnerabilities (XSS, CSP, taint flows) | 18 | 8.7% |
| Browser-extension scripts | 8 | 3.9% |
| Data leakage by scripts | 8 | 3.9% |
| Script performance, size and dead code | 8 | 3.9% |
| Script change and identity over time | 7 | 3.4% |
| Cryptojacking | 5 | 2.4% |
| WebAssembly (non-mining) | 5 | 2.4% |
65 detection tuples did not fold into any family and are printed by the report script rather than dropped — a slightly larger share of a larger population than the 45 of the earlier corpus, so this fold has aged well where others have not.
The shape to notice: the privacy reader's own two families — tracking/advertising classification and fingerprinting-script detection — lead the ranking, and together they are barely a quarter of the field (53 of 206). If you search these venues for “JavaScript detection” you will mostly get client-side vulnerability and malware papers, which use the same parsers and the same instrumented browsers on a different question. They are worth reading for method and misleading as related work.
Anchor papers to read first
- SoK: Advances and Open Problems in Web Tracking [13Vekaria, Yash; Beugin, Yohan; Munir, Shaoor; Acar, Gunes; Bielova, Nataliia; Englehardt, Steven; Iqbal, Umar; Kapravelos, Alexandros; Laperdrix, Pierre; Nikiforakis, Nick; Polakis, Jason; Roesner, Franziska; Shafiq, Zubair; Zimmeck, Sebastian (2025): "SoK: Advances and Open Problems in Web Tracking". arXiv preprint arXiv:2506.14057. (Link)] — a 2025 systematisation by fourteen of the field's authors. It is the fastest orientation to where script classification sits in the wider tracking literature. Preprint, so check for a venue version.
- SoK: After Decades of Web Tracker Detection, What's Next? [14Rieder, Wolf; Raschke, Philip; Cory, Thomas; Sechting, Christian René; Kumar, Aditya; Küpper, Axel (2026): "SoK: After Decades of Web Tracker Detection, What's Next?", in: Proceedings of the IEEE Symposium on Security and Privacy. (Link)], IEEE S&P 2026 — a meta-study specifically of tracker detectors, which is the classifier lineage this page is about.
- Then the granularity chain in order: TrackerSift [3Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] → blanket-vs-selective blocking [4Amjad, Abdul Haddi; Shafiq, Zubair; Gulzar, Muhammad Ali (2023): "Blocking JavaScript Without Breaking the Web: An Empirical Investigation", in: Proceedings on Privacy Enhancing Technologies. (DOI)] → NoT.js [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)].
Methods, and Which Ones Are Current
This corpus now reaches 2026, but its 2025 and 2026 venue-years are provisional and thin. A ranking of what the 2010–2026 literature did is a fact about the literature, not advice about what to do now. The table below dates each method and states its status as of 2026-08-06; the rows marked current were checked against post-2024 work outside the corpus, and the tool table further down now gives partial corpus support for two of them.
| Era | Method | Representative work | Status in 2026 |
|---|---|---|---|
| 2010–2015 | Static AST and lexical features, hand-built, for malicious JS | Prophiler, Revolver; HideNoSeek later attacks it | Historical for tracking; still alive in malware detection |
| 2013–2016 | Signature whitelisting of known-good scripts | SICILIAN [11Soni, Pratik; Budianto, Enrico; Saxena, Prateek (2015): "The SICILIAN Defense: Signature-based Whitelisting of Web JavaScript", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] | Superseded by bundling and per-request script generation |
| 2015–2019 | Supervised classifiers on script URL + content features | Ikram et al., one-class learning [15Ikram, Muhammad; Asghar, Hassan Jameel; Kaafar, Mohamed Ali; Mahanti, Anirban; Krishnamurthy, Balachander (2017): "Towards Seamless Tracking-Free Web: Improved Detection of Trackers via One-class Learning", in: Proceedings on Privacy Enhancing Technologies. (DOI)] | Superseded by graph and behavioural methods |
| 2016–2019 | Counting accesses to a hand-picked API set, with thresholds | Englehardt & Narayanan [16Englehardt, Steven; Narayanan, Arvind (2016): "Online Tracking: A 1-million-site Measurement and Analysis", in: Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security, pp. 1388–1401. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)]; VisibleV8 [17Jueckstock, Jordan; Kapravelos, Alexandros (2019): "VisibleV8: In-browser Monitoring of JavaScript in the Wild", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] enables it at engine level | Historical as a classifier; still the right way to collect the signal |
| 2020–2022 | Graph representations of page execution, ML-classified | AdGraph [18Iqbal, Umar; Snyder, Peter; Zhu, Shitong; Livshits, Benjamin; Qian, Zhiyun; Shafiq, Zubair (2020): "AdGraph: A Graph-Based Approach to Ad and Tracker Blocking", in: 2020 IEEE Symposium on Security and Privacy (SP), pp. 763-776. (DOI)], WebGraph [19Siby, Sandra; Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair; Troncoso, Carmela (2022): "WebGraph: Capturing Advertising and Tracking Information Flows for Robust Blocking", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2875-2892. USENIX Association, Boston, MA. (Link)] | The reference baselines — every new paper compares against them. But see the maintenance warning below |
| 2020–2022 | Sibling lineages on the same instrumentation: sequential ML over request chains; syntactic + dynamic script features | Khaleesi [20Iqbal, Umar; Wolfe, Charlie; Nguyen, Charles; Englehardt, Steven; Shafiq, Zubair (2022): "Khaleesi: Breaker of Advertising and Tracking Request Chains", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2911-2928. USENIX Association, Boston, MA. (Link)], FP-Inspector [21Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair (2021): "Fingerprinting the Fingerprinters: Learning to Detect Browser Fingerprinting Behaviors", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)] | Still cited baselines, but neither is a page-execution graph — do not lump them in |
| 2023–2024 | Content-independent representations: V8 bytecode as text | Ghasemisharif & Polakis [22Ghasemisharif, Mohammad; Polakis, Jason (2023): "Read Between the Lines: Detecting Tracking JavaScript with Bytecode Classification", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] | Current. 97.08% accuracy on Tranco top-50K scripts; robust to URL and source-level obfuscation |
| 2021 | Method granularity by attributing requests to the method that issued them | TrackerSift [3Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] | The measurement that motivated the rest of this row-group |
| 2024 | Function granularity with dynamic calling context, plus automatic surrogates | NoT.js [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] | Current and where the field is going |
| 2024 | Deployability as a first-class goal — small feature sets, low overhead | AdFlush [23Lee, Kiho; Lim, Chaejin; Jin, Beomjin; Kim, Taeyoung; Kim, Hyoungshick (2024): "AdFlush: A Real-World Deployable Machine Learning Solution for Effective Advertisement and Web Tracker Prevention", in: Proceedings of the ACM Web Conference. (DOI)] | Current. 27 features selected from 883; F1 0.98 vs AdGraph 0.93 / WebGraph 0.90; 56% less CPU and 80% less memory than AdGraph; F1 stayed above 0.97 over six months without retraining |
| 2024 | Taint tracking plus entropy, rather than API counts | FP-Tracer [24Boussaha, Soumaya; Hock, Lukas; Bermejo, Miguel; Rumín, Rubén Cuevas; Rumín, Ángel Cuevas; Klein, David; Johns, Martin; Compagna, Luca; Antonioli, Daniele; Barber, Thomas (2024): "FP-tracer: Fine-grained Browser Fingerprinting Detection via Taint-tracking and Entropy-based Thresholds", in: Proceedings on Privacy Enhancing Technologies. (DOI)], PanoptiChrome [25Kanyal, Rahul; Sarangi, Smruti R. (2024): "PanoptiChrome: A Modern In-browser Taint Analysis Framework", in: Proceedings of the ACM Web Conference. (DOI)] | Current for the fingerprinting sub-problem |
| 2024 | Federated detection without centralising the crawl | FP-Fed [26Annamalai, Meenatchi Sundaram Muthu Selva; Bilogrevic, Igor; Cristofaro, Emiliano De (2024): "FP-Fed: Privacy-Preserving Federated Detection of Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] | Current, and the only route if you cannot ship crawl data |
| 2025 | Function-level detection from V8 bytecode + Transformer, matched pre-execution | ByteDefender [27Nikkhah Bahrami, Pouneh; Cutler, Dylan; Bilogrevic, Igor (2025): "Byte by Byte: Unmasking Browser Fingerprinting at the Function Level Using V8 Bytecode Transformers", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)], CCS 2025 | Current, and outside this corpus. Combines the two live directions above |
| 2026 | Head-to-head comparison of syntactic filter matching against taint tracking | Calzavara et al. [28Calzavara, 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)], PoPETs 2026 | Read before choosing between the two; artifact available, functional and reproduced |
What is genuinely superseded
- Matching on script URL or filename.
google-analytics.com/analytics.jsis trivially renamed, first-party-proxied or CNAME-cloaked. Every current method is deliberately content- or behaviour-based for this reason. - Fixed thresholds on API-access counts. They were a reasonable 2016 heuristic and are now a baseline that learned methods beat by wide margins; more importantly they encode one crawl's idea of which APIs matter.
- Treating a filter-list hit as the definition of tracking rather than as a noisy label. See the next section.
What the corpus cannot tell you, and what we found instead
LLM-based classification of web scripts is, as of 2026-08-06, essentially absent from the peer-reviewed literature. A targeted search across PETS 2025/2026, USENIX Security 2025, NDSS 2025/2026, IMC 2025, TheWebConf 2025/2026, CCS 2025 and arXiv found no paper that classifies web scripts as trackers with a language model, or that uses one to summarise a script's privacy-relevant behaviour. The nearest work is adjacent rather than on-point: LLM-aided deobfuscation feeding a graph classifier for JavaScript malware,6) LLM screening of malicious npm packages, and humanify, which uses a model only to suggest identifier names during de-minification.7)
Treat this as an opportunity, not a settled answer. If you are planning an LLM-based script classifier, you are not late — but you also have no baseline to cite, so budget for building one, and for the reviewer question about cost, reproducibility and prompt/version drift that this page cannot yet answer for you.
Two 2025 results that change how you design a crawl
- Scripts behave differently on mobile and desktop, on purpose. Zafar et al. [29Zafar, Ahsan; Su, Junhua; Datta, Sohom; Kapravelos, Alexandros; Das, Anupam (2025): "Same Script, Different Behavior: Characterizing Divergent JavaScript Execution Across Different Device Platforms", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] find that 20.6% of scripts on the Tranco top 10K take platform-specific execution paths, attribute the divergence source for 92.8% of conditional Web-API calls, and report that 76% of those involve known fingerprinting APIs — desktop paths skew to fingerprinting and bot detection, mobile paths to behavioural profiling. A desktop-only crawl does not just miss mobile scripts; it sees different code in the same script.
- Manifest V3 did not break blocking, contrary to the common assumption. Lukić and Papadopoulos [30Lukić, Karlo; Papadopoulos, Lazaros (2026): "Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad Blocker Effectiveness", in: Proceedings on Privacy Enhancing Technologies. (Link)] compared MV2 and MV3 builds of four popular blockers and found no statistically significant reduction in effectiveness under
declarativeNetRequest. MV2 was disabled everywhere on 2025-07-24 and remaining MV2 extensions leave the Chrome Web Store on 2026-08-31,8) so if your method depends on an MV2 extension, it has already stopped working — but do not assume the blocking results in older papers are invalidated.
Ground Truth, Which Is This Field's Weakest Link
Of the 206 papers, 198 record at least one classification task. What they classify with:
classification.method | Papers | Share of 198 |
|---|---|---|
| heuristic-rules | 108 | 54.5% |
| manual-labelling | 70 | 35.4% |
| third-party-service | 53 | 26.8% |
| blocklist | 52 | 26.3% |
| curated-database | 40 | 20.2% |
| dynamic-analysis | 33 | 16.7% |
| supervised-ml | 31 | 15.7% |
| regex-or-signature | 27 | 13.6% |
| static-analysis | 25 | 12.6% |
| graph-analysis | 13 | 6.6% |
| other | 11 | 5.6% |
| unsupervised-ml | 11 | 5.6% |
| llm | 2 | 1.0% |
The llm row is new: on the 4,322-paper corpus this enum never fired for a JavaScript-classification task at all. Two papers is not a trend, and it does not contradict the finding below that no peer-reviewed paper yet classifies web scripts as trackers with a language model.
And what they treat as truth. These 198 papers produce 351 distinct free-text ground-truth strings, folded here into families; 80 tuples did not fold and are printed by the report script.
| Ground-truth family | Papers | Share of 198 |
|---|---|---|
| Authors' own manual inspection | 102 | 51.5% |
| Filter list or tracker database | 44 | 22.2% |
| Malware / phishing blacklist service | 13 | 6.6% |
| Synthetic or seeded ground truth | 9 | 4.5% |
| Vulnerability database | 9 | 4.5% |
| Spec or documentation | 5 | 2.5% |
| Prior published dataset or labels | 4 | 2.0% |
| Library signature catalogue | 1 | 0.5% |
| Recruited or external annotators | 1 | 0.5% |
The most common ground truth for “is this script a tracker” is the authors reading the script. That is a defensible choice at small scale and it is what most of this field does — but it means the labels are unpublished, unaudited and, in most cases, produced by the same people who built the classifier. Only 44 of 198 papers anchor to a filter list or tracker database, and exactly one used annotators from outside the author team.
Validation, counted per paper rather than per tuple: 146 of 198 (73.7%) report some validation for at least one classification, and 52 (26.3%) report none anywhere. Where validation exists it is overwhelmingly more manual inspection (129 papers, 65.2%); comparison to another method 33 (16.7%), cross-validation 16 (8.1%), a held-out test set 12 (6.1%).
If you use a filter list as ground truth
You probably will, and it is the right pragmatic choice — but be explicit that it is a proxy label with known bias, not truth:
- Filter lists label requests, not scripts. A script gets its label by inheritance from what it requested, which is exactly the inference that mixed scripts break. NoT.js's own labelling is deliberately conservative for this reason: a function participating in any non-tracking request is labelled non-tracking, so mixed functions (3.9% of its ground truth) are counted as negatives [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)].
- Lists over-cover the head and under-cover the tail; up to 90% of EasyList's resource-blocking rules provide no benefit in common browsing [31Snyder, Peter; Vastel, Antoine; Livshits, Ben (2020): "Who Filters the Filters: Understanding the Growth, Usefulness and Efficiency of Crowdsourced Ad Blocking", Proc. ACM Meas. Anal. Comput. Syst. 4(2). (DOI) (Link)], and Requests has the fuller argument.
- Your recall is capped by the list's coverage, so a classifier that “outperforms the filter list” is being measured against its own teacher. Every current paper handles this by hand-checking a sample of its false positives; Ghasemisharif & Polakis [22Ghasemisharif, Mohammad; Polakis, Jason (2023): "Read Between the Lines: Detecting Tracking JavaScript with Bytecode Classification", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] report that all three of their models surfaced tracking scripts absent from current lists, which is the useful form of that result.
Label sources that are alive in 2026
| Source | Granularity | State, checked 2026-08-06 |
|---|---|---|
| EasyList / EasyPrivacy | mostly URL pattern; $script option gives partial script-URL granularity | Actively maintained, commits daily |
| DuckDuckGo Tracker Radar | resource regex rule, with fingerprinting score 0–3, apis, cookies, prevalence, responseHashes | Monthly automated data updates; the closest thing to a maintained public per-script label set |
| WhoTracks.me | tracker/company level | Alive, monthly updates; the public site now redirects to Ghostery |
| HTTP Archive | page-level script inventories, queryable in BigQuery | Alive; no JavaScript chapter in the 2025 Almanac, so per-script stats are thinner than they were |
No purpose-built, publicly released, hand-labelled corpus of tracking scripts appeared in 2024–2026 that we could find. That absence is itself a contribution someone could make.
Getting the Signal
The instrumentation comparison lives on Crawler and is not repeated here. What matters for analysis is which question each layer can answer at all:
| Layer | Answers | Cannot answer |
|---|---|---|
| HAR / request log | which URLs loaded, in what order | which script caused it |
CDP (Debugger.scriptParsed, Runtime.addBinding, Profiler coverage) | script identity, parse-time stack, per-function coverage, calls to bindings you injected | complete API-call provenance; coverage is explicitly lossy under GC, and precise coverage disables optimised code |
| In-page API overrides (OpenWPM's instrument) | which API was called with what arguments and stack | anything the page can detect or unhook — you are in the page's own JS world |
| Patched engine (VisibleV8, PageGraph) | every JS↔host boundary crossing, below the reach of page JavaScript; causal graph of DOM/script/request | flows through the script's own data |
| Taint tracking (Project Foxhound, PanoptiChrome) | did this value reach that sink | costs a browser build and answers nothing about prevalence |
| Static AST / bytecode | what the code could do, on code you never executed | what it actually did, and anything behind eval |
Which of these the 206 papers actually name:
| Tool family | Papers | Share of 206 |
|---|---|---|
| Esprima | 21 | 10.2% |
| OpenWPM | 19 | 9.2% |
| PageGraph | 8 | 3.9% |
| Project Foxhound (taint tracking) | 8 | 3.9% |
| VisibleV8 | 8 | 3.9% |
| js-beautify | 7 | 3.4% |
| V8 (as an analysis substrate) | 7 | 3.4% |
| Closure Compiler | 5 | 2.4% |
| Babel, FP-Inspector, Jalangi, WABT | 4 each | 1.9% |
| JStap | 3 | 1.5% |
| jsdom | 2 | 1.0% |
| Acorn, AdGraph, Emscripten, JSgraph, JSNice, Khaleesi, Rhino, SpiderMonkey, TAJS, UglifyJS, unnamed deobfuscator | 1 each | 0.5% |
Two rows moved enough to matter. Esprima has overtaken OpenWPM as the most-named tool, and Project Foxhound went from 2 papers to 8 and PageGraph from 4 to 8 — the taint-tracking and page-graph instruments the Methods table above calls current are the ones the 2025–2026 papers actually picked up. That is the rare case where the corpus confirms a currency judgement instead of only dating it.
120 of 206 papers (58.3%) name no JavaScript-analysis tool at all — they wrote their own parser, regexes or instrumentation and did not name it. That is the field's reproducibility problem in one number.
Maintenance status, because half of these are frozen
Checked 2026-08-06 against each repository's own commit history.
| Tool | Last commit | Verdict |
|---|---|---|
| OpenWPM | 2026-08-02 | Alive; v0.35.0, pinned to Firefox 152, GPLv3 |
| VisibleV8 | 2026-07-03 | Alive; rolling Chromium patchsets, prebuilt Docker images, BSD-3 |
| PageGraph (in brave-core; pagegraph-crawl) | 2026-08-04 | Alive; full JS-API recording needs Brave Nightly. pagegraph-rust is archived — use pagegraph-query |
| Project Foxhound | 2026-07-24 | Alive; v142.0.1, GPL-3.0. History was resynced to upstream in Feb 2026, so re-clone an old checkout |
| Tracker Radar Collector | 2026-07-22 | Alive; stock Chromium over CDP, no patched engine |
| Jalangi2 | 2026-01-10 | Dormant; community bugfixes only, no releases |
| WebGraph | 2024-07-30 | Stalled; the README's own last note is an unresolved OpenWPM/Firefox compatibility problem |
| AdGraph | 2019-09-16 | Abandoned in practice; cite the method [18Iqbal, Umar; Snyder, Peter; Zhu, Shitong; Livshits, Benjamin; Qian, Zhiyun; Shafiq, Zubair (2020): "AdGraph: A Graph-Based Approach to Ad and Tracker Blocking", in: 2020 IEEE Symposium on Security and Privacy (SP), pp. 763-776. (DOI)], do not plan to run it |
| NoT.js | 2024-07-18 | Frozen at its paper |
| Esprima | 2021-08-29 | Frozen; caps at ES2019. ESLint moved to Espree-over-Acorn years ago — use Acorn or @babel/parser |
The pattern is worth internalising before you plan a project around one of these: the production tools (OpenWPM, VisibleV8, PageGraph, Foxhound, Tracker Radar) are maintained; the paper artifacts are not. Budget for porting anything from the third column.
Static analysis and its four enemies
Static analysis of shipped web JavaScript is fighting the build pipeline, not the tracker:
- Minification is near-universal and destroys the identifier names your features probably depend on. Skolka et al. [1Skolka, Philippe; Staicu, Cristian-Alexandru; Pradel, Michael (2019): "Anything to Hide? Studying Minified and Obfuscated Code in the Web", in: Proceedings of the ACM Web Conference. (DOI)] found 38.5% of all crawled scripts had gone through some transformation.
- Obfuscation proper is rare but definitionally contested. The same study puts deliberate obfuscation at 0.67% of scripts (2,842); Sarker et al. [2Sarker, Shaown; Jueckstock, Jordan; Kapravelos, Alexandros (2020): "Hiding in Plain Site: Detecting JavaScript Obfuscation through Concealed Browser API Usage", in: Proceedings of the ACM Internet Measurement Conference. (DOI)] — defining it as invoking APIs that static analysis cannot resolve — put it at 95.90% of domains loading at least one obfuscated script — where the denominator is the 77,423 Alexa top-100K domains for which they obtained script data at all, so ~74% of the top 100K rather than 95.9% of it, with 5,028 distinct obfuscated scripts calling
eval. Both are correct. Report your definition and your unit or the number means nothing. - Bundling merges unrelated code into one artifact — 40% of sites [8Rack, Jeremy; Staicu, Cristian-Alexandru (2023): "Jack-in-the-box: An Empirical Study of JavaScript Bundling on the Web and its Security Implications", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)].
- Dynamic code defeats it outright. Forced-execution engines exist precisely for this: J-Force, and more recently FV8 [32Pantelaios, Nikolaos; Kapravelos, Alexandros (2024): "FV8: A Forced Execution JavaScript Engine for Detecting Evasive Techniques", in: Proceedings of the USENIX Security Symposium. (Link)], which selectively forces execution of the APIs that conditionally inject dynamic code (
eval,setTimeoutand friends) to reveal evasive behaviour and surfaced 28 evasion categories across npm packages and browser extensions.
If you must analyse statically, the current toolchain is Acorn or @babel/parser for the AST, swc/meriyah when speed matters, and one of the live deobfuscators (webcrack, synchrony, restringer) as a preprocessing step — all four checked active in 2026. Bytecode [22Ghasemisharif, Mohammad; Polakis, Jason (2023): "Read Between the Lines: Detecting Tracking JavaScript with Bytecode Classification", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)][27Nikkhah Bahrami, Pouneh; Cutler, Dylan; Bilogrevic, Igor (2025): "Byte by Byte: Unmasking Browser Fingerprinting at the Function Level Using V8 Bytecode Transformers", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] is the interesting alternative: it survives source-level obfuscation because it is what the engine actually compiled.
Crawl Methodology, Against the Corpus
Of the 206 papers, 165 (80.1%) ran a crawl and 163 recorded a configuration. They report better than the corpus average on every axis — and still leave the two axes that matter most for this topic largely unstated.
crawlConfig field | States a value | Share of 163 | All 1,080 papers with a crawl configuration |
|---|---|---|---|
| Statefulness | 45 | 27.6% | 20.3% |
| Interaction depth | 147 | 90.2% | 77.9% |
| Consent action | 79 | 48.5% | 32.3% |
| Headless or headful | 31 | 19.0% | 13.0% |
| Authentication | 134 | 82.2% | 72.1% |
| Browser named | 132 | 81.0% | 49.0% |
19.0% state headless-or-headful, and for this topic that is not a formality. Two measurements in the corpus quantify the cost:
- Jueckstock et al. [33Jueckstock, Jordan; Sarker, Shaown; Snyder, Peter; Beggs, Aidan; Papadopoulos, Panagiotis; Varvello, Matteo; Livshits, Benjamin; Kapravelos, Alexandros (2021): "Towards Realistic and Reproducible Web Crawl Measurements", in: Proceedings of the ACM Web Conference. (DOI)] found about 10% of script families show consistent browser-configuration bias, and traced one concretely: Crazyegg's script invoked fewer than 15 browser APIs under a naive crawl but nearly 60 under a stealth crawl, because a function named
uaBotshort-circuits on bot detection. A naive crawl does not under-count that script — it observes a different program. - FP-Fed [26Annamalai, Meenatchi Sundaram Muthu Selva; Bilogrevic, Igor; Cristofaro, Emiliano De (2024): "FP-Fed: Privacy-Preserving Federated Detection of Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] found that a real user logging in and completing an authentication flow encountered 3.07× more fingerprinting scripts than its automated crawlers did.
Add Zafar et al.'s 20.6% platform-divergent scripts [29Zafar, Ahsan; Su, Junhua; Datta, Sohom; Kapravelos, Alexandros; Das, Anupam (2025): "Same Script, Different Behavior: Characterizing Divergent JavaScript Execution Across Different Device Platforms", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] and the conclusion is unavoidable: the scripts you most want to classify are the ones deciding what to show you. See Crawler on detectability and Crawling location on the vantage-point half of the same problem.
The consent row deserves its own sentence. On EU-facing sites a large part of the advertising and analytics stack is loaded by the consent management platform after a consent click, so a crawl that never interacts with the banner measures a different script population from one that accepts — and a third one from one that rejects. Half these papers do not say which they did. Decide deliberately, state it, and see Consent for how to drive the interaction; the same choice is what makes Cookies figures comparable or not.
Artifact release is a bright spot: 53 of the 68 papers from 2022–2024 (77.9%) released an artifact link, against 65.0% for the corpus over the same years, and 27 of 29 (93.1%) in the provisional 2025–2026 window against 76.5%. Only 19 of 206 (9.2%) assess a law — GDPR in 17 — against 6.9% corpus-wide (402 of 5,859), and 24 (11.7%) recruited participants.
What to Report
A reviewer who works in this area will look for these, and their absence is the most common reason a script-classification result is not believed:
- The unit. Script URL, script content hash, script instance, or function — and how you decided two observations are the same script.
- The denominator. Scripts, script instances, sites, or pages, and which rank cut of which list. A percentage of scripts and a percentage of sites differ by an order of magnitude on the same crawl; compare the 0.41% of scripts that FP-Fed calls fingerprinting [26Annamalai, Meenatchi Sundaram Muthu Selva; Bilogrevic, Igor; Cristofaro, Emiliano De (2024): "FP-Fed: Privacy-Preserving Federated Detection of Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] with the site-level fingerprinting figures on Fingerprinting.
- The labels. Where ground truth came from, how many items, who produced it, and whether it is released. If it is a filter list, say which list, which snapshot date, and how you propagated request labels to scripts.
- Validation. A hand-checked sample of false positives and false negatives, with counts. A quarter of this literature reports none.
- The crawl. Browser and version, headless or headful, stateful or stateless, consent action, interaction depth, vantage point, and — because of the results above — whether you checked for divergent behaviour under a stealth or headful configuration.
- Handling of what you could not analyse. Inline scripts,
eval-generated code, workers, WebAssembly,blob:/data:sources, and scripts that deleted themselves. Report the count you dropped rather than letting it vanish into the denominator. - Reproducibility. The script corpus if licensing allows, the feature extractor, and the trained model. This subfield is good at this — 78% for 2022–2024 and higher since — so a paper without it stands out.
Reproducing These Figures
The population is built from three independent signals over the extraction, because neither obvious schema handle works (see above). A paper joins if it measures the web platform and at least one of:
- a
detectiontuple whose phenomenon — not merely its technique — names a script, JavaScript, WebAssembly,eval, SRI or a browser-API surface. Requiring only the technique admits every paper that used a line of JavaScript to run an experiment; - a
classificationtuple whose target names a script; - the paper used or produced a JavaScript-specific analysis tool, from an ordered family list.
Three families are in the tool table but deliberately excluded from the membership rule, because their non-analysis use is large and it was measured rather than assumed: OpenWPM alone dragged in an IPv6-scanning study, a QUIC website-fingerprinting paper and an HSTS study that used it as a plain crawler; Emscripten dragged in two papers that compiled to WebAssembly; SpiderMonkey dragged in RIDL, where the engine is the victim of a CPU attack. Three further papers — Spectre, Fallout and RIDL — were removed by hand after reading, with the reason recorded inline in the script: JavaScript is their exploit vector, not their object of study.
| Signal combination | Papers | Share of 206 |
|---|---|---|
| classification only | 57 | 27.7% |
| detection only | 53 | 25.7% |
| detection + classification | 30 | 14.6% |
| tool only | 26 | 12.6% |
| tool + detection + classification | 19 | 9.2% |
| tool + detection | 13 | 6.3% |
| tool + classification | 8 | 3.9% |
The whole rule, including every fold and the reason for each hand exclusion, is below. It needs only extractions.jsonl and the shared lib.mjs helper; the companion report_javascript.mjs prints every figure on this page with its denominator, the residue of each fold, and the full 206-paper list. Every query behind this section is on javascript; corpus-level caveats are on corpus.
- js_fold.mjs
// Population definition and folding rules for the `privacy:javascript` page. // // The question the page is about is "how do you analyse or classify the // JavaScript a page runs". Neither of the obvious schema handles answers it: // // * `tools[].category == "program-analysis"` fires on 1,385 papers (used or // produced), but that category is dominated by binary, Android and // smart-contract analysis (LLVM 101, Soot 81, IDA Pro 62, FlowDroid 48, // angr 39). Esprima, the highest-ranked JavaScript parser, is 23. // * `studyTypes` includes `code-or-binary-analysis` on 1,484 papers, same // problem, and it is the least reproducible field in the schema (57%). // // So the population is assembled from three independent signals and every // paper records which one(s) fired, so the mix can be printed rather than // asserted. Counts are of PAPERS. // --------------------------------------------------------------------------- // Signal 1: the paper used or produced a JavaScript-specific analysis tool. // Ordered families; first match wins. Deliberately does NOT include generic // browser automation (Selenium/Puppeteer drive a browser, they do not analyse // the script) nor HTML parsers (BeautifulSoup). // // `populationSignal: false` marks families that appear in the toolchain table // but must NOT by themselves put a paper in the population, because the tool // has a large non-analysis use. Measured, not assumed: OpenWPM alone dragged in // an IPv6-scanning paper, a QUIC website-fingerprinting paper and an HSTS // study, all of which used it as a plain crawler; Emscripten dragged in two // papers that *compiled* to WebAssembly rather than analysing it; SpiderMonkey // dragged in RIDL, where the engine is the victim of a CPU attack. // --------------------------------------------------------------------------- export const JS_TOOL_FAMILIES = [ // Instrumented browsers / runtime interception [/^openwpm/i, 'OpenWPM (JS instrumentation)', false], [/visiblev8|visible v8|\bvv8\b/i, 'VisibleV8'], [/pagegraph/i, 'PageGraph'], [/foxhound/i, 'Project Foxhound (taint tracking)'], [/jalangi/i, 'Jalangi'], [/^js.?graph/i, 'JSgraph'], [/adgraph/i, 'AdGraph'], [/webgraph/i, 'WebGraph'], [/khaleesi/i, 'Khaleesi'], [/fp.?inspector/i, 'FP-Inspector'], // Parsers / AST toolchain [/esprima/i, 'Esprima'], [/\bacorn\b/i, 'Acorn'], [/^babel$|babel[- ]minify|babel parser/i, 'Babel'], [/\btajs\b/i, 'TAJS'], [/jstap/i, 'JStap'], [/jsnice/i, 'JSNice'], [/abstract syntax tree/i, 'AST toolchain (unnamed)'], // Engines used as an analysis substrate [/spidermonkey/i, 'SpiderMonkey', false], [/^v8\b|v8 javascript|v8 v[0-9]/i, 'V8', false], [/\brhino\b/i, 'Rhino', false], [/jsdom/i, 'jsdom', false], // Rewriting / deobfuscation / minification [/uglify/i, 'UglifyJS'], [/closure compiler/i, 'Closure Compiler'], [/js.?beautif|javascript beautif/i, 'js-beautify'], [/deobfuscat/i, 'deobfuscator (custom or unnamed)'], // WebAssembly [/\bwabt\b|webassembly binary toolkit/i, 'WABT'], [/emscripten/i, 'Emscripten', false], [/binaryen/i, 'Binaryen', false], ]; // BabelNet is a multilingual lexicon, not the JS compiler. Excluded by name. const TOOL_EXCLUDE = /babelnet/i; export function foldJsTool(name) { if (!name || TOOL_EXCLUDE.test(name)) return null; for (const [rx, label] of JS_TOOL_FAMILIES) if (rx.test(name)) return label; return null; } // Same fold, but only the families that are strong enough to define membership. export function foldJsToolStrict(name) { if (!name || TOOL_EXCLUDE.test(name)) return null; for (const [rx, label, isSignal] of JS_TOOL_FAMILIES) { if (rx.test(name)) return isSignal === false ? null : label; } return null; } // --------------------------------------------------------------------------- // Signal 2: a detection tuple whose phenomenon or technique names the script // itself, or a script-level Web API surface, as the thing observed. // --------------------------------------------------------------------------- const JS_TOKEN = /javascript|(^|[^a-z])js([^a-z]|$)|\bscripts?\b|scriptlet|webassembly|\bwasm\b|\bdom\b|\beval\b|inline code|web api|browser api|\bast\b|\bsri\b|subresource integrity/i; // Words that make a `js`/`script` hit a false friend: shell scripts, Python // scripts, "scripted" experiments, transcripts, and the biology sense. const JS_TOKEN_FALSE = /shell script|bash script|python script|perl script|transcript|scripted (interview|survey|dialogue)|manuscript|postscript/i; export function detectionNamesJs(d) { const s = [d.phenomenon, d.technique, d.metric].filter(Boolean).join(' ~ '); if (!s) return false; if (JS_TOKEN_FALSE.test(s) && !/javascript/i.test(s)) return false; return JS_TOKEN.test(s); } // Membership requires the script to be the *object* detected, which is what // `phenomenon` names. Requiring only `technique` admits every paper that used a // line of JavaScript to run its experiment — a password-meter study, an RC4 // attack — and the script is not what those measure. export function detectionObjectIsJs(d) { const s = d.phenomenon; if (!s) return false; if (JS_TOKEN_FALSE.test(s) && !/javascript/i.test(s)) return false; return JS_TOKEN.test(s); } // --------------------------------------------------------------------------- // Signal 3: a classification tuple whose target is a script. // --------------------------------------------------------------------------- export function classificationNamesJs(c) { const s = [c.targetDetail, c.resourceName, c.taxonomy, c.groundTruthSource] .filter(Boolean) .join(' ~ '); if (!s) return false; if (JS_TOKEN_FALSE.test(s) && !/javascript/i.test(s)) return false; return JS_TOKEN.test(s); } // --------------------------------------------------------------------------- // The population itself, with the reasons recorded. // --------------------------------------------------------------------------- export function jsSignals(p) { const reasons = []; const tools = new Set(); const strictTools = new Set(); // `otherToolsMentioned` is deliberately excluded: it is the merely-mentioned // list, and a tool a paper name-drops is not a tool it analysed with. for (const t of p.tools) { if (t.usedOrMentioned !== 'used' && t.usedOrMentioned !== 'produced') continue; const fam = foldJsTool(t.name); if (fam) tools.add(fam); const strict = foldJsToolStrict(t.name); if (strict) strictTools.add(strict); } if (strictTools.size) reasons.push('tool'); if (p.detection.some(detectionObjectIsJs)) reasons.push('detection'); if (p.classification.some(classificationNamesJs)) reasons.push('classification'); return { reasons, tools: [...tools], strictTools: [...strictTools] }; } // Read by hand from the automatic population and removed, with the reason. The // automatic rule cannot tell "the script is what I measured" from "a script is // how I delivered my attack" or "I compiled my system to WebAssembly". export const HAND_EXCLUDED = new Map([ ['CCS/2019/fallout-leaking-data-on-meltdown-resistant-cpus', 'JS is the attack delivery vector for a CPU side channel, not the object of study'], ['IEEE-SP/2019/spectre-attacks-exploiting-speculative-execution', 'same: JS as an exploit vector for a microarchitectural attack'], ['IEEE-SP/2019/ridl-rogue-in-flight-data-load', 'same'], ]); // Prime Match (Emscripten), Tangible Web (Emscripten/WebXR) and Glowing in the // Dark (OpenWPM as a plain crawler) were also read and rejected; they are not // listed here because narrowing the tool signal already removes them, and a // hand rule that fires on nothing is a rule that hides a change in the code. export function paperKey(p) { return `${p.venue}/${p.year}/${p.slug}`; } // A JavaScript-analysis paper measures the *web* platform and shows at least // one signal. The web restriction is what separates this from npm/Node.js // supply-chain work, which uses the same parsers on a different object; that // slice is reported separately rather than folded in. export function isJsPaper(p) { if (!p.platforms.includes('web')) return false; if (HAND_EXCLUDED.has(paperKey(p))) return false; return jsSignals(p).reasons.length > 0; } // --------------------------------------------------------------------------- // Research families. `detection.phenomenon` is ~20% reproducible run-to-run, // so this yields a RANKING, never a precise percentage. Ordered; first match // wins, because the discriminating word is usually the qualifier: "tracking // script" and "malicious script" share the noun and share no method. // --------------------------------------------------------------------------- export const JS_FAMILIES = [ [ /xss|cross.?site scripting|sanitiz|taint|\bcsp\b|content security policy|injection (flaw|vulnerab)|dom.?based|clickjack|postmessage|same.?origin|sop\b|prototype pollution|client.?side (validation|vulnerab)/i, 'Client-side vulnerabilities (XSS, CSP, taint flows)', ], [ /malicious|malware|drive.?by|exploit kit|infect|compromis|web shell|phish|scam|blackhat seo|doorway|cloak|evasi|obfuscat|deceptive/i, 'Malicious-script and cloaking detection', ], [ /fingerprint/i, 'Fingerprinting-script detection', ], [ /track(ing|er)|advertis|\bads?\b|adblock|ad.?block|analytics|session replay|beacon|pixel|third.?party (script|code|inclusion|domain|js)|behavioural|behavioral profil|targeted advert/i, 'Tracking- and advertising-script classification', ], [ /crypto.?(jack|min)|mining|coinhive|monero/i, 'Cryptojacking', ], [ /webassembly|\bwasm\b/i, 'WebAssembly (non-mining)', ], [ /librar|dependenc|supply chain|\bsri\b|subresource integrity|inclusion|cdn|version|outdated|framework usage|npm|package/i, 'Third-party libraries, inclusion and supply chain', ], [ /leak|\bpii\b|exfiltrat|personal (data|information)|email address|password field|form/i, 'Data leakage by scripts', ], [ /extension|content script|add.?on/i, 'Browser-extension scripts', ], [ /web api|browser api|api (usage|access|call)|feature usage|javascript (feature|propert|api)|getcomputedstyle|sensor|permission/i, 'Web-API usage measurement', ], [ /performance|overhead|latency|energy|page load|benchmark|coverage|bloat|unused|dead code|size/i, 'Script performance, size and dead code', ], [ /update|evolution|churn|change|longitudinal|drift|dynamic (script|javascript)|hash/i, 'Script change and identity over time', ], ]; export function foldJsFamily(text) { if (!text) return null; for (const [rx, label] of JS_FAMILIES) if (rx.test(text)) return label; return null; } // --------------------------------------------------------------------------- // Ground-truth sources for a script-classification task. 351 distinct strings // across the population, so again a RANKING. Ordered; first match wins, with // the named external resources ahead of the generic "manual" phrasings, // because "manual validation and known ad-domain filter lists" is both. // --------------------------------------------------------------------------- export const GT_FAMILIES = [ [/easylist|easyprivacy|adblock plus|ublock|adguard|disconnect|filter.?list|filter rules|blocking rules|brave ad ?block|block ?list|blacklist|whotracks|ghostery|webxray|tracker radar|fanboy|smartfilter|fortiguard|web filter|cookiepedia|advertising.?(domain|company) (list|pattern)|ad-related domain|ad-?network url/i, 'Filter list or tracker database'], [/virustotal|safe.?browsing|phishtank|malware scanner|av (engine|scanner)|capture-hpc|apwg|openphish|clamav|wepawet|phoneyc|security essentials|exploit.?kit|malicious and benign|known-good and known-bad/i, 'Malware / phishing blacklist service'], [/retire\.?js|\bcve\b|vulnerability (report|database|repositor)|snyk|npm advisory|nvd\b/i, 'Vulnerability database'], [/mdn|webidl|standards document|w3c|chrome.?idl|specification/i, 'Spec or documentation'], [/wappalyzer|library signature|library catalog|catalogue/i, 'Library signature catalogue'], [/prior work|previous stud|\[[0-9]+\]|et al\.|published dataset|existing dataset|hussain/i, 'Prior published dataset or labels'], [/crowdwork|mechanical turk|prolific|participants|students|domain expert|annotators/i, 'Recruited or external annotators'], [/manual|authors|by hand|inspect|researcher|single-researcher|survey and validation|hand-label/i, "Authors' own manual inspection"], [/synthetic|injected|generated|simulated|controlled|honeyclient|honeypot|seeded|poc|proof.?of.?concept|testbed/i, 'Synthetic or seeded ground truth'], ]; export function foldGroundTruth(s) { if (!s) return null; for (const [rx, label] of GT_FAMILIES) if (rx.test(s)) return label; return null; }
Methodology and limitations of these figures
- Seven venues only. CCS, IMC, NDSS, PETS, USENIX Security, TheWebConf, IEEE S&P, 2010–2026, with 2025 and 2026 provisional. EuroS&P, ACSAC, RAID, AsiaCCS, CHI and SOUPS are absent entirely — for this topic ACSAC and EuroS&P are a real hole, since a good deal of web-script security work lands there. Every claim here is a claim about those seven venues.
- 206 is a floor, and it has a false-positive tail. Papers whose extraction never names a script as the object of detection are missing; conversely a handful of papers in the population (an e-voting client audit, a router-attack paper, a PHP injection-sink study) analyse JavaScript incidentally. The report script prints the full list so you can judge.
- Not every field can carry a percentage.
crawlConfig.*,legal.lawandplatformsreproduce to within a few points on a repeat extraction and carry the figures here.classification.methodagrees on only 58% of papers between two runs of the same schema over the same text, so its table above is a rough share, not a precise figure — a repeat extraction moves those rows.detection.phenomenon,classification.resourceNameandgroundTruthSourceagree on roughly 20% of exact strings, which is what the folding is for and why the family and ground-truth tables print their residue. - Silence is not absence. “Does not state whether it ran headless” means the paper did not say. These are reporting figures, not practice figures.
- Every quoted figure was checked against the paper's own text. The prevalence values in the extraction are model summaries, so each number reproduced on this page was re-located in
paper.cols.txtafter whitespace normalisation. The dataset's own “0.9% of quotes cannot be located” figure was measured on the earlier 4,322-paper run and has not been re-measured.
Open Questions
- No public, hand-labelled corpus of tracking scripts exists. Every current method builds its own labels from filter lists plus manual inspection, which is why cross-paper comparison is impossible. A shared benchmark would do for this field what EasyList did for request blocking.
- LLM-based script classification is unmeasured. No peer-reviewed paper found as of 2026-08-06. The obvious study — LLM against WebGraph, AdFlush and NoT.js on a fixed script corpus, reporting cost and version drift as well as F1 — has no baseline yet.
- Nobody has measured how much a headless or containerised crawler under-counts script classification specifically. [33Jueckstock, Jordan; Sarker, Shaown; Snyder, Peter; Beggs, Aidan; Papadopoulos, Panagiotis; Varvello, Matteo; Livshits, Benjamin; Kapravelos, Alexandros (2021): "Towards Realistic and Reproducible Web Crawl Measurements", in: Proceedings of the ACM Web Conference. (DOI)] and [26Annamalai, Meenatchi Sundaram Muthu Selva; Bilogrevic, Igor; Cristofaro, Emiliano De (2024): "FP-Fed: Privacy-Preserving Federated Detection of Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)] show the gap exists for API traces and fingerprinting scripts; its size for tracking-script prevalence at scale is unknown.
- Function-granularity blocking has no successor paper. NoT.js [5Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] and ByteDefender [27Nikkhah Bahrami, Pouneh; Cutler, Dylan; Bilogrevic, Igor (2025): "Byte by Byte: Unmasking Browser Fingerprinting at the Function Level Using V8 Bytecode Transformers", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)] both stop at detection plus surrogate generation; nobody has measured what happens when either is deployed to real users at scale, or whether trackers adapt.
- Cross-platform divergence is a confound in every older result. If 20.6% of scripts execute differently by platform [29Zafar, Ahsan; Su, Junhua; Datta, Sohom; Kapravelos, Alexandros; Das, Anupam (2025): "Same Script, Different Behavior: Characterizing Divergent JavaScript Execution Across Different Device Platforms", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)], every desktop-only prevalence figure in this page's tables is a measurement of the desktop path only. Re-running any of them on mobile is a well-defined study.
Related Pages
- Requests — classifying at the request/URL layer, where filter lists live, and why they cap this page's ground truth.
- Cookies — what the scripts write; the provenance argument (a cookie set by a blocked resource) is the same idea one layer down.
- Fingerprinting — 39.8% of browser-fingerprinting papers are really detecting scripts, so that page and this one share a method.
- Stateful stateless — only 27.6% of these papers state it, and a stateless crawl sees first-visit script behaviour only.
- Website classification — where script classification sits in the wider taxonomy.
- Crawling location — the vantage-point half of the “the site served you different code” problem.
- Ethics — forced execution, deobfuscation and stealth crawling all override a site operator's expressed preferences.
References
- [1]
- Skolka, Philippe; Staicu, Cristian-Alexandru; Pradel, Michael (2019): "Anything to Hide? Studying Minified and Obfuscated Code in the Web", in: Proceedings of the ACM Web Conference. (DOI)
- [2]
- Sarker, Shaown; Jueckstock, Jordan; Kapravelos, Alexandros (2020): "Hiding in Plain Site: Detecting JavaScript Obfuscation through Concealed Browser API Usage", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
- [3]
- Amjad, Abdul Haddi; Saleem, Danial; Gulzar, Muhammad Ali; Shafiq, Zubair; Zaffar, Fareed (2021): "TrackerSift: untangling mixed tracking and functional web resources", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
- [4]
- Amjad, Abdul Haddi; Shafiq, Zubair; Gulzar, Muhammad Ali (2023): "Blocking JavaScript Without Breaking the Web: An Empirical Investigation", in: Proceedings on Privacy Enhancing Technologies. (DOI)
- [5]
- Amjad, Abdul Haddi; Munir, Shaoor; Shafiq, Zubair; Gulzar, Muhammad Ali (2024): "Blocking Tracking JavaScript at the Function Granularity", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [6]
- Smith, Michael; Snyder, Peter; Livshits, Benjamin; Stefan, Deian (2021): "SugarCoat: Programmatically Generating Privacy-Preserving, Web-Compatible Resource Replacements for Content Blocking", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [7]
- Lekies, Sebastian; Stock, Ben; Wentzel, Martin; Johns, Martin (2015): "The Unexpected Dangers of Dynamic JavaScript", in: Proceedings of the USENIX Security Symposium. (Link)
- [8]
- Rack, Jeremy; Staicu, Cristian-Alexandru (2023): "Jack-in-the-box: An Empirical Study of JavaScript Bundling on the Web and its Security Implications", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [9]
- Kupoluyi, Jesutofunmi; Chaqfeh, Moumena; Varvello, Matteo; Coke, Russell; Hashmi, Waleed; Subramanian, Lakshmi; Zaki, Yasir (2022): "Muzeel: assessing the impact of JavaScript dead code elimination on mobile web performance", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
- [10]
- Wang, Xinzhe; Zhuang, Zeyang; Meng, Wei; Cheng, James (2024): "Detecting and Understanding Self-Deleting JavaScript Code", in: Proceedings of the ACM Web Conference. (DOI)
- [11]
- Soni, Pratik; Budianto, Enrico; Saxena, Prateek (2015): "The SICILIAN Defense: Signature-based Whitelisting of Web JavaScript", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [12]
- Lauinger, Tobias; Chaabane, Abdelberi; Arshad, Sajjad; Robertson, William; Wilson, Christo; Kirda, Engin (2017): "Thou Shalt Not Depend on Me: Analysing the Use of Outdated JavaScript Libraries on the Web", in: Proceedings of the Network and Distributed System Security Symposium. (Link)
- [13]
- Vekaria, Yash; Beugin, Yohan; Munir, Shaoor; Acar, Gunes; Bielova, Nataliia; Englehardt, Steven; Iqbal, Umar; Kapravelos, Alexandros; Laperdrix, Pierre; Nikiforakis, Nick; Polakis, Jason; Roesner, Franziska; Shafiq, Zubair; Zimmeck, Sebastian (2025): "SoK: Advances and Open Problems in Web Tracking". arXiv preprint arXiv:2506.14057. (Link)
- [14]
- Rieder, Wolf; Raschke, Philip; Cory, Thomas; Sechting, Christian René; Kumar, Aditya; Küpper, Axel (2026): "SoK: After Decades of Web Tracker Detection, What's Next?", in: Proceedings of the IEEE Symposium on Security and Privacy. (Link)
- [15]
- Ikram, Muhammad; Asghar, Hassan Jameel; Kaafar, Mohamed Ali; Mahanti, Anirban; Krishnamurthy, Balachander (2017): "Towards Seamless Tracking-Free Web: Improved Detection of Trackers via One-class Learning", in: Proceedings on Privacy Enhancing Technologies. (DOI)
- [16]
- Englehardt, Steven; Narayanan, Arvind (2016): "Online Tracking: A 1-million-site Measurement and Analysis", in: Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security, pp. 1388–1401. Association for Computing Machinery, New York, NY, USA. (DOI) (Link)
- [17]
- Jueckstock, Jordan; Kapravelos, Alexandros (2019): "VisibleV8: In-browser Monitoring of JavaScript in the Wild", in: Proceedings of the ACM Internet Measurement Conference. (DOI)
- [18]
- Iqbal, Umar; Snyder, Peter; Zhu, Shitong; Livshits, Benjamin; Qian, Zhiyun; Shafiq, Zubair (2020): "AdGraph: A Graph-Based Approach to Ad and Tracker Blocking", in: 2020 IEEE Symposium on Security and Privacy (SP), pp. 763-776. (DOI)
- [19]
- Siby, Sandra; Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair; Troncoso, Carmela (2022): "WebGraph: Capturing Advertising and Tracking Information Flows for Robust Blocking", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2875-2892. USENIX Association, Boston, MA. (Link)
- [20]
- Iqbal, Umar; Wolfe, Charlie; Nguyen, Charles; Englehardt, Steven; Shafiq, Zubair (2022): "Khaleesi: Breaker of Advertising and Tracking Request Chains", in: 31st USENIX Security Symposium (USENIX Security 22), pp. 2911-2928. USENIX Association, Boston, MA. (Link)
- [21]
- Iqbal, Umar; Englehardt, Steven; Shafiq, Zubair (2021): "Fingerprinting the Fingerprinters: Learning to Detect Browser Fingerprinting Behaviors", in: Proceedings of the IEEE Symposium on Security and Privacy. (DOI)
- [22]
- Ghasemisharif, Mohammad; Polakis, Jason (2023): "Read Between the Lines: Detecting Tracking JavaScript with Bytecode Classification", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [23]
- Lee, Kiho; Lim, Chaejin; Jin, Beomjin; Kim, Taeyoung; Kim, Hyoungshick (2024): "AdFlush: A Real-World Deployable Machine Learning Solution for Effective Advertisement and Web Tracker Prevention", in: Proceedings of the ACM Web Conference. (DOI)
- [24]
- Boussaha, Soumaya; Hock, Lukas; Bermejo, Miguel; Rumín, Rubén Cuevas; Rumín, Ángel Cuevas; Klein, David; Johns, Martin; Compagna, Luca; Antonioli, Daniele; Barber, Thomas (2024): "FP-tracer: Fine-grained Browser Fingerprinting Detection via Taint-tracking and Entropy-based Thresholds", in: Proceedings on Privacy Enhancing Technologies. (DOI)
- [25]
- Kanyal, Rahul; Sarangi, Smruti R. (2024): "PanoptiChrome: A Modern In-browser Taint Analysis Framework", in: Proceedings of the ACM Web Conference. (DOI)
- [26]
- Annamalai, Meenatchi Sundaram Muthu Selva; Bilogrevic, Igor; Cristofaro, Emiliano De (2024): "FP-Fed: Privacy-Preserving Federated Detection of Browser Fingerprinting", in: Proceedings of the Network and Distributed System Security Symposium. (Link)
- [27]
- Nikkhah Bahrami, Pouneh; Cutler, Dylan; Bilogrevic, Igor (2025): "Byte by Byte: Unmasking Browser Fingerprinting at the Function Level Using V8 Bytecode Transformers", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [28]
- 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)
- [29]
- Zafar, Ahsan; Su, Junhua; Datta, Sohom; Kapravelos, Alexandros; Das, Anupam (2025): "Same Script, Different Behavior: Characterizing Divergent JavaScript Execution Across Different Device Platforms", in: Proceedings of the ACM SIGSAC Conference on Computer and Communications Security. (DOI)
- [30]
- Lukić, Karlo; Papadopoulos, Lazaros (2026): "Privacy vs. Profit: The Impact of Google's Manifest Version 3 (MV3) Update on Ad Blocker Effectiveness", in: Proceedings on Privacy Enhancing Technologies. (Link)
- [31]
- Snyder, Peter; Vastel, Antoine; Livshits, Ben (2020): "Who Filters the Filters: Understanding the Growth, Usefulness and Efficiency of Crowdsourced Ad Blocking", Proc. ACM Meas. Anal. Comput. Syst. 4(2). (DOI) (Link)
- [32]
- Pantelaios, Nikolaos; Kapravelos, Alexandros (2024): "FV8: A Forced Execution JavaScript Engine for Detecting Evasive Techniques", in: Proceedings of the USENIX Security Symposium. (Link)
- [33]
- Jueckstock, Jordan; Sarker, Shaown; Snyder, Peter; Beggs, Aidan; Papadopoulos, Panagiotis; Varvello, Matteo; Livshits, Benjamin; Kapravelos, Alexandros (2021): "Towards Realistic and Reproducible Web Crawl Measurements", in: Proceedings of the ACM Web Conference. (DOI)
uBlockOrigin/uBlock-scriptlets repository returns 404. Checked 2026-08-06.oxc; the model only proposes names.