Skip to content

Running Scans (CLI)

The Vygl CLI runs every scan engine locally and uploads only findings metadata. It works on a developer laptop, a CI runner, or any environment with Docker.

vygl scan [OPTIONS] [PATH]

The most common flags:

Flag (long / short) Purpose
--scan-types / -t Comma-separated list of engines (default: sast,sca,secrets,iac,malware). Valid values: sast, sca, secrets, iac, malware, container. malware and container are gated by org/project settings — when the cloud policy says they’re off, the CLI skips them automatically and prints a short notice.
--image Scan a container image instead of a directory. Forces --scan-types container.
--fail-on Exit non-zero when findings reach this severity. One of critical, high, medium, low.
--no-upload Run engines locally without pushing results to the cloud.
--name / -n Project name (use when there’s no git remote).
--project-id / -p Bind to an existing project explicitly (also VYGL_PROJECT_ID).
--branch / -b Override the auto-detected branch (useful in detached-HEAD CI).
--commit Override the auto-detected commit SHA.
--repo-url Override the auto-detected repository URL.
--git-provider Force the SCM type (github, gitlab, bitbucket, gitea); also VYGL_GIT_PROVIDER.
--pr Pull/merge request number — enables PR comment posting. Auto-detected on GitHub Actions, GitLab CI, and Bitbucket Pipelines.
--config / -c Path to a .vygl.yml (overrides auto-discovery).
--show-all Print every finding to stdout, not just the gate-triggering ones.
--trigger Mark the trigger source on the dashboard (default ci; e.g. manual, scheduled).
--api-url Override the API base URL. Defaults to VYGL_API_URL then https://api.vygl.io.

Scan only secrets and dependencies, fail on high or above:

vygl scan --scan-types secrets,sca --fail-on high .

Local-only run (no upload):

vygl scan --no-upload .

Scan a container image:

vygl scan --image myorg/myapp:1.2.3

CI run with explicit branch (detached HEAD):

vygl scan --branch main .

Scan a PR and post a comment:

vygl scan --fail-on high --pr 1234 .

Write machine-readable output:

vygl scan --no-upload --json evidence/vygl.json .

The CLI image ships no SAST ruleset. Rules come from your organisation: when you are signed in, the CLI fetches the org policy and scans with the rules seeded for your account. Nothing to configure.

For self-hosted or air-gapped installs, point the CLI at a ruleset you hold a licence for:

vygl scan --rules /opt/vygl-rules --no-upload .

or set it once in .vygl.yml:

scan:
sast:
rules_path: /opt/vygl-rules

The org policy still wins when you are signed in, so a local ruleset is a fallback for disconnected use rather than an override.

Vygl never uses opengrep’s built-in auto ruleset. That config is fetched from Semgrep’s registry on every scan — it cannot work without egress, and it would mean contacting a third party on each run, which is not how a local-first scanner should behave.

The console render is a summary — it stops at 15 findings unless you pass --show-all, and shows only severity, rule and file:line. --json PATH writes the whole result instead: every finding with its message, CWE, snippet and fingerprint, the per-engine outcome, and the dependency inventory.

It needs no account. On the --no-upload path — or with no API key at all — the document is fully populated, so you can run Vygl as a scanner on code that never leaves your machine.

vygl scan --no-upload --json out.json .
# one row per finding
jq -r '.findings[] | [.severity, .rule_id, .file_path, (.start_line|tostring)] | @tsv' out.json
# did every engine actually run?
jq '.engines' out.json
{
"schema_version": 1,
"tool": { "name": "vygl", "version": "0.1.0" },
"scan": { "root": "/scan", "local_only": true, "verified": false, "scan_id": null,
"scan_types_requested": ["sast","sca","secrets","iac"],
"started_at": "…", "completed_at": "…",
"git": { "branch": "main", "commit_sha": "…", "repo_url": "…" } },
"engines": {
"sast": { "status": "ok", "duration_s": 41.2, "findings": 12, "error": "" },
"secrets": { "status": "error", "duration_s": 0.3, "findings": 0,
"error": "engine did not complete: binary_not_found" }
},
"summary": { "total": 15, "critical": 1, "high": 4, "medium": 6, "low": 3, "info": 1 },
"type_counts": { "sast": 12, "sca": 3 },
"findings": [ /* … */ ],
"dependencies": [ /* … */ ]
}

Things worth knowing:

  • The file is written for every outcome — a clean pass, a --fail-on breach, a Block-mode trip, or a failed upload. It lands before the gate runs, so --fail-on high --json out.json gives you exit code 1 and a complete document.
  • A write failure never fails your build. An unwritable path prints a warning to stderr; the exit code is whatever the scan itself decided.
  • Paths are repo-relative (server/deploy.js, not /scan/server/deploy.js), so a scan inside a container resolves against your checkout on the host.
  • Findings are ordered deterministically — severity, then path, line, rule — so two runs over an unchanged tree diff cleanly.
  • verified is always false and scan_id always null. The document is written before results are pushed, so AI verdicts aren’t in it. They live in the dashboard.
Command What it does
vygl auth set-key <KEY> Store an API key locally in ~/.vygl/credentials (chmod 600).
vygl auth test Verify your key works and show the org it belongs to.
Command What it does
vygl project list List all projects in your organization.
Command What it does
vygl results <SCAN_ID> Fetch and print results from a previous scan.