Why this matters
For its first thirteen issues this journal described a Python benchmark, and the most common question we were asked was whether any of it held for JavaScript. It is a fair question. TypeScript and JavaScript are the most widely deployed languages on the web, they have their own vulnerability idioms — prototype pollution, template injection in server-rendered frameworks, client-side XSS through framework escape hatches — and a scanner that is good at Django is not thereby good at Next.js. A benchmark that wants to say anything general about vulnerability scanners has to test more than one language, and it has to do so without letting the second language quietly distort the results of the first.
What we did
RealVuln 3.0.0 adds 74 TypeScript/JavaScript applications to the unchanged 66-repository Python corpus, for 140 in total. Twenty-four are community applications — OWASP Juice Shop, NodeGoat, DVNA, DVWS and others — pinned by commit SHA to forks we control so the pins cannot drift. Fifty are company-style applications generated end-to-end by coding agents (Claude Opus 4.7, GPT-5.5 at x-high effort, Kimi K2.6, DeepSeek V4 Pro and DeepSeek V4 Flash, ten each) across five stacks: Express, Next.js, NestJS with Angular, Remix, and Fastify with Vue. Each generated app was then seeded with reviewed vulnerabilities and published as a single-snapshot public repository. The review produced 2,236 scoring vulnerabilities, bringing the dataset to 4,138, plus 176 non-scoring locations described in issue 14. The 280 false-positive traps are still Python-only; we say so rather than imply otherwise.
Two measurement details changed with the language. Lines of code are now counted for every repository from its pinned tree, excluding vendored third-party libraries checked into asset directories — a bundled three.js or jQuery is not code the repository's authors wrote, and counting it would make the TS/JS corpus look four times larger than it is. On that basis the corpus is 741,034 lines, 607,252 of them TS/JS; Juice Shop alone is 45,003. One asymmetry remains and is documented on the dataset page: the Python count covers .py files only, so a Django or Flask application's HTML templates are not counted, whereas a React or Angular application's component code is. Per-line cost figures should be read with that in mind.
The bigger change is to the leaderboard itself. Almost every scanner we had published ran on Python only, and putting a 74-repository TS/JS score in the same column as a 66-repository Python score would be a comparison between different exams. The site now has language tabs — Overall, Python, TypeScript / JS — crossed with the existing authorship tabs. The rule is simple and strict: the Overall tab ranks only scanners that have covered every repository in every language, and a run that covered one language appears on that language's tab and nowhere else. A scanner is never scored on code it did not see. Cost is computed per tab too, so the Python tab prices a scanner on the Python code it scanned, not on the whole corpus.
What we observed
Nine scanners have covered both languages at the time of writing: Kolega DevSec Max V0.1.0, Daybreak Blue, GPT-5.6 Sol, GLM-5.3, GPT-6 Astra, Claude Sonnet 5, DeepSeek V4 Flash, DeepSeek V4 Pro and Semgrep. That is enough for the Overall tab to mean something, and not yet enough to call it a full leaderboard: most of the agentic LLM scanners we ran on Python have not yet run on the 74 new repositories, and they sit on the Python tab until they do.
The Python results did not move. Same 28 scanners, same true- and false-positive counts, same F3 to the decimal as 2.1.0 — which was the point of leaving that corpus untouched, and which we verified by diffing the frozen 2.1.0 data against the new build. One label changed: the Python results published in 2.1.0 under Kolega DevSec Max V0.0.1 now sit under V0.1.0, because the TS/JS run is the same scanner and its Python path did not change. The numbers are identical and the old slug stays in the frozen 2.1.0 release.
What did move is how scanners behave when the language changes, and that is a large enough subject to be its own issue.
Why we think that happened
The leaderboard split is a consequence we accepted rather than one we wanted. Everyone asks for a single overall number, and with two languages and unequal coverage a single number would have been misleading — it would have rewarded whichever scanners happened to have run on the easier language. Restricting the Overall table to full-coverage runs and showing the per-language tables alongside costs a click and buys an honest comparison. We would rather have a leaderboard that is slightly harder to read than one that is slightly wrong.
What's next
False-positive traps for TypeScript/JavaScript are the next ground-truth job, so that precision on the new corpus is measured the same way it is on Python. Java is the next language, and it will arrive the same way this one did: as its own tab, ranked separately until enough scanners have covered it to earn a place on Overall. 3.0.0 is frozen at realvuln.com/v/3.0.0/ and every figure here can be regenerated from the repository.