Skip to main content

Vulkro vs Semgrep

A rule engine you extend, or an analysis engine you can score.

Semgrep matches patterns you give it, across a great many languages, with rules you can read. Vulkro builds a route inventory and a call graph first, then reports the paths it can trace, in five languages. That single structural choice decides everything below it, including the run where both tools went through one corpus on one machine with one scoring script.

  • One corpus, one machine
  • Opengrep scored too
  • Every figure carries its date
  • Scans run locally

01 / The short answer

Which one you should pick

Read this section and stop, if you like. Everything under it is the working.

Choose Semgrep if

  • Your stack is mostly languages we do not read. Vulkro parses Python, JavaScript, TypeScript, Go and Java. Semgrep parses far more. If your services are Ruby, PHP, C#, Kotlin, Scala or C, Vulkro reads none of that source, and no amount of depth makes up for a parser that does not exist.
  • You want to read the rule that fired. Semgrep is an open-source engine with a public rule syntax, so the logic behind a finding is a text file you can open, disagree with and fork. Vulkro detectors are closed: you get the finding, the path and the rule id, not the rule.
  • Rule authoring is the job. If your security team maintains detection for your own conventions and wants one syntax across every language it owns, that is the thing Semgrep is built to be and Vulkro is not.
  • You need a scanner with no licence to negotiate. The open-source engine can be adopted without a purchase decision. Vulkro is licensed per seat and issued directly by our team, which is a slower door to walk through.

Choose Vulkro if

  • You want fewer findings to read. On the corpus below Vulkro found 47 of the 83 catalogued bugs against 13 false positives. Semgrep found 17 against 15. Precision is the row a triage rota actually feels.
  • You need a path, not a match. A finding cites the hops from the request to the sink, across files, in Python, JavaScript, TypeScript and Go for up to four hops. Java taint is same-file only, and that limit is real.
  • You want one pass over everything, not one pass over code. Application code, dependency CVEs, secrets, infrastructure configuration and built container images come out of the same scan and the same report, with CVE matching done against a checksummed bundle on your own disk.
  • Nothing may leave the machine. Every scan runs locally with no cloud step at any tier, and VULKRO_OFFLINE=1 enforces that at the process boundary for an air-gapped build.
  • The subject is a Salesforce build. Apex, Lightning components, Flow and org metadata are read by a separate edition, Vulkro for Salesforce: the same review, pointed at a Salesforce org instead of a service codebase.

Plenty of teams should run both, and section 06 says how. These are not the same kind of tool competing for one slot.

02 / The architectural difference

A pattern matcher, and a program model

One structural distinction sits under every row in every table on this page, so it is worth three paragraphs before any number appears.

Semgrep matches shapes. You point it at rules, it parses each file, and it reports where the shape occurs. That is why it supports so many languages: adding one is a grammar plus rules, and the rules are the product. Coverage is therefore something you configure rather than something the tool has.

Vulkro builds a model before it looks for anything. Every scan extracts the route inventory, builds the call graph, then runs taint from the values a request brings in through to whatever consumed them. A finding is emitted when a path exists, and the finding prints that path. That is why the supported list is five languages rather than twenty: the grammar is the cheap half, and the framework model and route extraction are the expensive half.

The trade cuts both ways, and it is worth being blunt about the direction that goes against us. A program model only knows what it modelled. A convention peculiar to your codebase, a sink your own platform team wrote, an internal helper that everybody knows is dangerous: Semgrep can be told about all of those in an afternoon, and Vulkro cannot. What Vulkro gives back is a traced path across files rather than a match in one.

There is a second structural line worth naming. Cross-file analysis in the Semgrep world belongs to the commercial engine, which can also run in Semgrep’s cloud with your source uploaded, and the open-source engine works a file at a time. Vulkro cross-file taint is in the one binary, runs on your machine, and is on by default. Java is the exception on our side and it is a real one: Java taint does not cross a file boundary at all, so a Java result is narrower than a Python or Go one on the same repository.

Opengrep, a fork of the open-source Semgrep engine, went through the same harness and returned the same counts: 17 catalogued bugs found, 15 false positives, precision 0.53 and recall 0.20. Only the wall clock differed. If you are choosing between those two, this benchmark does not separate them, and everything this page says about the Semgrep architecture applies to the fork.

vulkro explain VULK-10424 hops
Dataflow pathSQL injection in order lookupVULK-1042
  1. 01checkout.py:214request handlersource

    order_id read from request.args, no type or format check

  2. 02services/orders.py:88helper

    passed through lookup_order(order_id) unchanged

  3. 03db/query_builder.py:41query builder

    concatenated into the WHERE clause with an f-string

  4. 04db/session.py:57sinksink

    cursor.execute(sql) runs the assembled statement

4 hops resolved. No sanitiser between the source and the sink.

Four hops resolved across four files. This is the shape of evidence a program model can print and a single-file pattern match cannot. In Java the same trace stops at the first file boundary.

03 / Measured

One corpus, one machine, one scoring script

This is the only comparative number on this page. Every tool below ran through the same harness and was scored by the same rule, at the confidence floor we recommend in CI.

Artifactbench/comparison/scorecard-high.md · vulkro 0.18.0 · measured 2026-07-18 · Tier 1 corpus, min-confidence high, match window file + line plus or minus 5

Vulkro, Semgrep and Opengrep scored on the same corpus: catalogued bugs found and missed, false positives, precision, recall, F1 and whole-corpus wall clock.
ToolFound (of 83)MissedFalse positivesPrecisionRecallF1Runtime
Vulkro 0.18.047 of 8336130.780.570.6649.2s
Semgrep 1.169.017 of 8366150.530.200.3039.6s
Opengrep 1.25.017 of 8366150.530.200.3052.9s
Every catalogued bug cites the project’s own vulnerability documentation, an in-source vulnerability marker, or a published CVE or GHSA pinned to the scanned commit, and was confirmed by reading the sink at that commit. Bugs the scanner misses stay in the corpus as false negatives.
  • What the table covers

    83 catalogued bugs in real projects, pinned to a commit, in the languages Vulkro parses. Nothing here scores either tool on a language Vulkro cannot read, which is most of Semgrep’s range. Read the result as a depth comparison on shared ground, not as a verdict on the wider tool.

  • What a false positive means here

    A finding that fired inside a catalogued file at no catalogued line. Findings in files the corpus does not catalogue are counted separately and left out of the metrics for every tool, so neither side is penalised for reporting things the corpus never labelled.

  • Rules decide a Semgrep result

    Semgrep’s output is a function of the rule set it is pointed at. A team that has invested in its own rules should expect a different number from the configuration this harness ran, and the only honest way to find out is to run both over your own tree.

  • Semgrep finished faster

    39.6s against Vulkro’s 49.2s over the whole corpus. Opengrep took 52.9s. At this size the wall clock is not a reason to choose any of the three, and the row is here because leaving it out would be the dishonest option.

The bugs Vulkro missed are in that scorecard too: 36 of 83 were not found in this run, listed by class. They stay in the corpus rather than being retired. Our own weak spots, in our own words.

04 / Where Semgrep wins

The things it does better, without the hedging

A comparison page that cannot name the other tool's real advantages is an advertisement. These are the ones that would make us pick Semgrep.

Breadth

The languages Vulkro does not read

Semgrep parses far more languages than the five Vulkro analyses. A Rails service, a Laravel app, a .NET tier or a Kotlin module is invisible to Vulkro: those files are counted in the inventory and skipped. If that is where your risk lives, the depth argument on this page does not apply to you.

Openness

Rules you can read, change and own

The engine is open source and the rule syntax is public, so the logic that produced a finding is inspectable and forkable. Vulkro detectors are proprietary; what we publish instead is the benchmark method, the finding IDs and the explanation output. On this axis Semgrep is straightforwardly better, and there is no version of our answer that changes it.

Authoring

Writing detection for your own conventions

Semgrep patterns read like the code they match, which is why so many teams write their own. Vulkro custom rules are narrower (regex or AST matching) and are not a substitute for a rule language. A convention specific to your codebase is something Semgrep can be taught in an afternoon and Vulkro cannot.

Consistency

One syntax across a polyglot monorepo

In a repository with six languages, one rule language covering all six is worth a great deal on its own. Vulkro answers the same monorepo by registering each sibling service from its build marker, but only the five parsed languages get analysed at all.

Adoption

It is already wired into your build

Published rule packs, CI templates, pre-commit hooks and editor integrations. If your team has authored rules and wired them in, that investment is a genuine switching cost and should be counted as one rather than waved off.

Speed

It finished the corpus faster than we did

39.6s against 49.2s on the same machine. Both are fast enough to sit in a pull request, so this is not the deciding row, but it is Semgrep’s row and it stays in the table.

05 / Coverage

Capability, side by side

Not a measurement. The table above is the measured part of this page; this one states what each tool is shaped to do, with the gaps on both sides left visible.

Capability comparison between Vulkro and Semgrep, with unassessed cells marked as such.
CapabilityVulkroSemgrep
Languages read for code analysisPython, JavaScript, TypeScript, Go, Java. Everything else is counted and skipped.Many more than five. Breadth is the point of the design.
Cross-file dataflowOn by default in the one binary, up to four hops, for Python, JavaScript, TypeScript and Go. Java taint is same-file only.The open-source engine analyses a file at a time. Cross-file analysis belongs to the commercial engine, which can also run in Semgrep’s cloud.
Route and endpoint inventoryBuilt on every scan before any rule runs, which is what gives the authorization checks something to check against.Whatever the loaded rules match. A rule engine inventories nothing outside its rules.
Dependency CVE matchingIn the same scan, against a bundle on your disk. The bundle is checksummed and matching makes no network call.A separate product from the code scanner.
SecretsIn the same scan, with provider-specific rules.A separate product from the code scanner.
Infrastructure as code and DockerfilesA configuration pass reads Terraform, Kubernetes manifests, Compose files, Helm charts and Dockerfiles for shape.Rule packs exist for configuration files, so coverage is whatever you choose to load.
Built container imagesPackage databases are extracted from the layers, whiteouts applied, and matched offline against the same bundle.Outside what a source pattern engine reads.
Custom rulesRegex or AST matching. Narrower than a rule language, and we do not pretend otherwise.A full rule language, and the strongest part of the tool.
Where the scan runsYour machine, at every tier. VULKRO_OFFLINE=1 enforces it at the process boundary.Your machine for the open-source engine. The commercial platform can run analysis in its cloud, which means source leaves the machine.
Repeat runsDeterministic: same tree, same binary, same findings in the same order, asserted by tests in the build.Pattern matching is deterministic. A rule set pulled from a registry can change between runs, which is a property of the configuration rather than of the engine.
Machine-readable outputSARIF among a wide set of formats.SARIF.
Salesforce (Apex, Flow, org metadata)A separate edition, Vulkro for Salesforce, reads Apex, Lightning components, Flow and org metadata, and audits live org posture.Not assessed. We have not run Semgrep against a Salesforce build, so this page makes no claim about it.
Capability comparison, not a measurement, and no figure in it comes from the benchmark. The Vulkro column is stated from the shipped binary. The Semgrep column is our reading of its published documentation and product structure; where we could not check something ourselves, the cell says it was not assessed rather than guessing.
analysis depth by language7 languages x 5 capabilities
Analysis depth for each supported language, by capability. Each cell is full, partial or none.
LanguageRoute mappingentry pointsTaint, same filesource to sinkTaint, across filesvia call graphFramework awarenessrouters, ORMsDedicated detectorslanguage rules
PythonDjango, Flask, FastAPIFullFullFullFullFull
JavaScriptExpress, Koa, Next.jsFullFullFullFullFull
TypeScriptExpress, NestJS, Next.jsFullFullFullFullFull
Gonet/http, Gin, Echo, chiFullFullFullFullFull
Javasame-file taint onlyFullFullNoneFullFull
Apexentry points, not URL routesPartialFullPartialFullFull
Terraform, Dockerfileconfiguration, not dataflowNoneNoneNoneFullFull
  • 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.

Row one of the table above, in full: what each parsed language actually gets. The empty cells are the point, and the Java row is the one to read.

The honest reading of those two tables together: Semgrep is wider and Vulkro is deeper on the ground they share. If the languages in the matrix are your languages, depth is the thing worth buying. If they are not, breadth wins and this page should not talk you out of it. The full language depth chart.

06 / Running both, or moving across

What it costs to switch, and why most teams should not

Both tools emit SARIF, so the comparison is a diff rather than a project. The part that does not travel is the part you built yourself.

# same tree, same commit, two reports
$ vulkro scan . --format sarif > vulkro.sarif
$ semgrep scan . --sarif --output semgrep.sarif

# then read the two side by side. the interesting rows are the ones only one tool reported
The fastest way to find out whether the defaults match your codebase: run both over the same commit and diff the reports in a SARIF viewer. GitHub Code Scanning ingests either file.

What does not port is the rules. Semgrep’s pattern language expresses compound shapes that Vulkro custom rules have no equivalent for, and a library your team authored and tuned over two years is not a file format problem. Treat it as a switching cost, price it honestly, and be suspicious of any vendor who tells you the migration is a command. This page used to make exactly that claim and it has been removed.

Which is why the arrangement we would actually recommend is not a switch. Run Vulkro for the traced classes and for the dependency, secret, configuration and container passes that come out of the same scan, and keep Semgrep for the conventions your team encoded and for the languages Vulkro does not parse. The two reports overlap far less than the category name suggests.

Testing that costs nothing but an afternoon: your first CLI sign-in starts a 14-day trial of the full product, no card, and every scan in it runs on your own machine. Licences are issued per seat directly by our team after that.

See alsoVulkro vs BearerVulkro vs SnykVulkro vs TrivyCompare the editionsThe result and its boundary