Confidence model
Every finding carries a confidence level and a one-line confidence_reason that says why it fired and what would make it a false positive.
vulkro scan . # default: Medium and above
vulkro scan . --min-confidence high # strictest report
vulkro scan . --all-confidence # everything, including Low
Levels
| Level | Meaning | Typical examples |
|---|---|---|
| Verified | The finding is on an unprotected route whose handler reads user input. | Missing auth on a handler that reads the request body. |
| High | Strong evidence. | Provider-format secrets, traced injection paths, KEV-listed CVEs. |
| Medium | The pattern matches, with a known false-positive shape. | Unscoped :id lookup with no visible auth check; generic secret pattern. |
| Low | Heuristic only. Hidden by default. | Pattern match with little context; test, fixture or migration code. |
Confidence is separate from the finding's disposition (Proven, Unproven, Not checked) and from its exploitability grade.
Strict-confidence filter
--strict-confidence (or VULKRO_STRICT_CONFIDENCE=1) also drops any High finding with an empty evidence list. A High finding survives only with at least one corroborating signal: a traced data flow, an exact-match rule, or several pattern signals that add up. Off by default.
Per-detector-category tiers
What each level means depends on the detector. Every rule page links here.
| Category | High | Medium | Low |
|---|---|---|---|
| Secrets | Provider-format match (AKIA, sk_live_, ghp_) outside test code. | High-entropy value assigned to a *_KEY, *_TOKEN or *_PASSWORD name. | Long hex or base64 string with no naming signal. |
| Auth | Reachable route with no visible auth middleware, decorator or guard. | Looks unprotected in the file, but a global mount or framework convention may apply auth. | Route name suggests admin or internal, nothing else. |
| IDOR / BOLA | :id param flows into an unscoped object lookup. | Unscoped lookup, flow not confirmed. | :id route with no visible check. |
| Mass assignment | Request body bound to a model with fields the API does not allowlist. | Model schema not fully resolved. | Pattern match only. |
| Injection | Traced flow from request to SQL, shell, template or eval sink with no sanitiser. | Sink with a likely tainted argument. | Sink call, pattern only. |
| Crypto | Provably wrong use: md5 for passwords, ECB mode, verify=False on TLS. | Weak primitive, possibly non-security use. | Mention in a comment or test name. |
| SCA / CVE | Direct dependency on a KEV-listed advisory at the exact version. | Transitive dependency, or a range match. | Typosquat or new-package signal with no CVE. |
| SSRF | Tainted URL reaches an HTTP client with no allowlist or host check. | Chain crosses an unresolved function. | URL-shaped argument to an HTTP client. |
| LLM | User input or a secret flows into a model SDK call without isolation. | Input near an LLM call, no full chain. | Template string passed to an LLM call. |
| CSRF / cookies | Session middleware without CSRF protection; SameSite=None without Secure. | CSRF middleware unclear; cookie missing one flag. | Session-like cookie name only. |
confidence_reason
taint flowed from req.body to db.query without sanitiser
auth helper not found in import scope
AKIA-prefix matches AWS access-key format
It appears in JSON, SARIF (properties.confidence_reason) and the local console.
Calibration table
Detector categories with a measured true-positive rate under 30% are lowered from High to Medium by default, and the reason says so. The table is updated when benchmark results change.
Where the bar is
Precision and recall at each confidence cut are published, with the corpus and scoring method, on Benchmark.
Offline false-positive triage (--triage)
--triage lowers confidence by one level for findings in test, fixture, example, generated, vendored and migration paths. It is path-based, deterministic, uses no model and makes no network call. It never drops a finding.
vulkro scan . --triage --min-confidence high
See also: vulkro scan, Glossary