VyQL Docs Company Star on GitHub

Open source · Apache-2.0

No hidden gaps. No hand-waving.

VyQL follows untrusted input through your code to the point it turns dangerous, and names the controls that would have stopped it. If a path exists, it shows you every hop. If a control covers it, it stays quiet.

$ curl -fsSL https://dl.vyprsec.ai/vyql/install.sh | sh

macOS and Linux, amd64 and arm64. Verifies its checksum, installs to ~/.local/bin. No signup, no API key, nothing gets sent anywhere.

Other Ways to Install

Homebrewbrew install vyprai/tap/vyql
Containerdocker run --rm -v "$PWD:/work" ghcr.io/vyprai/vyql scan .
Gogo install github.com/vyprai/vyql/cmd/vyql@latest
GitHub Actionsvyql-security-scan
Claude Code/plugin marketplace add vyprai/claude-plugins

Every one carries the security knowledge with it. No second setup step, and nothing to point at a server.

Quickstart

From the Terminal

Four commands cover almost everything:

vyql scan .                      # everything in this tree
vyql explain .                   # why each finding fired
vyql scan -fail-on none .        # report without failing the shell
vyql scan -baseline .vyql-baseline.json .   # only what is new

Reviewing a change rather than a repo is a different question. Scan both sides and diff, which keys on a fingerprint so moving a function doesn't read 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 ships as an agent skill, so you can ask instead of remembering flags. In Claude Code:

/plugin marketplace add vyprai/claude-plugins
/plugin install vyql@vypr

Then it is a conversation. Each turn builds on the last, so you never re-state which finding you mean:

you  audit this repo for security problems

     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 against your own machine and nothing else, and the skill asks before executing one. Writing it is the deliverable; running it is your call.

It runs the commands, reports what was read before what it found, and asks before installing anything or writing a reproduction. The skill is a plain SKILL.md, an open format, so any agent that reads it can follow the same steps. Claude Code is just the one with an installer.

Either Way, the Same Shape

Scope what you are looking at, scan, read the coverage line, look at the list, verify the ones that matter, and reproduce if you need proof. Stop wherever you have your answer. Most scans end at the list.

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));
});

No config file, no picking 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 something is wrong and leave the rest to you. VyQL names the control that would have fixed it. If you already have that control, it tells you why it didn't apply.

What You Get

Knowledge as Data

A small Go engine plus 12,400 .vyql files you can open and edit. Adding a framework usually means writing a binding, not patching Go.

One Graph, 22 Languages

Java, Python and JavaScript are the most complete. Coverage thins out from there, and the scan tells you what you actually got.

Built for CI

scan exits 1 on anything high or critical. SARIF and JSON output. Baseline files if you're adopting it on a codebase that already has findings.

It Admits What It Missed

Files it couldn't parse get reported whether you ask or not. Otherwise a clean run over code nothing read looks the same as a genuinely clean run.