Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Notable changes to JevGate. Versions follow [Semantic Versioning](https://semver

## [Unreleased]

Measured on 103 pinned projects (24 new open-source ones of kinds not tried before, among them intentionally vulnerable Rails, Node, GraphQL, C# and Java apps, a Deno framework, a WordPress plugin, a cookiecutter template and projects in Kotlin, Swift, Elixir and C, and 8 more of the maintainer's own), with findings labeled by hand: on the 70 labeled projects JevGate was tuned on, 75% of reviews were right against 69% with 0.20.0 (136 wrong reviews against 192), and 72% of considers against 65% (254 wrong considers against 354); on 11 held-out projects, 61% of reviews against 57%, and 56% of considers against 54%. Undecided units went from 2.2% to 1.5% of judged units.
Measured on 103 pinned projects (24 new open-source ones of kinds not tried before, among them intentionally vulnerable Rails, Node, GraphQL, C# and Java apps, a Deno framework, a WordPress plugin, a cookiecutter template and projects in Kotlin, Swift, Elixir and C, and 8 more of the maintainer's own), with findings labeled by hand: on the 72 labeled projects JevGate was tuned on, 76% of reviews were right against 69% with 0.20.0 (142 wrong reviews against 192), and 73% of considers against 64% (239 wrong considers against 355); on 11 held-out projects, 63% of reviews against 57%, and 56% of considers against 54%. On 14 projects added after that tuning (9 of the maintainer's own, Online Boutique, a browser extension, a React Native template, a Solidity and a dbt project), 64% of reviews and 65% of considers were right, against 53% and 47%. Undecided units went from 2.2% to 1.5% of judged units.

- Server templates: ERB, EJS, JSP, Handlebars, Mustache, Nunjucks, Twig, Jinja and Go templates, and HTML under `templates/`, `views/`, `layouts/`, `partials/` or `includes/`, are judged. Their inline `<script>` code is parsed with the template's tags blanked and judged as the page's JavaScript in the visitor's browser, its top-level statements by every security rule, as a PHP page script is. Their `template code` is one more unit: each tag that writes request, cookie, session or signed-in-user data unescaped (ERB `raw` and `html_safe`, `<%== … %>`, EJS `<%- … %>`, `{{{ … }}}`, `|safe`, `|raw`), judged by injection, and every scriptlet and declaration of a JSP page once one reads the request, judged by every security rule. A template holding neither is not selected, so 900 of the corpus's 1,003 templates stay out of reports. On the intentionally vulnerable apps, 24 documented vulnerabilities that no rule read are found: RailsGoat's and DVNA's three XSS each, DVGA's paste-page XSS, DVJA's reflected XSS, and JavaVulnerableLab's JSP-only SQL, HQL and command injections, XSS, path traversal, SSRF and leaked stack traces. Of the labeled findings in templates, 42 of 49 reviews and 16 of 25 considers were right. About $0.1 on the corpus.
- Node views: a handler that renders a view by name (`res.render('app/products', …)`, a view under `views/` named without its extension) is sent the view's lines that write values unescaped (EJS `<%- … %>`, Handlebars `{{{ … }}}`, Pug `!=`, Nunjucks's and Swig's `|safe`), as a Django view is sent its templates, and its presence question, markup check and markup Choice name such templates. DVNA's reflected XSS in the product search is a review and the stored XSS of its product list a consider; only such handlers' requests change.
Expand All @@ -29,6 +29,12 @@ Measured on 103 pinned projects (24 new open-source ones of kinds not tried befo
- Documentation: Claude Code skills, commands and subagent definitions (Markdown under `.claude/skills`, `.claude/commands` and `.claude/agents`) are project documentation, checked for stale paths, repetition and size; a session loads only their descriptions, so they do not count toward what loads at its start. One project's skill cited documentation paths a rename had removed, which nothing reported. A path holding a `$` placeholder, such as a command's `.kiro/specs/$1/spec.json`, names no file.
- Project templates: a file under a directory whose name holds a `{{ … }}` placeholder, as a cookiecutter template's `{{cookiecutter.project_slug}}` does, is parsed without its Jinja tags (statements and comments blanked, each placeholder read as a name of the same length, so lines stay the file's) and judged; the evidence keeps the tags. 31 of cookiecutter-django's Python and JavaScript files, the generated application's settings, models, views and tests, were skipped for syntax errors and are judged; its Celery settings turning off Redis certificate checks (`ssl.CERT_NONE`) are a review.
- Tests: a test said to assert internal details is asked, with the bodies of the functions it calls, what its assertions read: results, state the program shows or acts on next, or effects a caller observes clear the consider; stored input or calls between the program's own functions keep it. Asked of the test and the signatures it calls, the check read a debug panel's recorded queries (`panel._queries`, which the panel renders), Devise's documented hooks and an app's state after an action as internals: 49 of 66 such considers labeled on the corpus were wrong. A test that reads members through reflection or a cast to `any` keeps its consider without being asked. Labeled tests/value considers went from 29% to 77% right (20 right and 6 wrong, against 22 and 55), and on held-out projects from 2 right and 13 wrong to 1 wrong. About $0.006 of follow-ups on the corpus.
- Injection: a consider that rests on the function's parameters ("a caller passing outside input would make it exploitable") is asked, with the functions that call it, what the values it places can hold. It becomes a note when text the program fixes (literals its callers pass, numbers, names from a fixed list), values it creates or a local tool's own arguments lead; text another party can set, or callers that are not shown, keep it. Asked where the values come from, the recheck answered "the function's parameters" at 0.9 even for a Rust helper whose four callers pass literal SQL fragments. On the labeled projects, such considers went from 30 right and 51 wrong to 30 right and 32 wrong. Only these follow-ups are asked.
- Hardcoded values: the Choice that names a finding's value lists, with each value, the other lines of its file that write it. A consider that rests only on a value's name, whose file writes that value again, is then asked what the value is: copies that must change together, or a value nothing near it explains, keep it; a value that the field or argument it fills or a comment beside it explains, an idiom such as a tolerance near zero or a unit conversion, or a hand-tuned number, clearly chosen, make it a note. On the labeled projects, such considers went from 53 right and 77 wrong (41%) to 32 right and 28 wrong (53%); the others are notes. Offered instead as the locate's "no value stands out", those kinds also took values with copies that must stay equal. Only these follow-ups are asked.
- Hardcoded values: a review or consider that rests only on a value changing between environments is asked, once its value or constant is named and with the function or the lines that use the constant, where that value would differ. A value each installation must set, or the author's own account or domain, keeps it; the same value in every copy on purpose (the program's own service or registered client id, a provider's fixed address, a path the platform fixes), a fallback used only when configuration gives none, or code no deployment runs, at 0.80, make it a note. The Choice that names a file's constant lists the lines that use each one. On the labeled projects, such findings went from 17 right and 36 wrong to 15 right and 19 wrong: a desktop app's own license server, a provider's registered redirect and fallbacks behind environment variables are notes; a frontend's API host edited in code three times and a template author's domain as a fallback are the right ones lowered. Only these follow-ups are asked.
- Injection: outside PHP, a path check left undecided after its trace and recheck is asked, with the functions that call it, where the paths come from: the program's own directories or configuration, the command line or settings of the person running a local program, a whole path its caller gives, or another party's input (a request, an uploaded file or archive entry, a record users can edit). The program's own or the local user's clear it. A path, URL or redirect check still undecided on the function's parameters, whose Choice does not lean toward another party's input, is a note naming the path, URL or redirect, as a found one already was. Undecided injection units on the corpus went from 813 to 289, and no review or consider changed; the new Choice costs about $0.02 on the corpus.
- Tests: a test whose "checks only its mocks" or "recomputes its expected value" answer stays undecided after its recheck leans: below 0.50 it is clear. Labeled from the code, 4 of 43 such tests below 0.50 were hollow, against 10 of 35 at 0.50 or more. Undecided tests on the corpus went from 792 to 156, and undecided units overall from 1.39% to 0.83% of judged units. Nothing is asked again.
- Unsafe settings: outside C#, PHP and Django, the TLS check asks whether code accepts a server's certificate, host key or host name without verifying it, and names a connection made without TLS (gRPC's insecure credentials, an insecure OpenTelemetry exporter, `sslmode=disable`) and verification skipped only when a caller or the operator asks for it as not turning it off. Asked whether code turns verification off, Online Boutique's gRPC clients and telemetry exporters inside its cluster, and its database reached through the AlloyDB connector, were seven reviews whatever the examples said, and httpx's `verify=False` branch and vaultwarden's opt-in setting for invalid SMTP certificates were reviews too. The escape check names i18next's `escapeValue: false` in a React app, whose components escape what they render, as escaping kept. Labeled unsafe-settings reviews went from 54 right and 24 wrong to 54 right and 14 wrong; pgweb defaulting a bookmark's `sslmode` to `disable`, which turns off TLS rather than verification, is no longer one. Only unsafe-settings traces are asked again.
- Tests: Deno tests are test cases, in each of their forms: `Deno.test("name", fn)`, `Deno.test({ name: "name", fn() {…} })` and `Deno.test(function name() {…})`, with `.only` and `.ignore`. oak writes its 266 tests in the object form, and none of them was judged: its test files got a file-purpose request each and the test rules found nothing to ask. Only Deno projects' requests change.

## [0.20.0] - 2026-09-26
Expand Down
48 changes: 43 additions & 5 deletions docs/classification-cascade.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ signatures, or one candidate pair.
its mocks is asked again with the bodies of the functions it calls and its
file's imports, mocks and setup hooks (a part too long is left out, never
cut); each answer replaces the first unless only the first is decisive.
One still undecided then leans: below 0.50 it is clear. Labeled from the
code, 4 of 43 such tests below 0.50 were hollow, against 10 of 35 above.
A Ruby test is sent with the groups it is declared in, since an RSpec
example reads as a sentence continuing them and the outer group often names
the class under test. Its recheck shows, instead of every hook of the
Expand Down Expand Up @@ -190,7 +192,20 @@ signatures, or one candidate pair.
again with the body of the function both call: whether it throws before
the rest of a test runs is in that body.
Then one locate Choice per split finding picks the body block to extract,
and one per hardcoded-value review or consider names the value it is about.
and one per hardcoded-value review or consider names the value it is about,
each value listed with the other lines of its file that write it. A
consider that rests only on a value's name, whose file writes that value
again, is then asked what the value is: copies that must change together,
or a value nothing near it explains, keep it; a value that the field or
argument it fills or a comment beside it explains, an idiom such as a
tolerance near zero or a unit conversion, or a hand-tuned number make it a
note. A finding that rests only on a value changing between environments
is asked, with the value's function or the lines that use the constant,
where it would differ: each installation or the author's own account
keeps it; the same in every copy on purpose (the program's own service, a
provider's fixed address, a path the platform fixes), a fallback used only
when configuration gives none, or code no deployment runs, at 0.80, make it
a note.
Special-case findings in different files that name the same identity
become one finding at the strongest site; the others are notes pointing at
it. Numbers and paths are not grouped: `1000` meant metres per kilometre in
Expand All @@ -202,7 +217,14 @@ signatures, or one candidate pair.
`eval` of model output; a check per kind decides and names the kind. An
injection whose origin stays unclear or is the function's parameters is
asked its origin and checks again with up to three callers; its answer
replaces the traced one unless only the traced one is decisive.
replaces the traced one unless only the traced one is decisive. A consider
that still rests on the function's parameters is asked, with its callers,
what the values it places can hold: text the program fixes (literals its
callers pass, numbers, names from a fixed list), values it creates itself,
or a local tool's own arguments make it a note; text another party can set,
or callers not shown, keep it. Asked where the values come from, the
recheck answered "the function's parameters" at 0.9 even for a helper
whose every caller passed a literal SQL fragment.
The markup check names text shown as a JSX child and CSS values or class
names as escaped or inert; sending where each built string goes did not
settle React units, the examples did. A sensitive-data trace lists the
Expand Down Expand Up @@ -244,7 +266,16 @@ signatures, or one candidate pair.
trace in C# also gets the `const` and `static readonly` fields the code
names, often declared in another file, so a key written in the code does
not read as configuration. Other languages keep their wording: the
additions were measured on ASP.NET Core projects only. A broad weak-setting
additions were measured on ASP.NET Core projects only. Outside C#, PHP and
Django, the TLS check asks whether code accepts a server's certificate,
host key or host name without verifying it, so a connection made without
TLS (gRPC's insecure credentials, an insecure OpenTelemetry exporter,
`sslmode=disable`) presents nothing to verify, and verification skipped
only when a caller or the operator asks for it is not turned off: asked
whether code turns verification off, a microservices demo's
cluster-internal gRPC clients stayed reviews whatever the examples said.
The escape check names i18next's `escapeValue: false` in a React app as
escaping kept. A broad weak-setting
answer that none of the specific checks leans toward names no setting to
change and is at most a note: on an action marked `[AllowAnonymous]` on
purpose it was 0.85 while every check stayed at 0.30 or less.
Expand Down Expand Up @@ -343,8 +374,15 @@ signatures, or one candidate pair.
numbers, or values handed to it) was tried for considers on parameters
and dropped: it cleared a sort column taken from the request as readily
as clauses with placeholders. The same question about paths cleared real
traversals, reading names stored in an index as the program's own, so path
checks stay undecided until callers show more. The SQL check counts
traversals, reading names stored in an index as the program's own. Outside
PHP, an undecided path check is now asked, with its callers, where the
paths come from, naming a record users can edit as another party's input
and offering the local user's command line or settings: the program's own
paths or the local user's at 0.80 clear it (74 units on the corpus, among
them httprouter serving a directory through `http.Dir`). A path, URL or
redirect check still undecided on the function's parameters, whose Choice
does not lean toward another party's input, is a note, as a found one
already was: 450 injection units had stayed uncertain that way. The SQL check counts
identifiers quoted by doubling embedded quotes as handled (identifiers
cannot be bound), and the URL check excludes requests a web page sends from
the user's browser; on fresh repositories both had flagged such code, while
Expand Down
Loading
Loading