skip to content
mohamed azahrioui
‹ writing

6 min read

Most of your critical vulnerabilities cannot be reached

A scanner tells you a vulnerable function exists in your dependencies. It does not tell you whether your code can actually reach it. ReachGate answers that second question with a deterministic verdict you can verify yourself.

Open any vulnerability scanner on a real codebase and you get a wall of critical findings. Most of them are noise. Not because the CVEs are wrong, but because the vulnerable function sits in a dependency your code never actually calls. The finding is real. The risk, for your application, is not.

This is the same pattern I keep coming back to: a tool answers one question with confidence and leaves a second, more important question unanswered.

The question scanners answer

A scanner answers: does a known-vulnerable version of a package exist somewhere in your dependency tree? It matches your lockfile against a vulnerability database and reports every hit. That is a necessary check. It is not sufficient on its own.

A match says nothing about whether the vulnerable code path is live in your application. A CVE in a parser you never invoke is not a risk you need to fix today. But the scanner still ranks it critical, and the critical count is what drives the security backlog.

The question scanners do not answer

Here is the second question: starting from my application's entry points, is there an actual path in the code that reaches the vulnerable function?

That is reachability. If no path exists, the vulnerability is not exploitable through your app, no matter how high the CVE score is. If a path does exist, that finding belongs at the top of the queue.

A concrete example

A scanner flags a critical vulnerability in an image-processing library. The vulnerable function is a TIFF decoder. Your application uses the library only for its JPEG path and never touches TIFF. There is no call, direct or transitive, from any route handler to that decoder. Real CVE, real match, zero reachable path. It should not be blocking a release.

The inverse is worse: a medium-rated CVE whose vulnerable function sits three hops from a public endpoint. That is the one you want surfaced first, and a severity-sorted list buries it under the noise.

What ReachGate does

ReachGate walks the code graph and runs a bounded breadth-first search from the application's entry points toward each flagged function. Find a real path and the finding is reachable. Prove no path exists within the graph and the finding is not reachable. On the codebases I tested, roughly 80 percent of the critical findings came back not reachable.

The word that matters there is bounded. The search has a budget, and that budget is exactly where most tools quietly start lying.

It refuses to lie

When a bounded search runs out of budget before it can prove or disprove a path, there are two options. Return not reachable and look clean, or admit you do not know. ReachGate returns UNKNOWN. It never reports a safe verdict it did not actually prove.

This is the same design as everything else I build. A fixed rule engine decides the verdict, and the AI is only allowed to write the explanation. The model never moves a finding from reachable to safe. It describes what the engine already decided, and nothing more.

To keep that honest, one test in the suite exists only to sneak a fabricated safe result past the engine. It is supposed to fail, and it does. If a change ever lets a fake verdict through, that test breaks the build.

Every verdict ships as proof you can check

A verdict you cannot verify is just a different black box. So every ReachGate result exports as evidence:

  • OpenVEX, so a not-reachable finding becomes a standard not_affected statement with a justification, not a private label.
  • SARIF, so the reachable findings drop straight into GitLab and GitHub code scanning.
  • An sha256 manifest and an optional Ed25519 signature over the results, so no one can quietly edit a verdict after the fact.
  • An offline verifier that re-checks the signature and manifest with no token and no network, so you do not have to trust my server to trust the result.

One engine, three ways to run it

The verdict logic lives in one place and is exposed three ways: a CLI for local runs, a GitLab CI bot that comments the reachable findings on a merge request without re-posting duplicates on every pipeline, and a GitLab Duo agent that reaches the same engine over an MCP server. Same rules, same receipts, three surfaces.

The pattern underneath

This is the third time I have built the same shape. TrustGate decides whether an AI agent may take a risky action, given the live state of the data behind it. My note on data supply chain trust argues that agent governance needs that second, data-aware dimension. ReachGate decides whether a vulnerability is reachable, given the actual code graph.

Different domains, one idea: let a deterministic engine make the call, let the model only explain it, and ship every decision as auditable proof. The moment the model is allowed to decide, you have traded a verifiable answer for a confident guess. In security triage, and in agent actions, that trade is not worth it.

Links