Open source · Apache-2.0
It shows the whole path, and what is missing.
VyQL follows untrusted input through your code, to the place where it becomes dangerous. Then it names the controls that should have stopped it there. If a path exists, you see every step of it. If a control already covers the path, VyQL reports nothing.
$ curl -fsSL https://dl.vyprsec.ai/vyql/install.sh | sh
macOS and Linux, amd64 and arm64. It checks the checksum and installs to
~/.local/bin. There is no signup and no API key, and nothing
leaves your machine.
Other Ways to Install
| Homebrew | brew install vyprai/tap/vyql |
| Container | docker run --rm -v "$PWD:/work" ghcr.io/vyprai/vyql scan . |
| Go | go install github.com/vyprai/vyql/cmd/vyql@latest && vyql update -yes |
| GitHub Actions | vyql-security-scan |
| Claude Code | /plugin marketplace add vyprai/claude-plugins |
Homebrew, Docker, release archives, and the install script ship the free
definitions with the engine. go install is the engine only;
follow it with vyql update -yes, or run an interactive scan
and accept the download prompt.
Quickstart
From the Terminal
These commands cover almost everything you need:
vyql scan . # everything in this tree vyql explain . # why each finding fired vyql scan -fail-on none . # report without failing the shell vyql scan -exclude node_modules -exclude '**/*_templ.go' . # skip these vyql scan -baseline .vyql-baseline.json . # only what is new
Reviewing one change is a different question than reviewing a whole repository. For that, scan both sides and compare them. The comparison uses a fingerprint, so if you move a function, it does not appear as a new finding:
git worktree add -q /tmp/base $(git merge-base HEAD origin/main) vyql scan -format json /tmp/base > before.json vyql scan -format json . > after.json vyql diff before.json after.json
Through an Agent
VyQL also ships as an agent skill. Then you can ask a question instead of remembering the flags. In Claude Code:
/plugin marketplace add vyprai/claude-plugins /plugin install vyql@vypr
After that it works as a conversation. Every answer keeps the context of the previous one, so you do not need to repeat which finding you mean:
you audit this repo for security problems
Scanning the whole tree. vendor/, node_modules/ and testdata/ are
skipped already. On top of those I would skip:
**/*_templ.go generated templates, one huge function each
examples/ sample code, not shipped
Anything else to leave out, or shall I go?
you go
scanned python:14 javascript:3, 2 files unread (.kt)
7 findings. 2 critical, 3 high, 2 medium.
1. VYQL-INJ-002 critical api/users.py:88 request body reaches a shell
2. VYQL-PATH-001 high api/files.py:23 query param reaches open()
...
you verify the high ones
4 families: INJ (2), PATH (3), CRY (1), CFG (1). Verifying all four,
one agent each. Nothing deferred.
INJ 1 real, 1 false positive
PATH 3 false positives, all through the same startswith() guard
CRY 1 real
CFG 1 unresolved, needs to know whether debug mode ships
The three PATH findings share a cause. That guard is a containment
check VyQL does not model, so it is a binding gap, not three bugs.
you what about the INJ one
Real. api/users.py:88 is a request handler and the path carries the
value with no escaping. Fix is core.ShellEscape: subprocess with a
list rather than shell=True.
you prove it
Booting from a clean worktree at the scanned commit.
POST /users/import with a payload of "; id" returns uid=0(root).
Torn down.
you fix it then
One file, api/users.py, introducing subprocess with an argument list.
Shall I?
A reproduction runs only against your own machine, and the skill asks you first before it runs one. Writing the reproduction is the result. Whether to run it is your decision.
It runs the commands, and it tells you what was read before it tells you what it found. It asks first before it installs anything, before it scans, and before it writes a reproduction. It asks about scope because scope decides what the scan cannot find — a directory skipped there is one the report calls clean without having read it. And it never shortens a list of findings without saying so. The skill itself is a plain SKILL.md file in an open format, so any agent that can read it can follow the same steps. Claude Code is only the one that has an installer for it.
Either Way, the Same Shape
Agree what you are looking at, scan it, read the coverage line, then read the list. Verify the findings that matter to you, and reproduce one if you need proof. You can stop at any point where you already have your answer. For most scans, the list is enough.
What a Finding Looks Like
An Express app that takes a filename from the query string and passes it to the filesystem:
// app.js
const express = require("express");
const fs = require("fs");
const app = express();
app.get("/download", (req, res) => {
const name = req.query.name;
res.send(fs.readFileSync("/var/data/" + name));
});
There is no config file, and you do not choose which rules to run:
$ vyql scan .
analysis profile: HTTP API server (api)
2 finding(s):
[P3] [HIGH] VYQL-PATH-001 (conf=high, fp=c61843fd71a2284b)
source: code.HttpInput @ app.js:6
sink: code.FilePathAccess @ app.js:7
taint path: app.jsAttr#80 -> app.jsFormat#81 -> app.jsArg#83
unless path coveredBy core.PathCanonicalization: not satisfied, no neutralizing
control dominates the path; none found anywhere on flows
unless endpoint coveredBy core.PathAccessCheck: not satisfied, no guard on sink
Look at the last two lines. Most scanners tell you that something is wrong, and the rest of the work stays with you. VyQL names the control that would have fixed it. And if you already have that control in your code, VyQL tells you why it did not apply here.
What You Get
Knowledge as Data
A small Go engine, plus 12,400 .vyql files that you can open and
edit. To add a framework you usually write a binding. You do not patch Go.
One Graph, 22 Languages
Java, Python and JavaScript are the most complete ones. The other languages have less coverage, and every scan tells you what you really got.
Built for CI
scan exits 3 when it finds anything high or critical, and 1 only when
it could not run. SARIF and JSON output. Baseline files, for a codebase that
already has findings.
It Tells You What It Did Not Read
Files that no frontend could read are reported, also when you do not ask for it. Without that line, a clean result over code that nobody read looks the same as a result that is really clean.
Downloads
847 release downloads
Counted from GitHub release assets since 2026-08-05. Continuous integration and mirrors are included. go install and container pulls are not counted at all.