Inspiration

Security scanners are good at answering one question: does vulnerable code exist?

But in a merge request, reviewers need the next question answered:

Can this vulnerable code actually be reached from the application's entry points?

Without reachability context, scanner output becomes a flat list of alarms. A severe finding with no path from the attack surface gets mixed with reachable vulnerabilities that deserve attention now. Datadog's State of DevSecOps 2025 found that just 18% of critical-CVSS vulnerabilities are still considered critical after applying contextual prioritization. Four out of five "critical" findings get downgraded once context is applied, but separating signal from noise is still manual triage work.

I built ReachGate to turn vulnerability triage from scanner noise into verifiable GitLab evidence.

What it does

ReachGate uses GitLab Orbit as a code evidence graph for vulnerability reachability.

For each finding, ReachGate starts from the declared attack surface in reachgate.yml, walks Orbit's graph of files, definitions, imports and calls, and checks whether a graph path reaches the vulnerable definition.

This is graph reachability — a concrete import/call path from the attack surface to the vulnerable definition — not full data-flow taint. That boundary is deliberate, and it is never hidden: every certificate names the search strategy behind the verdict.

It returns three verdicts:

  • REACHABLE: a concrete graph path exists from a declared entry point to the vulnerable code.
  • NOT_REACHABLE: the search exhausted its frontier within configured bounds, with no bound hit and zero API errors.
  • UNKNOWN: the evidence is incomplete, so ReachGate refuses to pretend the finding is safe.

In the MR workflow, each verdict becomes a GitLab-native receipt comment with the graph path, rule breakdown, stable fingerprint and reachability certificate. CI also uploads a machine-readable reachgate-receipts.json artifact. The bundled MR workflow is advisory and comment-only by default, but the receipts and JSON artifacts are gate-ready evidence teams can wire into policy.

The core rule is simple: the LLM never decides the verdict. The agent can run the workflow and explain the result, but the decision comes from deterministic rules and Orbit graph evidence.

How I built it

ReachGate has four connected layers:

  1. Orbit reachability engine
    A Python engine queries GitLab Orbit and reconstructs reachability with bounded BFS over neighbors. It records why each walk stopped, so a cut-off search cannot be mistaken for proof.

  2. Deterministic policy and certificates
    Fixed rule weights produce REACHABLE, NOT_REACHABLE or UNKNOWN. Every receipt includes a certificate with policy version, search bounds, evidence mode, API errors, frontier status and a stable fingerprint.

  3. GitLab MR workflow
    CI posts fingerprint-idempotent receipts on merge requests and uploads reachgate-receipts.json, giving reviewers and automation the same evidence without duplicate comment spam. Each receipt also renders the graph path as a Mermaid diagram that GitLab draws inline in the comment: a reachable finding shows the connected entry-to-vulnerability path, a not-reachable one shows the nodes with no path between them — so reviewers read the evidence, not just a label.

  4. Replayable evidence layer
    The repo includes 422 focused tests, plus 5 standalone install-gate tests deselected by default; an offline proof verifier; OpenVEX and SARIF 2.1.0 exports; a sha256 evidence manifest; a machine-checkable Evidence Contract; an adversarial reachgate selftest that rejects fake-green evidence; fix-verification over before/after receipts; a contract-check gate; an offline evidence explorer; a doctor command that checks whether declared entrypoint globs actually match indexed Orbit files; a coverage report that surfaces verdict counts, UNKNOWN reasons and blind spots; and a portable evidence capsule (reachgate capsule build) — a deterministic, byte-stable zip of the receipts, the OpenVEX and SARIF exports, the sha256 manifest, the offline verifier and a HOW_TO_VERIFY.txt — that a reviewer can carry away and verify offline with no token and no network, optionally Ed25519-signed (reachgate capsule sign, [sign] extra) for authenticity and tamper-evidence checkable with the public key alone.

ReachGate also runs through a /reachgate Agent Skill with GitLab Duo and Orbit MCP. The agent executes the same deterministic workflow; it does not invent verdicts.

What makes it different

Most AI security demos summarize scanner output. ReachGate does something stricter: it asks GitLab Orbit for graph evidence.

If a path exists, ReachGate shows it.

If no path exists and the search was exhaustive within configured bounds, ReachGate says NOT_REACHABLE.

If the graph, config, API or search bounds cannot support that conclusion, ReachGate says UNKNOWN.

That third verdict is a core feature. In security automation, an honest "not enough evidence" is safer than a confident false "not reachable."

ReachGate is not just another scanner. It is a portable evidence layer: the same receipt can be inspected by a human, verified offline, checked against an Evidence Contract, compared across runs, and exported to downstream security formats.

Challenges

The hardest part was making NOT_REACHABLE honest.

A bounded graph search can easily lie if a timeout, API error, missing definition, bad entrypoint glob or search cap is treated as "no path found." ReachGate only returns NOT_REACHABLE after an exhaustive negative: frontier exhausted, no bound hit, zero API errors. Anything less becomes UNKNOWN with the exact reason.

Orbit traversal also required real engineering work. Orbit does not provide native pathfinding, so ReachGate reconstructs paths with bounded BFS. I also found that some JavaScript imports appear as ImportedSymbol nodes instead of ordinary graph edges, so the engine treats matching imported symbols as first-class reachability evidence.

The other challenge was resisting overclaiming. A security tool should not say "safe" just because the graph is incomplete. That is why ReachGate has an Evidence Contract and fake-green fixtures: if a receipt claims NOT_REACHABLE while the search timed out, the contract check fails.

Accomplishments

I proved the workflow live on GitLab:

  • MR !2 shows both outcomes from the same Orbit-backed pipeline: a REACHABLE SSRF receipt (score 85, 1 hop from content/frontend/404/archives_redirect.js) with a concrete graph path, and an exhaustive NOT_REACHABLE path-traversal receipt.
  • MR !3 proves fingerprint-idempotent reruns: same findings, same fingerprints, no duplicate MR comments.
  • Each MR receipt renders the graph path as an inline Mermaid diagram, so the reachable path — or its absence — is visible directly in the comment.
  • A captured UNKNOWN receipt proves incomplete evidence does not become fake-green.
  • reachgate selftest accepts real evidence, rejects fake-green evidence, and keeps UNKNOWN as an evidence gap.
  • reachgate verify replays the captured proof offline with no token and no network.
  • ReachGate exports the same receipt evidence to OpenVEX, SARIF 2.1.0, and a sha256 evidence manifest.
  • reachgate capsule build packages the same evidence into a deterministic, byte-stable capsule a judge can verify offline; reachgate capsule sign adds an optional Ed25519 signature, so authenticity and single-byte tampering are checkable with the public key alone.
  • The Evidence Contract and contract-check make overclaiming machine-checkable.
  • Fix verification can compare before/after receipt artifacts and only claim reachability_removed when the after receipt is an exhaustive NOT_REACHABLE under the same policy.
  • The offline explorer and Judge Pack make the proof easy to inspect during review.
  • Everything ships as one reachgate CLI — verify, coverage, export-vex/export-sarif, manifest, contract-check, fixcheck, blame, explorer, capsule, selftest, judge — set up with a single pip install. The offline evidence commands run immediately against bundled proof receipts: no token, no network, no repo checkout required.

The result is not just a demo screenshot. It is a replayable GitLab evidence trail.

What I learned

The biggest lesson was that security automation needs auditability more than confidence.

For vulnerability triage, reviewers need to know what was searched, what path was found, whether the search exhausted its frontier, whether bounds or API errors affected the result, and whether the same finding fingerprints the same way on rerun.

That is why ReachGate is built around receipts, certificates, fingerprints, explicit UNKNOWNs, and contract-checked evidence.

Try it out

What's next

Next steps include turning advisory receipts into optional blocking policy, native GitLab Vulnerability Report annotations, async Orbit traversal for larger graphs, deeper dependency reachability, and attack-surface suggestions for reachgate.yml.

ReachGate's core idea is simple:

Do not triage vulnerabilities by scanner noise or LLM opinion. Triage them with verifiable reachability evidence from GitLab Orbit.

Built With

  • agent-skills
  • gitlab-ci/cd
  • gitlab-duo-agent-platform
  • gitlab-merge-requests
  • gitlab-orbit-api
  • gitlab-work-items
  • httpx
  • mermaid
  • orbit-mcp
  • pytest
  • python
  • pyyaml
Share this project:

Updates