Vulkro vs Bearer
Close on what they find, far apart on what you read.
Bearer and Vulkro found a similar share of the same corpus, so this page is not going to pretend detection is the story. What separates them is the volume around the answer and how long you wait for it: on that run Bearer reported 72 false positives to Vulkro’s 13, and took roughly 7.3 times as long. Bearer’s own strength is somewhere else entirely, and this page says where.
- One corpus, one machine
- Both scan locally
- Recall within one row of each other
- Every figure carries its date
01 / The short answer
Which one you should pick
Read this section and stop, if you like. Everything under it is the working.
Choose Bearer if
- The deliverable is a privacy report. Classifying which sensitive data types flow where is Bearer’s primary output, not a side channel of a security scan. If your auditor is asking which systems touch personal or health data, Bearer is deeper at that than Vulkro’s PII and PHI detectors.
- Your code is Ruby or PHP. Bearer reads both. Vulkro parses neither: those files are counted in the inventory and skipped, so a clean Vulkro result on a Rails or Laravel service means nothing at all.
- Your review is periodic rather than per-commit. A scan measured at 360.1s over the corpus below is comfortable on a nightly or weekly schedule. The wall clock only becomes a problem when the scan has to sit in a pull request.
- You need a scanner with no licence to negotiate. Bearer’s CLI is source-available under the Elastic License 2.0: free for in-house use, with a restriction on offering it as a hosted service to third parties. Vulkro is licensed per seat and issued directly by our team.
Choose Vulkro if
- The output has to be a queue somebody clears. 47 catalogued bugs against 13 false positives is a list a team reads. 43 against 72 is a list a team learns to skim, and a skimmed list is an unread list.
- It has to run on every push. 49.2s over the same corpus, roughly 7.3x faster on the same machine, and deterministic: the same commit scanned twice returns the same findings in the same order, so a failing gate means the code moved.
- You want one pass over everything, not one pass over code. Dependency CVEs, secrets, infrastructure configuration and built container images come out of the same scan and the same report, with CVE matching done offline against a checksummed bundle on your own disk.
- Your audit frame is exposure rather than data classification. Broken access control, injection, SSRF, mass assignment and the rest of the OWASP API surface, ranked by what the code exposes. Vulkro emits privacy findings too, but they are one axis rather than the organising principle.
- 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.
These two answer different questions well enough that running both is a reasonable outcome. Section 06 says how, and what the second scan costs you in time.
02 / The architectural difference
Two tools with different first questions
Both read your source on your machine and neither uploads it. The split is not privacy posture, it is what each one asks the code before it reports anything.
Bearer asks what data this code touches. It classifies sensitive data types (personal, health, payment and the rest), follows where those values travel, and ranks by the sensitivity of what is at risk. The natural deliverable is a data-protection picture of a system: which components handle what, and where it crosses a boundary.
Vulkro asks what this code exposes. Every scan extracts the route inventory and builds the call graph first, then reports the paths where an untrusted value reached something that trusted it, and the routes missing an authorization check that their siblings have. The natural deliverable is a ranked defect list with a file and a line on every row.
Both are legitimate first questions, and neither report is a superset of the other. A team that needs to answer an auditor about personal data is not served by a list of injection sinks, and a team trying to close a release gate is not served by a data inventory. The useful way to choose is to name the artifact you actually have to produce.
The one place the difference stops being philosophical is volume. A report shaped for a periodic review can afford to be generous about what it surfaces, because a human is going to sit with it. A report that blocks a merge cannot, because every false alarm is paid for by the next engineer who stops reading. That is the whole of the argument in section 03.
| Severity | Finding | CWE | Rule |
|---|---|---|---|
| CRIT | SQL injection in order lookupcheckout.py:214 | CWE-89 | VULK-1042 |
Dataflow pathVULK-1042
4 hops resolved. No sanitiser between the source and the sink. | |||
| HIGH | Missing authorization on invoice downloadroutes/invoice.ts:47 | CWE-639 | VULK-2117 |
| HIGH | SSRF via user-supplied URLproxy-handler.ts:23 | CWE-918 | VULK-1180 |
| MED | Hardcoded API token committed to the repositoryconfig/stripe.js:9 | CWE-798 | VULK-3304 |
03 / Measured
One corpus, one machine, one scoring script
This is the only comparative number on this page. Both tools ran through the same harness and were scored by the same rule, with Vulkro 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
| Tool | Found (of 83) | Missed | False positives | Precision | Recall | F1 | Runtime |
|---|---|---|---|---|---|---|---|
| Vulkro 0.18.0 | 47 of 83 | 36 | 13 | 0.78 | 0.57 | 0.66 | 49.2s |
| Bearer 2.0.2 | 43 of 83 | 40 | 72 | 0.37 | 0.52 | 0.43 | 360.1s |
- Recall is the close row
0.57 against 0.52, a gap of 0.05. Bearer found 43 of the 83 catalogued bugs and Vulkro found 47. Read this table as a difference in noise and time, not as a difference in detection, because detection is where the two tools are nearest.
- 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 both tools, so neither side is penalised for reporting things the corpus never labelled.
- What the noise costs
72 false positives against 43 true ones means most of what landed in the scored files was not a catalogued bug. That is 5.5x the false positives Vulkro produced on the same files. Precision is the row a triage rota feels every week, which is why the argument on this page is about the queue rather than the scoreboard.
- What the table does not measure
Nothing here scores either tool on a language Vulkro cannot parse. Bearer reads Ruby and PHP and Vulkro reads neither, so a codebase made mostly of those is a codebase this result does not describe. It also measures nothing about privacy classification, which is the output Bearer is actually built to produce.
- Wall clock
360.1s against 49.2s on the same machine, roughly 7.3x. In practice that is the difference between a check that runs on every push and a check that runs overnight, and those are two different products even when the findings overlap.
Our own misses are in the same scorecard: 36 of 83 catalogued bugs were not found in this run, listed by class, and they stay in the corpus rather than being retired. Where we lose, in our own words.
04 / Where Bearer wins
The things it does better, without the hedging
Precision is our row. It is not the only row, and a page that could not name the other tool's real advantages would be an advertisement rather than a comparison.
Privacy
Sensitive data classification is its flagship, and it is better at it
Breadth
Ruby and PHP, which Vulkro does not parse
Recall
It is not far behind on finding real bugs
Licensing
It can be adopted without a purchase decision
Influence
It changed how we rank privacy findings
05 / Coverage
Capability, side by side
Not a measurement. The table in section 03 is the measured part of this page; this one states what each tool is shaped to do, and marks the cells we could not check ourselves.
| Capability | Vulkro | Bearer |
|---|---|---|
| Languages read for code analysis | Python, JavaScript, TypeScript, Go, Java. Everything else is counted and skipped. | A wider published list that includes Ruby and PHP. |
| What the report is organised around | Exposure: routes, authorization, injection, SSRF, mass assignment, supply chain. | Sensitive data: which data types the code touches, and where they flow. |
| Cross-file dataflow | On by default, up to four hops, for Python, JavaScript, TypeScript and Go. Java taint is same-file only. | Dataflow is central to its design. We did not measure its cross-file depth, so this page does not rate it. |
| Route and endpoint inventory | Built on every scan before any rule runs, which is what gives the authorization checks something to check against. | Not assessed. |
| Privacy and data-protection reporting | PII and PHI detectors, plus records-of-processing output formats. One axis, not the organising principle. | The flagship. A large sensitive-data catalogue with data-protection reports as the primary deliverable. |
| Dependency CVE matching in the same pass | Yes, against a bundle on your disk. The bundle is checksummed and matching makes no network call. | Not assessed. The benchmark above scored application-code findings only. |
| Secrets, infrastructure config and built container images | All three in the same scan: provider secret rules, a configuration pass over Terraform, Kubernetes, Compose and Helm, and package databases extracted from image layers. | Not assessed. |
| Where the scan runs | Your machine, at every tier. VULKRO_OFFLINE=1 enforces it at the process boundary. | Its CLI runs locally too. This is a tie, and manufacturing a difference here would be dishonest. |
| Repeat runs | Deterministic: same tree, same binary, same findings in the same order, asserted by tests in the build. | Not assessed. |
| Machine-readable output | SARIF among 24 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. | Apex is not in its published language list. |
- 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.
5 manifest formats are parsed (npm, PyPI, Go modules, crates.io, Maven). The default published bundle currently ships npm and PyPI; the wider bundle covers the rest. Bundles are checksummed, and matching happens with no network call.
Read the two columns as different products rather than as a winner and a runner-up. Bearer answers a data question with more depth than we do; Vulkro answers an exposure question and brings the dependency, secret and configuration passes with it. The full language depth chart.
06 / Running both, or moving across
The cadence argument, and the diff that settles it
Both tools emit SARIF, so comparing them on your own tree is an afternoon rather than a project. There is no rule library to port in either direction.
# same tree, same commit, two reports $ vulkro scan . --format sarif > vulkro.sarif $ bearer scan . --output bearer.sarif --report security # budget for the second run: it is the slow one on the corpus we measured
Switching costs almost nothing here, which is unusual. Neither tool asks you to author a rule library, so there is no investment stranded on either side: what you lose by leaving Bearer is the data classification, and what you lose by leaving Vulkro is the traced paths and the passes bundled with them. Both are capability losses rather than sunk-cost losses.
Which is why running both is a sensible outcome, and the cadence is what makes it workable. Put Vulkro in the pull request, where 49.2s over a whole corpus and a deterministic verdict are the properties that matter, and run Bearer on the schedule your privacy review actually operates on. Trying to fit the slower scan into every push is what makes teams turn a scanner off, and a scanner that has been turned off finds nothing.
Testing that costs nothing but the 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 SemgrepVulkro vs SnykVulkro vs TrivyCompare the editionsThe result and its boundary