vet: scanning lockfiles, workflows and agent configs for supply chain risk
Protect against malicious open source packages 🤖
At a glance
- What is it?
- vet is a Go tool that reads the lockfiles, manifests, CI workflows and AI agent configs of a project and reports malicious packages, known vulnerabilities and risky workflow patterns, with policy as code in CEL deciding when a scan fails. It never installs or runs the dependencies it inspects, and the v2 branch is in alpha while v1 remains the latest release.
- Who is it for?
- vet suits a team that wants a supply chain check in pull request review and in CI, that wants the decision about what fails written as a policy file rather than left to a person, and that is willing to run a tool that reaches a hosted intelligence service.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 2 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The threat model in one line
The Why vet section is the shortest and the most important part of the README, and it reads like a threat model. Your dependencies, your GitHub Actions workflows and your AI agent configs run code that you did not write, and vet finds the risk in them. The tagline at the top of the page frames the same idea from the other side, to find malicious packages, vulnerabilities and risky CI workflows before they reach production. So the scope is three things a developer pulls in but does not author, the packages, the workflows, and the agent configuration files. That third category is new and is the reason the tool is not just a software composition analysis tool, and the two AI features, endpoint audit and agent mode, exist because the same reasoning applies to the tools an agent runtime loads.
What it checks, and the two claims worth checking for yourself
The feature list is six items, and they divide into a detection half and a decision half. Malicious packages are checked against SafeDep Threat Intel, which analyzes new package versions when the registries publish them. Known vulnerabilities come with the fixed version to upgrade to. Risky workflows cover dangerous triggers, template injection, exposed secrets and actions with no pinned commit SHA. Lockfile tampering and fresh versions cover entries from an untrusted registry and versions inside the cooldown window. Policy as code is rules in CEL that decide when a scan fails, and you can test a rule on a saved scan. And AI and crypto inventory lists the AI libraries and crypto algorithms your code calls, writing a CycloneDX xBOM and CBOM. Two claims in that list are worth holding loosely, since the README states that vet needs no account and sends no source code, while the go.mod includes a posthog-go dependency and the module path is safedep/vet/v2, so the intelligence is a hosted service.
What it scans, and what it explicitly does not do
The scan targets table is wider than the lockfile idea suggests. A directory is the default, and you can point at a subdirectory. A git repository can be scanned by URL. A container image can be scanned, either as an OCI reference such as oci://alpine:3.20 or as an image tarball. An SBOM in CycloneDX or SPDX form can be scanned. A single package can be scanned with a package URL such as pkg:npm/[email protected]. And there is a separate command, vet endpoint audit, which lists the tools on the machine. The lockfiles and manifests it reads cover npm, PyPI, Go, Maven, Gradle, Cargo, RubyGems, NuGet, Packagist, Pub and more, and it also reads the GitHub Actions workflows and the AI agent configs of the target. The sentence that matters is the last one in that paragraph, that it does not install or run any of them.
vet scan
vet scan ./service
vet scan https://github.com/safedep/vet
vet scan oci://alpine:3.20
vet scan sbom.cdx.json
vet scan pkg:npm/[email protected]
vet endpoint auditPolicy as code, and the suppression with an expiry date
A policy decides when a scan fails, and the file is a YAML document with a version, a list of rules and a list of suppressions. Each rule has an id, an optional description, a when expression and an action, and the expressions are CEL predicates over the finding and the package. The examples show a rule that fails on a finding whose family is malware, a rule that fails on the dangerous-trigger and template-injection control ids, a rule that fails on a critical vulnerability, and a rule that fails when a pull request adds a package version less than fourteen days old. Suppressions are keyed by a package URL and carry a reason and an expires date, and after the expiry date the suppression no longer hides the finding. That expiry field is the detail worth copying, because a suppression without an end date is a permanent exemption that nobody reviews.
version: 2
rules:
- id: no-malware
description: A malicious or suspicious package fails the build.
when: finding.family == "malware"
action: fail
- id: no-workflow-injection
when: finding.control_id in ["dangerous-trigger", "template-injection"]
action: fail
- id: new-dependency-age
when: package.change == "ADDED" && has(package.days_since_publish) && package.days_since_publish < 14
action: fail
suppressions:
- purl: pkg:npm/[email protected]
reason: The upgrade to 4.18.1 is in pull request 42.
expires: 2026-12-31Testing a policy on a saved scan before you apply it
The workflow for adopting a policy is two commands, and the first one is the one that makes the second safe. You test the policy on the last scan with no new scan, using vet report show last with the policy file, and then you apply it to a pull request with vet scan with the base-ref flag and the policy file. So a policy can be evaluated against the history of your project before it starts failing builds, which is the difference between tuning a gate and being surprised by it. The base-ref option also serves a second purpose, since it reports only the packages and workflows that a change adds, so a pull request that touches one dependency is not reported against the entire existing dependency set. There is also a SARIF export, vet report show with the report option, which writes SARIF from the saved scan with no new scan, for GitHub code scanning.
vet report show last --policy vet-policy.yml
vet scan --base-ref origin/main --policy vet-policy.yml
vet scan --fail-on high
vet scan --base-ref origin/main --fail-on high
vet report show --report sarif=vet.sarifFour install channels, and the alpha state of the v2 branch
The install table lists four channels, and the caveat above it is the important part. This branch is vet v2, in alpha, while vet v1 is on main and stays the latest release, and the v2.0.0 release notes map each v1 command and flag to v2. The channels are Homebrew with a cask from the safedep tap, mise, Go with go install of the cmd/vet package, and a container run against ghcr.io/safedep/vet. The mise channel installs a release one day after its publication, and the install documentation has the command for the newest build, the release binaries and the build attestations. The release tags on the v2 branch are dated alphas, v2.0.0-alpha builds from consecutive days in October 2026, so the v2 line is being cut continuously and none of it is a stable release yet.
brew install --cask safedep/tap/vet@edge
mise use -g 'github:safedep/vet[prerelease=true]@2'
go install github.com/safedep/vet/v2/cmd/vet@latest
docker run --rm -v "$PWD:/src" -w /src ghcr.io/safedep/vet:v2-latest scanThe AI-facing parts: agent mode, endpoint audit and the xBOM
Three features are aimed at the AI tooling in a development environment rather than at the code in the repository. The endpoint audit command lists the AI tools, MCP servers, editor extensions and global npm packages on a developer machine and checks them for malware, which is a different threat surface from the repository you cloned. Agent mode finds Claude Code and other agents and then writes JSON and never prompts, so an agent cannot stall the scan with a question. And the inventory feature produces a CycloneDX 1.7 xBOM of the AI libraries and the crypto the code uses, alongside a CBOM. The policy documentation also references an Agent Skill in the .claude/skills directory that writes a policy for you, and the use case list mentions a GitHub Action that comments on a pull request adding a package, a workflow or a finding, and fails the check on an attack.
The build system, and what the dependency list reveals
The repository is a Go workspace with a JavaScript workspace on top. The go.mod requires go 1.26.3 and pulls in a set of libraries that map directly onto the feature list: cel-go for the policy language, cyclonedx-go and go-spdx for the inventory formats, osv-scalibr and rustaudit for the vulnerability side, packageurl-go for the package URLs that key the suppressions, go-containerregistry and the moby client for image scanning, go-git for the repository target, and go-tree-sitter for parsing, which is how the tool reads source files well enough to find the crypto and AI library calls. There is a SafeDep gRPC API client generated from a buf.build schema, which is the transport to the hosted intelligence service, and posthog-go, which is the telemetry dependency worth noting given the README's claim that no account is needed. The Makefile is thorough, with a check target that builds, vets for Linux, macOS and Windows, runs the tests, lints only the changed lines against origin/v2 and runs a hermetic acceptance suite.
Editorial conclusion
vet suits a team that wants a supply chain check in pull request review and in CI, that wants the decision about what fails written as a policy file rather than left to a person, and that is willing to run a tool that reaches a hosted intelligence service. It is the wrong choice if you need the check to run with no network access at all, since the malicious package detection depends on SafeDep Threat Intel, and a poor fit if you want the scanner to be the only source of truth, because vet reports what it finds and the policy decides what to fail on, leaving judgement to you. Before adopting the v2 branch, read the v2.0.0 release notes, since they map each v1 command and flag to v2, and check whether the policy file you write is portable between the two lines.
Frequently asked questions
What does vet check?
It checks each package against SafeDep Threat Intel, known vulnerabilities with the fixed version to upgrade to, risky workflows such as dangerous triggers, template injection, exposed secrets and actions with no pinned commit SHA, lockfile tampering from untrusted registries and versions inside a cooldown window, and it inventories the AI libraries and crypto algorithms your code calls.
What can vet scan?
A directory, which is the default, a git repository by URL, a container image as an OCI reference or an image tarball, an SBOM in CycloneDX or SPDX form, a single package by package URL, and the tools on the machine with vet endpoint audit. It reads lockfiles and manifests for npm, PyPI, Go, Maven, Gradle, Cargo, RubyGems, NuGet, Packagist, Pub and more.
How do I write a vet policy?
In a vet-policy.yml file with a version, a list of rules and a list of suppressions. Rules have an id, a when expression written in CEL and an action, and suppressions are keyed by a package URL with a reason and an expires date after which the finding reappears. The README points at a policy.md page and an Agent Skill that writes a policy for you.
Does vet install or run the dependencies it scans?
No. The documentation states that vet reads the lockfiles and manifests of the supported ecosystems along with the GitHub Actions workflows and the AI agent configs, and that it does not install or run any of them. Controls then turn that data into findings.
Is vet v2 stable?
No, the v2 branch is in alpha, with dated alpha releases such as v2.0.0-alpha.20261014124335. The stable line is v1 on the main branch and stays the latest release, and the v2.0.0 release notes map each v1 command and flag to v2.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/safedep-vet)Was this useful?