Scan Command Reference

The scan command walks your project directory, parses package manifests locally, extracts package-manager install commands from CI/CD and shell files, and queries the Vulnetix VDB API to identify vulnerable dependencies. No file contents are ever uploaded to any server. Results are saved to .vulnetix/ and printed to your terminal.

Credentials are optional. When no credentials are configured the community fallback is used automatically.

Scan Is An Orchestrator

scan runs every analysis in one pass. It owns none of them: each is owned by its own subcommand, and scan composes those owners so a single invocation produces one coherent set of results, one SBOM and one quality-gate verdict.

PassOwnerEnable only thisTurn off inside scan
Dependency vulnerabilitiessca--evaluate-sca--no-sca
Static analysissast--evaluate-sast--no-sast
Secret detectionsecrets--evaluate-secrets--no-secrets
Container / OCI filescontainers--enable-containers--no-containers
Infrastructure as Codeiac--evaluate-iac--no-iac
License policylicense--evaluate-licenses--no-licenses
Local malwaremalscan— (always on)--no-malscan
AI inventory (AIBOM)aibom— (always on)--no-aibom
Cryptography inventory (CBOM)cbom— (always on)--no-cbom
Dependency fixesfix--sca-autofix opts in

Passing any --evaluate-* / --enable-containers flag switches scan to opt-in mode: only the named passes run. --no-* always wins.

Every pass behaves exactly as its own subcommand does — the same code runs either way, so scan --evaluate-licenses --allow MIT and license --allow MIT reach the same verdict. Two things are deliberately different when you go through scan:

  • External rule packs are not kind-locked. --rule org/repo on scan runs every rule kind the pack ships; on a specialized command only that command’s kind runs.
  • .gitignore handling for manifests. The specialized containers and iac commands prune gitignored manifests; scan, sca, sast and secrets do not, because dependency manifests routinely live in gitignored install directories. --include-ignored overrides the SAST-family behaviour.

Replaying results is not a scan: use report (scan --from-memory is a deprecated alias for it).

Usage

vulnetix scan [flags]

Flags

FlagTypeDefaultDescription
--pathstring.Directory to scan
--depthint3Maximum recursion depth for file discovery
--excludestringArray-Exclude paths matching glob pattern (repeatable)
--include-ignoredboolfalseInclude files matched by .gitignore. By default the SAST, secrets, containers and IaC passes skip gitignored paths; SCA and malscan always scan them (dependency install dirs like node_modules/ are commonly gitignored).
-o, --outputstringArray-Output target (repeatable): json-cyclonedx or json-sarif for stdout; file path (.cdx.json, .cdx, .bom.json, .sbom.json) for CycloneDX to file; file path (.sarif, .sarif.json) for SARIF to file. Multiple flags combine file outputs with pretty display.
-f, --formatstring-Deprecated — maps to --output json-cyclonedx. Use --output instead.
--concurrencyint-Deprecated, no-op. It never had any effect; set VULNETIX_SCA_CONCURRENCY (default 6) to change SCA fan-out
--no-progressboolfalseSuppress progress indicators
--pathsboolfalseShow full transitive dependency paths (npm, Python, Rust, Ruby, PHP, Go). Edges are built from locally installed packages (node_modules/, venv, vendor/, cargo metadata).
--no-exploitsboolfalseSuppress the detailed exploit intelligence section
--no-remediationboolfalseSuppress the detailed remediation section
--no-licensesboolfalseSkip license analysis during scan (license analysis runs by default)
--severitystring-Exit with code 1 if any vulnerability meets or exceeds this level: low, medium, high, critical. Severity is coerced from all available scoring sources (CVSS, EPSS, Coalition ESS, SSVC). Also gates on SAST findings.
--block-malwareboolfalseExit with code 1 when any dependency is a known malicious package.
--block-eolboolfalseExit with code 1 when a runtime or package dependency is end-of-life. Runtimes: Go, Node.js, Python, Ruby. Package-level checks activate when VDB has EOL data (404s are silently skipped).
--block-unpinnedboolfalseExit with code 1 when any direct dependency uses a version range (^, ~, >=) instead of an exact pin.
--exploitsstring-Exit with code 1 when exploit maturity reaches the threshold: poc (any public exploit), active (CISA/EU KEV / actively exploited), weaponized (in-the-wild only).
--results-onlyboolfalseOnly output when findings exist; completely silent when the scan is clean. Also suppresses exploit and remediation detail sections.
--no-ci-package-analysisboolfalseSkip dependency extraction from CI/CD pipeline files, including GitHub Actions workflows.
--no-shell-package-analysisboolfalseSkip dependency extraction from shell scripts, Makefiles and task recipes.
--version-lagint0Exit with code 1 when any dependency is within the N most recently published versions of that package (0 = disabled).
--cooldownint0Exit with code 1 when any dependency version was published within the last N days (0 = disabled, best-effort).
--sca-autofixboolfalseApply validated SCA fixes with the project package manager, then rescan to confirm. See SCA Autofix.
--sca-autofix-strategystringstableTarget strategy for --sca-autofix: stable, safest, or latest.
--sca-autofix-manifeststring-Restrict autofix edits to one manifest file; implies non-interactive manifest selection.
--sca-autofix-max-major-bumpint0Refuse autofix targets crossing more than N major versions.
--yesboolfalseNon-interactive autofix mode: auto-pick safe defaults and never prompt.
--evaluate-sastboolfalseEnable SAST analysis (exclusive mode — disables all other features not explicitly enabled)
--no-sastboolfalseSkip SAST (sast-kind) rules
--evaluate-scaboolfalseEnable SCA — package manifest analysis (exclusive mode)
--no-scaboolfalseSkip SCA — skip ordinary package manifests
--evaluate-licensesboolfalseEnable license analysis (exclusive mode)
--no-licensesboolfalseSkip license analysis
--evaluate-secretsboolfalseEnable secret detection rules (exclusive mode)
--no-secretsboolfalseSkip secret-detection SAST rules
--enable-containersboolfalseEnable container file analysis (exclusive mode)
--no-containersboolfalseSkip Dockerfile/OCI manifests and container SAST rules
--evaluate-iacboolfalseEnable IaC analysis (exclusive mode)
--no-iacboolfalseSkip HCL/Nix manifests and IaC SAST rules
--allowstring-Comma-separated SPDX licenses allowed by policy; passed to the license pass
--allow-filestring-YAML allow-list file for the license pass (overrides --allow)
--license-modestringinclusiveLicense conflict detection mode: inclusive (whole project) or individual (per manifest)
--policy-filestringdiscoveredLicence policy document (default .vulnetix/license-policy.yaml when present)
--exceptions-filestringdiscoveredApproved licence exceptions (default .vulnetix/license-exceptions.yaml when present)
--vex-filestringArray-Apply VEX statements from this file or directory before gates are evaluated (repeatable)
--no-vexboolfalseIgnore --vex-file and apply no third-party VEX
--projectstringinferredWhat this artefact is / who owns it — see Deployment context
--clusterstringinferredWhere it is deployed
--namespacestringinferredNamespace within the cluster
--environmentstringinferredDeployment stage, e.g. production
--tagstringArray-Additional key=value deployment label (repeatable)
--bom-manufacturerstringrepo ownerOrganization that created the BOM — see BOM authoring identity
--lifecyclestringderivedLifecycle stage(s) the BOM data was captured at, comma-separated
--snippet-contextint-1Surrounding non-empty source lines captured around each SARIF finding (-1 = dynamic, 0 disables). Also available on sast, secrets, containers and iac
--no-malscanboolfalseSkip the in-process malscan pass over local dependency install dirs
--no-aibomboolfalseSkip the AIBOM inventory pass
--no-cbomboolfalseSkip the CBOM cryptography inventory pass
--disable-default-rulesboolfalseSkip built-in default SAST rules (external --rule repos still loaded)
--list-default-rulesboolfalsePrint built-in SAST rules and exit. Also works on sast, secrets, containers and iac
-R, --rulestringArray-External SAST rule repo in org/repo format (repeatable); fetched from GitHub or --rule-registry — see Custom Rule Repositories
--rule-registrystringhttps://github.comOverride default registry URL for all --rule repos
--dry-runboolfalseDetect files and parse packages locally, check memory, then exit — zero API calls. Honoured by every scan-family command, scoped to the passes that command runs.
--from-memoryboolfalseDeprecated — use vulnetix report. Reconstructs the pretty output from .vulnetix/sbom.cdx.json without API calls
--fresh-exploitsboolfalseDeprecated — use vulnetix report --fresh-exploits
--fresh-advisoriesboolfalseDeprecated — use vulnetix report --fresh-advisories
--fresh-vulnsboolfalseDeprecated — use vulnetix report --fresh-vulns
--reachabilitystringbothTree-sitter reachability mode: direct, transitive, both, or off. Per-finding source-level reachability analysis runs against every produced CVE. Disable globally with off for large monorepos. See the Reachability Analysis section.

Third-party VEX

--vex-file and --no-vex are registered on every member of the scan family — scan, sca, sast, secrets, containers, iac.

vulnetix sca --vex-file vendor.openvex.json --severity high
vulnetix scan --vex-file ./vex/

Statements are applied in one place, immediately before the quality gates, so every gate honours VEX by construction. Filtering per gate would leave one that could be forgotten, and the forgotten one fails a build over a finding the vendor has already said does not apply.

The summary line names the suppressed count, because 0 vulnerabilities on its own reads as “nothing was found”:

1 packages | 0 vulnerabilities (12 suppressed by VEX)

Suppressed findings are annotated in the SBOM, never deleted. See the VEX Command Reference for the formats read, how statements are matched, and what the provenance properties record.

Deployment context

A repository scan answers “what is in this code”. It cannot answer “which of our clusters runs the vulnerable version” — a repository has no idea where its artefacts end up. These labels carry that from the pipeline that does know.

vulnetix scan --project payment-service --cluster prod-eu --namespace payments
DimensionAnswersCardinalityOwned by
cluster / namespace / environmentWhere is it deployed?Low, stablePlatform team
projectWhat is it, who owns it?High, volatileDev teams

They are separate, orthogonal fields on purpose: a scan belongs to cluster prod-eu and project payment-service at once, and collapsing them makes either query impossible.

Unset values are inferred from VULNETIX_CLUSTER, VULNETIX_PROJECT, VULNETIX_NAMESPACE, VULNETIX_ENVIRONMENT, CI_ENVIRONMENT_NAME, POD_NAMESPACE and CI_PROJECT_NAME, so a pipeline can export them once for a whole job.

Nothing is inferred from a branch name. “main means production” is a convention this CLI has no business assuming, and a wrong environment label is worse than an absent one — it attributes a finding to a cluster that never ran the code.

The labels land in metadata.properties as vulnetix:deployment/*, in .vulnetix/memory.yaml, and in the cli.* upload envelope, so the backend can answer fleet-scale questions across repositories. Locally, the same labels are queryable with bom ls and bom where.

BOM authoring identity

Every CycloneDX document this CLI writes says who made it and what it was looking at when it did. CycloneDX has no single field for that — it splits the idea across five members of metadata, and their own descriptions decide which is which:

FieldThe spec’s own wordsWhat we put there
metadata.manufacturer“The organization that created the BOM… common in BOMs created through automated processesThe organization running the scan
metadata.authors“The person(s) who created the BOM… common in BOMs created through manual processesOnly where a person made the claims, as in a hand-curated VEX
metadata.tools“The tool(s) used in the creation, enrichment, and validation of the BOM”Every Vulnetix command that touched the document
metadata.supplier“The organization that supplied the component that the BOM describesSubject-level; says nothing about who wrote the document
metadata.lifecycles“The stage(s) in which data in the BOM was capturedWhat this pass actually read

Who created it

metadata.manufacturer is the organization running the scan — not Vulnetix. Vulnetix supplied the instrument, and the instrument is named in metadata.tools; a document claiming Vulnetix created an inventory of your repository would be saying something different, and something untrue.

It is resolved from the first of these that answers:

  1. --bom-manufacturer
  2. VULNETIX_BOM_MANUFACTURER
  3. The repository owner your CI provider states — GitHub, GitLab, Azure DevOps, Bitbucket, Jenkins or the generic variables
  4. The owner segment of the repository’s git remote

If none of them answers, the field is omitted. An absent manufacturer costs a reader one unknown; an invented one is a false claim they have no way to detect.

When an organization id is available — --org-id or VULNETIX_ORG_ID — it rides along as manufacturer.bom-ref (urn:uuid:<org>), tying the document to a Vulnetix organization without asserting a display name the CLI cannot resolve. An id on its own is not enough to name a manufacturer, though: manufacturer.name is what a reader sees, so the field stays absent until one of the four sources above supplies a name.

What it was looking at

metadata.lifecycles is a statement about the observation, not about the product, and it is an array because one pass reads several kinds of source:

What the pass readPhase
Manifests and lockfilespre-build
An installed dependency tree — node_modules, site-packages, vendor/build
A container image, or packages recovered from compiled binariespost-build
AI or cryptographic inventory, found by observationdiscovery
Any of the above with deployment labels suppliedadds operations

Reading a manifest tells you what a build is intended to resolve — the artefacts do not exist yet — so that is pre-build. A consumer uses the difference to decide how much the component list can be trusted.

For the scan family specifically that means scan and sca claim pre-build, containers claims post-build, and any of them adds operations when deployment labels are supplied. cdx reads more source kinds in one pass and usually claims several phases at once.

--lifecycle replaces the derived answer outright, because a pipeline that states its own stage is making a claim about its process that no amount of filesystem inspection can contradict:

vulnetix scan --lifecycle post-build,operations

Valid phases are design, pre-build, build, post-build, operations, discovery and decommission. An unrecognised value is rejected rather than treated as a custom phase name — a typo silently becoming a custom lifecycle is indistinguishable downstream from a deliberate one.

Which tool

metadata.tools.components[0] is the command that authored the document, with its real version, its purl and links back to the project:

{
  "type": "application",
  "bom-ref": "urn:tool:vulnetix-sca",
  "group": "Vulnetix",
  "name": "vulnetix-sca",
  "version": "3.98.0",
  "purl": "pkg:golang/github.com/vulnetix/cli@v3.98.0",
  "externalReferences": [
    { "type": "website", "url": "https://www.vulnetix.com" },
    { "type": "vcs", "url": "https://github.com/vulnetix/cli" }
  ]
}

Each command names itself — vulnetix-sca, vulnetix-containers, vulnetix-cdx, vulnetix-aibom, vulnetix-cbom, vulnetix-vex, vulnetix-license-analyzer — because what produced a document is a real question, and “an SBOM built from manifests” differs from “an inventory read out of a container image” in ways the component list alone does not show.

Re-scanning the same repository

A re-scan writes the next revision of the same document: the serialNumber is kept and version increments, so two revisions can be ordered. Tool entries from other producers are preserved; this CLI’s own entry is replaced with the version that just ran, rather than sitting beside the older one.

Documents this CLI did not author

A command that transforms someone else’s document — bom enrich, bom import — appends itself to metadata.tools and leaves manufacturer and authors exactly as it found them. Enriching Syft’s SBOM does not make us its author. See Attribution when enriching.

Output Files

After every scan the following files are written under your project root:

PathDescription
.vulnetix/sbom.cdx.jsonCycloneDX 1.7 SBOM for all scanned packages
.vulnetix/sast.sarifSARIF 2.1.0 report from SAST analysis (written when any SAST sub-category runs)
.vulnetix/memory.yamlScan state record (timestamp, counts, git context) — also stores your ignore rules under a suppressions: block

Suppressing findings. To drop findings you have triaged (false positives, accepted risks, mitigated issues) use nosec comments in code or the vulnetix ignore command. Scans report how many findings each pass suppressed, so nothing disappears silently.

Package Scope Support

Vulnerabilities and packages are organised by dependency scope where the package manager provides that information:

Package ManagerScopes
npmproduction, development, peer, optional
Python (Pipfile)production, development
Python (requirements.txt)production
Goproduction (no scope distinction in go.mod / go.sum)
Rustproduction (no scope distinction in Cargo.lock)
Rubyproduction (group info requires Gemfile)
Maven / Gradleproduction, test, provided, runtime, system
Composerproduction, development
Yarn / pnpmproduction (scope requires correlation with package.json)
NuGetproduction
Swiftproduction
Pub (Dart)production, development
Hex (Elixir)production
sbt (Scala)production
CocoaPodsproduction
Conan (C/C++)production
vcpkg (C/C++)production

Supported Manifest Files

The scanner recognizes these package manager manifest and lock files:

JavaScript / TypeScript

FilenameEcosystemLock file?
package.jsonnpmNo
package-lock.jsonnpmYes
npm-shrinkwrap.jsonnpmYes
yarn.locknpmYes
pnpm-lock.yamlnpmYes

When package.json is present without package-lock.json, yarn.lock, or pnpm-lock.yaml, SCA resolves from the installed node_modules/ tree. The scan fails before the VDB lookup if node_modules/ is missing or if any direct dependency declared in package.json is not installed. When the install tree is present, Vulnetix reads each installed package’s package.json, uses the actual installed versions in the /v2/cli.sca request, and adds installed undeclared packages as transitive dependencies where it can connect them through package manifest dependency fields.

Python

FilenameEcosystemLock file?
pyproject.tomlPyPINo
requirements.txtPyPINo
requirements.inPyPINo
setup.pyPyPINo
setup.cfgPyPINo
PipfilePyPINo
Pipfile.lockPyPIYes
poetry.lockPyPIYes
uv.lockPyPIYes

Conda

FilenameEcosystemLock file?
environment.ymlCondaNo
environment.yamlCondaNo

Nested pip: dependencies inside a Conda environment.yml are attributed to PyPI.

Go

FilenameEcosystemLock file?
go.modGoNo
go.sumGoYes

Rust

FilenameEcosystemLock file?
Cargo.tomlCargoNo
Cargo.lockCargoYes

Deno

FilenameEcosystemLock file?
deno.jsonDenoNo
deno.lockDenoYes

Ruby

FilenameEcosystemLock file?
GemfileRubyGemsNo
Gemfile.lockRubyGemsYes

Java / Kotlin / Scala

FilenameEcosystemLock file?
pom.xmlMavenNo
build.gradleMavenNo
build.gradle.ktsMavenNo
gradle.lockfileMavenYes
build.sbtMavenNo
build.lockMavenYes
build.scMavenNo

Clojure

FilenameEcosystemLock file?
project.cljClojarsNo
deps.ednClojarsNo
bb.ednClojarsNo

Clojure coordinates resolve as Maven group:artifact PURLs.

C# / .NET

FilenameEcosystemLock file?
*.csprojNuGetNo
*.fsprojNuGetNo
*.vbprojNuGetNo
packages.configNuGetNo
packages.lock.jsonNuGetYes
paket.dependenciesNuGetNo
paket.lockNuGetYes

PHP

FilenameEcosystemLock file?
composer.jsonComposerNo
composer.lockComposerYes

Swift / iOS

FilenameEcosystemLock file?
Package.swiftSwiftNo
Package.resolvedSwiftYes
PodfileCocoaPodsNo
Podfile.lockCocoaPodsYes
CartfileCarthageNo
Cartfile.resolvedCarthageYes

Dart

FilenameEcosystemLock file?
pubspec.yamlPubNo
pubspec.lockPubYes

Elixir

FilenameEcosystemLock file?
mix.exsHexNo
mix.lockHexYes

Erlang

FilenameEcosystemLock file?
rebar.configHexNo
rebar.lockHexYes

C / C++

FilenameEcosystemLock file?
vcpkg.jsonvcpkgNo
conanfile.txtConanNo
conanfile.pyConanNo
conan.lockConanYes
CMakeLists.txtCPMNo
CPM.cmakeCPMNo
meson.buildMesonNo

CMakeLists.txt is only recognized when the file contains CPMAddPackage.

Haskell

FilenameEcosystemLock file?
*.cabalCabalNo
cabal.project.freezeCabalYes
stack.yamlStackNo
package.yamlhpackNo

package.yaml is only recognized when the file is an hpack package description (it declares dependencies alongside an hpack section such as library: or executables:).

OCaml

FilenameEcosystemLock file?
opamopamNo
*.opamopamNo

Nix

FilenameEcosystemLock file?
flake.nixNixNo
flake.lockNixYes

Julia

FilenameEcosystemLock file?
Project.tomlJuliaNo
Manifest.tomlJuliaYes

Crystal

FilenameEcosystemLock file?
shard.ymlCrystalNo
shard.lockCrystalYes

R

FilenameEcosystemLock file?
DESCRIPTIONCRANNo
renv.lockCRANYes

Zig

FilenameEcosystemLock file?
build.zig.zonZigNo

Bazel / Buck

FilenameEcosystemLock file?
MODULE.bazelBazelNo
WORKSPACEBazelNo
WORKSPACE.bazelBazelNo
BUCKBuckNo
BUCK2BuckNo

Containers

FilenameEcosystemLock file?
DockerfileDockerNo
ContainerfileDockerNo
*.dockerfileDockerNo
*.containerfileDockerNo
compose.yaml / docker-compose.ymlDockerNo

Container images from FROM lines, Compose image: fields, Kubernetes manifests, and Helm charts are emitted as pkg:oci/… components. Each carries a vulnetix:oci:registryType (dockerhub/gcr/ecr/acr/ghcr/gitlab/quay/local/private) and vulnetix:oci:private property so private-registry usage is visible in the BOM.

Kubernetes

FilenameEcosystemLock file?
*.yaml / *.yml with apiVersion + kindKubernetesNo

Container images are extracted from every pod-template shape — Pod, Deployment, ReplicaSet, StatefulSet, DaemonSet, Job and CronJob — including initContainers and ephemeralContainers.

Helm

FilenameEcosystemLock file?
Chart.yamlHelmNo

Chart dependencies become pkg:helm/… components; container images declared in a sibling values.yaml (image: shorthand or repository:/tag: blocks) are emitted as pkg:oci/….

Registry config

FilenameEcosystemParsed for deps?
.npmrc / .yarnrc / .yarnrc.ymlnpmNo (registry endpoints only)
settings.gradle / settings.gradle.ktsMavenNo (registry endpoints only)

These are not parsed for dependencies. The scanner extracts declared registry endpoints (never auth tokens) and prints an informational note when a private/custom registry is configured — a supply-chain visibility signal.

Infrastructure as Code

FilenameEcosystemLock file?
*.tfTerraformNo

CI/CD

FilenameEcosystemLock file?
.github/workflows/*.ymlGitHub ActionsNo
.github/workflows/*.yamlGitHub ActionsNo

Existing SBOMs

The scanner also detects and ingests existing SBOM documents:

  • SPDX JSON documents (identified by spdxVersion and SPDXID fields)
  • CycloneDX JSON documents (identified by bomFormat: "CycloneDX" and specVersion)

License Analysis

By default, vulnetix scan also runs license analysis on all discovered packages. License findings appear in the pretty output after the vulnerability summary and are stored in the CycloneDX BOM with source vulnetix-license-analyzer.

License resolution uses a multi-source pipeline (manifests, filesystem, container metadata, deps.dev, GitHub). See the License Command Reference for full details on resolution sources, evaluation rules, and conflict detection.

To skip license analysis:

vulnetix scan --no-licenses

Feature Control

By default, vulnetix scan runs all analysis categories: SCA, SAST (including secret detection, container rules, and IaC rules), and license analysis.

Enabling individual features (exclusive mode)

When any --evaluate-X flag is set, only the explicitly enabled categories run — everything else is disabled. This makes it easy to scope a scan to a single concern:

# SCA only (vulnerability analysis on package manifests)
vulnetix scan --evaluate-sca

# SAST only (general static analysis rules)
vulnetix scan --evaluate-sast

# Secret detection only
vulnetix scan --evaluate-secrets

# Container analysis only (Dockerfile/OCI rules)
vulnetix scan --enable-containers

# IaC analysis only (Terraform, Nix)
vulnetix scan --evaluate-iac

# License analysis only
vulnetix scan --evaluate-licenses

Disabling individual features

--no-X flags disable a specific category while leaving all others enabled:

# Run everything except license analysis
vulnetix scan --no-licenses

# Run everything except secret-detection rules
vulnetix scan --no-secrets

# Skip IaC files and rules
vulnetix scan --no-iac

# Skip containers
vulnetix scan --no-containers

# Skip all SAST-kind rules (but still run secrets/containers/IaC rules)
vulnetix scan --no-sast

Specialized scan commands

For convenience, dedicated top-level commands wrap common single-category scans:

CommandEquivalent
vulnetix scavulnetix scan --evaluate-sca --no-sast --no-secrets --no-containers --no-iac --no-licenses
vulnetix sastvulnetix scan --evaluate-sast --no-sca --no-secrets --no-containers --no-iac --no-licenses
vulnetix secretsvulnetix scan --evaluate-secrets --no-sast --no-sca --no-containers --no-iac --no-licenses
vulnetix containersvulnetix scan --enable-containers --no-sast --no-sca --no-secrets --no-iac --no-licenses
vulnetix iacvulnetix scan --evaluate-iac --no-sast --no-sca --no-secrets --no-containers --no-licenses

SAST (Static Application Security Testing)

By default, vulnetix scan also runs SAST analysis alongside SCA. SAST evaluates Rego-based rules against your source files to detect code-level security issues — weak cryptography, hardcoded credentials, missing lock files, insecure deserialization, and more.

SAST findings appear in the pretty output after the SCA summary and are written to .vulnetix/sast.sarif in SARIF 2.1.0 format. Each finding includes CWE, CAPEC, and MITRE ATT&CK technique mappings, plus stable fingerprints for tracking results across runs.

SAST rules are organized into four sub-categories that can be toggled independently:

Sub-categoryFlag to disableRule kindApplies to
Static analysis--no-sastsastGeneral code security rules
Secret detection--no-secretssecretsHardcoded credentials, API keys, tokens
Container rules--no-containersociDockerfile / Containerfile analysis
IaC rules--no-iaciacTerraform HCL, Nix files

Disabling SAST

# Skip all SAST-kind rules (secrets/containers/IaC still run)
vulnetix scan --no-sast

# Skip secret-detection rules specifically
vulnetix scan --no-secrets

# Skip all SAST entirely (all four sub-categories)
vulnetix scan --no-sast --no-secrets --no-containers --no-iac

# Skip built-in rules only (external rule repos still loaded)
vulnetix scan --disable-default-rules --rule myorg/my-rules

Listing Built-in Rules

Prints all all built-in rules with their IDs, names, severities, and language targets, then exit:

vulnetix scan --list-default-rules

External Rule Repos

Load additional Rego rules from Git repositories. See the Custom Rule Repositories guide for a full walkthrough including repository setup, rule authoring, and all flag combinations. Rules are fetched from GitHub by default or from a custom registry.

# Load rules from a GitHub repo
vulnetix scan --rule myorg/custom-rules

# Load multiple external rule repos
vulnetix scan --rule myorg/rules-a --rule myorg/rules-b

# Use a custom registry (e.g. GitLab or self-hosted Gitea)
vulnetix scan --rule myorg/rules --rule-registry https://gitlab.example.com

SAST Severity Gating

SAST findings participate in --severity gating alongside SCA vulnerabilities. When any SAST finding meets or exceeds the threshold, the scan exits with code 1:

# Exit 1 on high or critical SAST findings (or SCA vulnerabilities)
vulnetix scan --severity high

Built-in Rules

27 rules ship with the CLI. Run vulnetix scan --list-default-rules for full descriptions and CWE/CAPEC/ATT&CK mappings.

Cryptography

Rule IDSeverityNameLanguages
VNX-CRYPTO-001mediumMD5 usage detectedPython, Node, Go, Java, Ruby, PHP
VNX-CRYPTO-002mediumSHA-1 usage detectedPython, Node, Go, Java, Ruby, PHP

Container Security

Rule IDSeverityNameApplies to
VNX-DOCKER-001mediumDockerfile missing USER directiveDockerfile, Containerfile
VNX-DOCKER-002mediumDockerfile FROM :latest tagDockerfile, Containerfile

Go

Rule IDSeverityName
VNX-GO-001highMissing go.sum
VNX-GO-002highCommand injection via exec.Command

Java

Rule IDSeverityName
VNX-JAVA-001highCommand injection via Runtime.exec()
VNX-JAVA-002mediumSpring actuator endpoints exposed

Node.js

Rule IDSeverityName
VNX-NODE-001highMissing npm lock file
VNX-NODE-002higheval() or new Function() in JavaScript
VNX-NODE-003highCommand injection via child_process
VNX-NODE-004mediumExpress app without helmet
VNX-NODE-005mediuminnerHTML or dangerouslySetInnerHTML usage

PHP

Rule IDSeverityName
VNX-PHP-001highMissing composer.lock
VNX-PHP-002highDangerous function in PHP

Python

Rule IDSeverityName
VNX-PY-001highMissing Python lock file
VNX-PY-002higheval()/exec() usage in Python
VNX-PY-003highInsecure deserialization with pickle
VNX-PY-004highyaml.load() without SafeLoader
VNX-PY-005mediumWeak PRNG for security operations
VNX-PY-006mediumDjango DEBUG=True

Ruby

Rule IDSeverityName
VNX-RUBY-001highMissing Gemfile.lock
VNX-RUBY-002higheval() or system() in Ruby

Rust

Rule IDSeverityName
VNX-RUST-001highMissing Cargo.lock

Secrets & Credentials

Rule IDSeverityName
VNX-SEC-001criticalAWS access key ID
VNX-SEC-002criticalPrivate key committed
VNX-SEC-004criticalGitHub or GitLab token

SARIF Output

SAST results are always written to .vulnetix/sast.sarif. To write a combined SARIF report (SCA + SAST) to a custom path, use --output:

# Write combined SARIF to a file; pretty summary still goes to stdout
vulnetix scan --output results.sarif

# Write both CycloneDX and SARIF files alongside pretty output
vulnetix scan --output sbom.cdx.json --output results.sarif

The auto-written .vulnetix/sast.sarif contains SAST-only findings. The --output *.sarif file contains all scan findings (SCA + SAST) in SARIF format.

Auto-Discovery

The scanner walks the directory tree starting from --path up to --depth levels deep. It automatically skips common non-project directories:

  • node_modules, .git, .hg
  • __pycache__, .tox, .venv
  • vendor, .cargo

Use --exclude to skip additional paths by glob pattern.

Dependency graph from installed packages

After parsing manifests, the scanner also reads locally installed package directories to build a complete transitive dependency graph. This improves SBOM dependencies section accuracy and powers --paths output — no extra flags required.

EcosystemInstall directory
npmnode_modules/ (follows symlinks for pnpm)
Pythonvenv site-packages/*.dist-info/METADATA
Rustcargo metadata subprocess
Rubyvendor/bundle/ or GEM_HOME gemspec files
PHPvendor/*/composer.json
Gogo mod graph subprocess

If the install directory is not present, the scanner falls back to lock-file edge parsing where available.

For npm projects without a lock file, the installed node_modules/ tree is not just an edge source: it is the authoritative source for resolved package versions. Orphaned installed packages are tolerated as best-effort transitives, but missing direct dependencies stop the scan early because the CLI cannot safely infer what version should be reported.

Examples

Auto-discover and scan the current directory

vulnetix scan

Scan a specific project directory

vulnetix scan --path /path/to/project --depth 5

Exclude test fixtures and vendor directories

vulnetix scan --exclude "test/**" --exclude "vendor/**"

Emit CycloneDX JSON to stdout

vulnetix scan --output json-cyclonedx

Emit SARIF JSON to stdout

vulnetix scan --output json-sarif

Write CycloneDX to a file (pretty output still goes to stdout)

vulnetix scan --output /tmp/sbom.cdx.json

Write SARIF to a file (pretty output still goes to stdout)

vulnetix scan --output /tmp/results.sarif

Write both CycloneDX and SARIF files alongside pretty output

vulnetix scan --output /tmp/sbom.cdx.json --output /tmp/results.sarif

Raw CycloneDX JSON for scripting

vulnetix scan -o json-cyclonedx | jq '.components'

Suppress progress indicators (useful in CI without a TTY)

vulnetix scan --no-progress

Break the build on high or critical vulnerabilities

# Exit 1 if any high or critical vulnerability is found
vulnetix scan --severity high

# Exit 1 on any scored severity and write a CycloneDX BOM
vulnetix scan --severity low -o dist/sbom.cdx.json

Gate on EOL runtimes and packages

# Exit 1 when a runtime or package dependency is end-of-life
vulnetix scan --block-eol

# Combine with severity gating
vulnetix scan --block-eol --severity high

Gate on version lag (supply chain freshness)

# Exit 1 if using the very latest published version of any dependency
vulnetix scan --version-lag 1

# Exit 1 if any dependency is within the 3 most recent releases
vulnetix scan --version-lag 3

Gate on recently published dependencies (cooldown period)

# Exit 1 if any dependency was published in the last 3 days
vulnetix scan --cooldown 3

# Combine multiple supply chain gates
vulnetix scan --block-malware --block-unpinned --version-lag 1 --cooldown 3 --severity high

Suppress output when clean (results-only mode)

# No output when scan is clean; table appears only when findings exist
vulnetix scan --results-only

SAST — list, disable, and load custom rules

# List all built-in SAST rules and exit
vulnetix scan --list-default-rules

# Skip all SAST-kind rules (general static analysis)
vulnetix scan --no-sast

# Skip secret detection
vulnetix scan --no-secrets

# Skip all SAST entirely (all four sub-categories)
vulnetix scan --no-sast --no-secrets --no-containers --no-iac

# Skip built-in rules but load a custom rule repo from GitHub
vulnetix scan --disable-default-rules --rule myorg/custom-rules

# Load additional rules on top of the built-in set
vulnetix scan --rule myorg/extra-rules

# Use a self-hosted registry for custom rules
vulnetix scan --rule myorg/rules --rule-registry https://git.example.com

Suppress extra sections

# Skip exploit intelligence and remediation details
vulnetix scan --no-exploits --no-remediation

Skip license analysis

vulnetix scan --no-licenses

Dry run (detect files, no API calls)

vulnetix scan --dry-run

Reconstruct results from previous scan

# From memory — no API calls
vulnetix scan --from-memory

# Replaying stored results — prefer `vulnetix report`
vulnetix report --fresh-exploits
vulnetix report --fresh-advisories

# The deprecated equivalents (still accepted)
vulnetix scan --from-memory --fresh-exploits
vulnetix scan --from-memory --fresh-advisories

Org Quality Gate Policy

When you run a scan while authenticated and your organization has configured a Quality Gate, its settings are pulled in before the gate is evaluated and override the matching scan flag defaults — org policy always wins, even over a flag you pass explicitly. The override applies to --severity, --block-eol, --block-malware, --block-unpinned, --exploits, --version-lag, --cooldown, --sca-autofix-strategy, and --sca-autofix-max-major-bump. Settings the org left unset fall back to your flag or the builtin default.

Run with --verbose to see which settings were applied or superseded. Inspect the active policy with:

vulnetix config get quality-gate

Non-authenticated scans (no credentials, or the community fallback) have no organization to read from and use only the CLI flags you pass.

Exit Codes

CodeMeaning
0Scan completed successfully (no threshold breach)
1A gate was breached (--severity, --block-eol, --block-malware, --block-unpinned, --exploits, --version-lag, --cooldown), or a fatal error occurred — including a file requested with -o <path> that could not be written
2A local argument or configuration error, caught before any scanning started
3With --jail: the policy ran but could not reach a verdict, because some rules have no current scan coverage. Not a pass and not a breach