Language support
Every language we read, and every one we do not.
This is the page that answers the first question worth asking: how far does the analysis actually go on the code you already have, and what does it not read at all. Depth is stated per language, the limit that bites first is stated with it, and the languages with no parser today are listed by name. You should find that out here, not a week after you install.
- 5 languages parsed
- Cross-file taint up to 4 hops
- Java taint is same-file
- Scans run locally
01 / Depth, not breadth
The depth matrix
A wall of checkmarks tells you nothing, because every scanner claims every language. This is the table the docs carry: each supported language against each analysis capability, with the cells we cannot fill left visible.
| Language | Route mappingentry points | Taint, same filesource to sink | Taint, across filesvia call graph | Framework awarenessrouters, ORMs | Dedicated detectorslanguage rules |
|---|---|---|---|---|---|
| PythonDjango, Flask, FastAPI | Full | Full | Full | Full | Full |
| JavaScriptExpress, Koa, Next.js | Full | Full | Full | Full | Full |
| TypeScriptExpress, NestJS, Next.js | Full | Full | Full | Full | Full |
| Gonet/http, Gin, Echo, chi | Full | Full | Full | Full | Full |
| Javasame-file taint only | Full | Full | None | Full | Full |
| Apexentry points, not URL routes | Partial | Full | Partial | Full | Full |
| Terraform, Dockerfileconfiguration, not dataflow | None | None | None | Full | Full |
- Fullruns on every scan of that language
- Partiallimited to the cases named in the row
- Nonenot analysed at this depth today
Cross-file taint means a source in one file reaching a sink in another through the call graph. Java is same-file today: a tainted value that leaves the method is not followed into the callee's file, so a Java result set is narrower than a Python or Go one on the same codebase.
5
Languages with a parser
Python, JavaScript, TypeScript, Go, Java
4
Cross-file taint hops
Python, JavaScript, TypeScript and Go. Java is same-file only.
150+
Security checks
Shared across the supported languages, plus the configuration and dependency passes.
Read the matrix as a floor rather than a ceiling. A cell marked full means the capability runs on every scan of that language, not that it resolves every case a language can express.
02 / Per language
What each language actually gets
The frameworks the engine understands, and the limit that bites first. The limits are the useful half of this section: they tell you what a clean result does not prove.
Python
cross-file, up to 4 hops
Routes are read from decorators and from URL configuration, so the endpoint inventory is built before any rule runs. Taint follows a request value out of the handler, through helper modules, and into the sink that trusted it, crossing file boundaries through the call graph for up to four hops.
JavaScript
cross-file, up to 4 hops
Router registrations are resolved through their mount prefixes, including across files, so a handler declared in a sub-router is reported at the path it is actually served on rather than the fragment written beside it. Taint crosses module boundaries: a value read from the request in a controller is followed into the service file and the query builder that consume it.
TypeScript
cross-file, up to 4 hops
Treated as its own grammar rather than as JavaScript with the types stripped, which is what lets decorator-driven routing be read directly. The same four-hop cross-file taint applies, and class methods reached through an interface-typed dependency are resolved to their implementations.
Go
cross-file, up to 4 hops
Route paths are resolved even when the pattern is assembled from a variable or concatenated, provided the value can be traced to its literal parts. Taint follows a request value across packages and files for up to four hops, and gRPC service definitions are recognised as entry points alongside HTTP handlers.
Java
same-file taint only
Routing is read from the annotations: a class-level @RequestMapping supplies the base path and the method-level mapping family supplies the verb, so the endpoint inventory and the authorization checks around it are as complete as for any other language. A dedicated Java rule set covers injection, unsafe deserialization, XXE, JNDI, expression-language injection, path traversal and Spring Security configuration.
- 01
checkout.py:214request handlersourceorder_id read from request.args, no type or format check
- 02
services/orders.py:88helperpassed through lookup_order(order_id) unchanged
- 03
db/query_builder.py:41query builderconcatenated into the WHERE clause with an f-string
- 04
db/session.py:57sinksinkcursor.execute(sql) runs the assembled statement
4 hops resolved. No sanitiser between the source and the sink.
Apex is not in this list because it belongs to a different binary. Apex, Lightning components, Flow and org metadata are read by Vulkro for Salesforce, which is this same review pointed at a Salesforce build: the same engine, with a rule set written for the platform rather than for the web.
03 / Monorepos and mixed stacks
One repository, several stacks
Most repositories are not one language. A single primary verdict at the root is the usual reason a backend subtree is never scanned, so the project detector does not stop at the root.
A sibling-language sub-project is registered by its own build marker even when an enclosing project has already declared a different primary language. A repository whose root looks like a Node application still has its Go service registered from go.mod, its Java service from pom.xml or build.gradle, and its Python service from pyproject.toml, setup.py, requirements.txt, Pipfile or poetry.lock.
package.json is deliberately not one of those markers: the Node file family is already collected across the whole tree from the root, so recovering it again would only duplicate work. A nested module of the same language under an outer service of that language is pruned for the same reason, while a genuinely separate service in a disjoint subtree is kept. The walk is depth-capped, respects your ignore files, and is bounded by a hard sub-project cap, so it stays cheap on a large repository.
If another tool reported nothing in your backend, this is usually why. The scan found the root, decided the repository was one language, and never descended to the service that mattered. Check the route inventory first: if your backend endpoints are not in it, no rule ever ran on them, and a clean result means nothing.
487 routes discovered in 3 subprojects (Express, FastAPI, chi). 12 reachable without authentication. Showing 9.
| Method | Path | Subproject | Handler | Auth | Findings |
|---|---|---|---|---|---|
| POST | /api/v1/invoices/{id}/refund | services/billing | routes/invoice.ts:47 | anonymous | HIGH2 |
| GET | /api/v1/invoices/{id} | services/billing | routes/invoice.ts:112 | authenticated | MED1 |
| POST | /api/v1/webhooks/payments | services/billing | routes/webhooks.ts:29 | unknown | MED1 |
| POST | /checkout/confirm | apps/storefront | checkout.py:214 | authenticated | CRIT1 |
| GET | /cart | apps/storefront | api/cart.py:88 | anonymous | no findings |
| GET | /healthz | apps/storefront | api/health.py:12 | anonymous | no findings |
| POST | /v1/session | services/identity | internal/http/session.go:64 | anonymous | no findings |
| DELETE | /v1/session/{sid} | services/identity | internal/http/session.go:118 | authenticated | no findings |
| GET | /internal/admin/users | services/identity | internal/http/admin.go:31 | unknown | CRIT1 |
| Unknown means the handler runs behind a guard Vulkro could not resolve to a session check. It is listed, not scored. | |||||
04 / Not read at all
Languages with no parser today
There is no partial credit here. Where no grammar loads, files are counted in the inventory and skipped, and the report says so rather than reporting a clean subtree.
| Not read | What happens today | Notes |
|---|---|---|
| Ruby | No grammar loads for .rb files. They are walked, counted in the file inventory, and skipped. | Gemfile.lock is still read for the resolved gem list, so Ruby dependencies are inventoried even though no Ruby source is analysed. |
| PHP | No grammar loads for .php files. A PHP subtree inside a supported repository is skipped, not partially analysed. | Framework routing is where most of the value would be, and none of it is modelled today. |
| C# and .NET | No grammar loads for .cs files. ASP.NET routing is not read. | The enum variant exists in the scanner for future use, but no file is ever classified as C#, so no detector runs on one. |
| Kotlin | Kotlin source is not parsed. A Kotlin service in a Spring repository is invisible to the code analysis. | A Gradle build file still registers the subtree as a project, and a gradle.lockfile beside it is parsed for its resolved dependencies. |
| Rust | Rust source is not parsed. No route, taint or detector pass runs on it. | Cargo.toml and Cargo.lock are read, so Rust dependencies are still resolved to exact versions and inventoried. |
| C, C++, Swift, Scala, Elixir and the rest | No grammar, no rules, no partial coverage. Files in these languages are counted and skipped. | A language is listed as supported here only when a grammar loads for its extensions and a rule set exists that is worth running. |
This is a roadmap fact rather than an apology. Loading a grammar is the cheap half of supporting a language; the expensive half is the framework model, the route extraction and the rule set that make the results worth reading. A language ships here when that second half is done, which is why the supported list is short and why the depth on it is worth quoting.
05 / Beyond application code
The files that are not source code
Plenty of what gets exploited was never written in an application language. These passes do not need a grammar for your stack, so they run whatever the repository is written in.
Infrastructure as code
Dockerfiles
Built container images
Dependency manifests
5 manifest formats are parsed (npm, PyPI, Go modules, crates.io, Maven). Five manifest formats are parsed. The default published bundle currently ships npm and PyPI; the wider signed bundle covers Go modules, crates.io and Maven.
- CRITorg.apache.commons:commons-text@1.9MavenCVE-2022-42889reachable
pom.xmlStringSubstitutor.replace called from ReportController.render at src/main/java/com/acme/report/ReportController.java:88
- HIGHlodash@4.17.20npmCVE-2021-23337GHSA-35jh-r3h4-6jhmreachable
package-lock.jsontemplate() called from buildInvoice at routes/invoice.ts:47, two hops from the POST /invoices route
- MEDurllib3@1.26.4PyPICVE-2021-33503unknown
poetry.lockadvisory declares no vulnerable symbols, so no reachability claim is made and the severity is left as matched
- INFOgithub.com/gin-gonic/gin@1.7.7GoCVE-2023-29401unreachable
go.sumContext.FileAttachment is not called from the forward closure of any route, main, or plugin hookdowngraded from medium
- INFOtime@0.1.44crates.ioCVE-2020-26235unreachable
Cargo.lockpulled in transitively by chrono; no project function in the forward closure calls the affected symboldowngraded from high
Unreachable findings are downgraded, not dropped. Severity falls to Info, the row keeps its call-graph reason, and it stays in the report and in the JSON. A call site missed through dynamic dispatch shows up as down-ranked rather than as absent.
Five manifest formats are parsed. The default published bundle currently ships npm and PyPI; the wider signed bundle covers Go modules, crates.io and Maven.