# AI Tools Discovery
Source: https://docs.safedep.io/ai-security/ai-tools-discovery
Discover AI coding agents, MCP servers, CLI tools, IDE extensions, and agent skills across developer machines and project repositories
`vet ai discover` is an alias for `vet endpoint scan --kind ai-tool --kind agent-skill`. It scans the local system and project directory to inventory AI tool usage signals: coding agents, MCP servers, CLI tools, IDE extensions, project configuration files, and agent skills. When SafeDep credentials are configured, discovered items sync to SafeDep Cloud's Endpoint Hub automatically. See [Inventory](/governance/cloud/endpoint-hub/inventory).
## Prerequisites
* `vet` [installed](/governance/vet/quickstart)
## Usage
Discover all AI tool usage signals on the current system and project:
```bash theme={null}
vet ai discover
```
### Scope Filtering
Limit discovery to system-level or project-level signals:
```bash theme={null}
# Only system-level signals (global configs, CLI binaries, IDE extensions)
vet ai discover --scope system
# Only project-level signals for a specific repository
vet ai discover --scope project -D /path/to/repo
```
### JSON Output
Write a structured JSON inventory for downstream processing:
```bash theme={null}
vet ai discover --report-json inventory.json
# JSON only, suppress table output
vet ai discover --report-json inventory.json --silent
```
## What Gets Discovered
`vet ai discover` reports **usage signals**, not unique tools. The same tool may appear multiple times because it can be configured at different scopes. Each row represents a distinct configuration entry.
For example, Claude Code might produce:
| Type | Name | Scope | Why |
| ---------------- | ----------- | ------- | --------------------------------------- |
| `coding_agent` | Claude Code | system | `~/.claude/settings.json` exists |
| `project_config` | Claude Code | project | Project has a `CLAUDE.md` |
| `mcp_server` | my-server | system | Configured in `~/.claude/settings.json` |
| `mcp_server` | my-server | project | Also configured in `.mcp.json` |
### Signal Types
The `--kind` flag on `vet endpoint scan` controls which signal types are collected. `vet ai discover` always collects all of them.
| Type | Kind | Description |
| ------------------- | ------------- | -------------------------------------------------------------------------------------------------------------- |
| **coding\_agent** | `ai-tool` | AI coding assistant installed on the system, detected via system-level config directories |
| **mcp\_server** | `ai-tool` | Model Context Protocol server configured for an application |
| **cli\_tool** | `ai-tool` | Standalone AI CLI binary found on `$PATH`, verified by executing with a version flag |
| **ai\_extension** | `ai-tool` | AI-related IDE extension detected from installed extension manifests |
| **project\_config** | `ai-tool` | AI tool configuration or instruction file found in a project repository |
| **agent\_skill** | `agent-skill` | Agent skill directory discovered in a supported agent's skill path (e.g. `.claude/skills/`, `.agents/skills/`) |
### Scope
* **system** refers to user-global config (e.g. `~/.claude/settings.json`, `~/.cursor/mcp.json`)
* **project** refers to repo-scoped config (e.g. `.mcp.json`, `.cursorrules`, `CLAUDE.md`)
## What Gets Scanned
**App configuration** is read from well-known system and project-level config paths for each supported application. System-level configs indicate the tool is installed; project-level configs indicate the project is set up for a tool.
**CLI binaries** are discovered by searching `$PATH` for known binary names. Each candidate is executed with a version flag and the output verified against known patterns.
**IDE extensions** are discovered by reading extension manifests from supported IDE distributions and matching against a curated list of known AI extension identifiers.
**Agent skill directories** are discovered by scanning known per-agent skill paths at system and project scope.
## Security
Discovery makes no network calls. All scanning reads the local filesystem and `$PATH`. Environment variable and header values are never captured; only key names are recorded. CLI arguments matching secret patterns (`--token=`, `--api-key=`, `--password=`, etc.) are redacted. Sync to SafeDep Cloud is a separate step that runs only when credentials are configured.
Sync discovered AI tools and skills to SafeDep Cloud's Endpoint Hub
Detect AI SDK usage in source code and generate AI-enriched SBOMs
Learn about extended Bill of Materials and signature-based detection
# Gryph
Source: https://docs.safedep.io/ai-security/gryph-overview
Gryph records what your AI coding agents do, every file read, write, and command, to a local audit log you can query and replay.
AI coding agents like Claude Code, Cursor, and Gemini CLI can read any file, write anywhere, and run arbitrary commands on your machine. They fire off dozens of tool calls per session, and when something goes wrong there is usually no record of what happened. **Gryph** gives you that record.
Gryph hooks into your AI coding agents, logs every action to a local audit database, and lets you query, review, and replay agent activity. It is an observability tool. It tells you what an agent did, after the fact. It does not block or sandbox agent actions.
Gryph runs fully locally. All data stays on your machine. There is no cloud component, no telemetry, and no SafeDep account or API key.
## What Gryph does
* **Records agent activity.** Every file read, file write, and command execution becomes a structured event.
* **Stores it locally.** Events go to a local SQLite database on your machine. Nothing is transmitted.
* **Lets you investigate.** Query, filter, and replay sessions to understand and debug what an agent did.
* **Flags sensitive access.** Gryph detects when agents touch sensitive files like `.env`, keys, and secrets, and it can redact or hash captured content.
## Supported agents
Gryph installs lightweight hooks into the agents you already use:
* Claude Code
* Cursor
* Gemini CLI
* Windsurf
* OpenCode
* Codex
* Pi Agent
The list grows over time. See the [Gryph repository](https://github.com/safedep/gryph) for the current set and the events captured for each agent.
## Get started
```bash theme={null}
brew install safedep/tap/gryph
```
Other methods (install script, npm, Go) are in the [Gryph README](https://github.com/safedep/gryph#installation).
```bash theme={null}
gryph install
```
Gryph detects the AI coding agents on your machine and wires up its hooks.
Run your AI coding agent as usual. Gryph records activity in the background.
```bash theme={null}
gryph logs
```
View recent agent activity. Use `gryph query` to filter the audit log and `gryph sessions` to list recorded sessions.
Gryph is young and changes often. For the latest commands, configuration, and supported agents, see the [Gryph repository](https://github.com/safedep/gryph) and its [releases](https://github.com/safedep/gryph/releases).
## How it differs from the SafeDep MCP server
Gryph and the [SafeDep MCP server](/ai-security/mcp-server) both work with AI coding tools, but they solve opposite problems:
* **Gryph** observes the agent. It records what the agent reads, writes, and runs on your machine.
* **The MCP server** gives capabilities to the agent. It lets the agent ask SafeDep "is this package safe?" before suggesting an install.
Use them together. MCP helps the agent make safer suggestions, and Gryph keeps an audit trail of its actions.
Full documentation, configuration, and source.
Give your AI coding tools access to SafeDep package intelligence.
# SafeDep MCP Server
Source: https://docs.safedep.io/ai-security/mcp-server
Protect your AI coding agents against malicious packages using SafeDep MCP
The SafeDep CLI is the fastest way to get started. One command signs you in, sets up your API key, and configures SafeDep in every AI coding agent it finds on your machine. To configure things manually, see [Manual Setup](#manual-setup).
SafeDep monitors npm, PyPI, and other package registries in real time. It stays invisible when packages are safe and surfaces only when it blocks something dangerous.
SafeDep MCP has a free tier. See [pricing](https://safedep.io/pricing) for details.
With SafeDep Cloud, the packages your agents check through the MCP server appear per endpoint under [MCP Advisor](/governance/cloud/endpoint-hub/mcp-advisor) in Endpoint Hub, with the verdict each one got.
## Quick Start
Run the following command in your terminal. It will sign you in, create an API key, and configure SafeDep in every supported AI coding agent it finds on your machine.
```bash theme={null}
npx @safedep/cli setup mcp install
```
```bash theme={null}
pnpx @safedep/cli setup mcp install
```
```bash theme={null}
bunx @safedep/cli setup mcp install
```
Verify the setup by asking your coding agent to install a [test package](#testing). The agent should block it as malicious.
## Endpoints
| Endpoint | Description |
| -------------------------------------------------------------- | --------------------------- |
| `https://mcp.safedep.io/model-context-protocol/threats/v1/mcp` | SafeDep MCP endpoint (HTTP) |
| `https://mcp.safedep.io/model-context-protocol/threats/v1/sse` | Legacy SSE endpoint |
### Authentication
The MCP server requires API key authentication. The following HTTP headers are required:
| Header | Description |
| --------------- | --------------------------------------------------------------- |
| `Authorization` | `` |
| `X-Tenant-ID` | `your-tenant-domain (e.g. default-team.your-domain.safedep.io)` |
Your tenant domain is shown in [SafeDep Cloud settings](https://app.safedep.io/settings/api-keys) after you sign in.
## Manual Setup
To configure an agent manually, or if the CLI did not auto-detect yours, follow the instructions below. Each configuration requires a SafeDep API key and your tenant domain. Create an API key in [SafeDep Cloud settings](https://app.safedep.io/settings/api-keys).
Use `claude` CLI to add the MCP server to your user settings. This configuration will be available across all Claude Code projects.
```bash theme={null}
claude mcp add -s user --transport http safedep \
https://mcp.safedep.io/model-context-protocol/threats/v1/mcp \
--header "Authorization: " \
--header "X-Tenant-ID: "
```
Add the SafeDep MCP server to your Cursor configuration. Create or edit `~/.cursor/mcp.json` in your home directory:
```json theme={null}
{
"mcpServers": {
"safedep": {
"url": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
Restart Cursor after saving the configuration. You can verify the server connection in **Cursor Settings > MCP Servers**.
See the [Cursor MCP documentation](https://cursor.com/docs/context/mcp) for more details.
Add the SafeDep MCP server to your VS Code configuration. Create or edit the user-level `mcp.json` file for your platform:
| Platform | Path |
| -------- | -------------------------------------------------- |
| Linux | `~/.config/Code/User/mcp.json` |
| macOS | `~/Library/Application Support/Code/User/mcp.json` |
| Windows | `%APPDATA%\Code\User\mcp.json` |
```json theme={null}
{
"servers": {
"safedep": {
"type": "http",
"url": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
Reload VS Code after saving the configuration.
Add the SafeDep MCP server to your Gemini CLI configuration. Edit `~/.gemini/settings.json` in your home directory:
```json theme={null}
{
"mcpServers": {
"safedep": {
"httpUrl": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
See the [Gemini CLI repository](https://github.com/google-gemini/gemini-cli) for MCP configuration details.
Add the SafeDep MCP server to your OpenCode configuration. Create or edit `~/.config/opencode/opencode.json` in your home directory:
```json theme={null}
{
"mcp": {
"safedep": {
"type": "remote",
"url": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"enabled": true,
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
Add the SafeDep MCP server to your Antigravity configuration. Create or edit `~/.gemini/antigravity/mcp_config.json` in your home directory:
```json theme={null}
{
"mcpServers": {
"safedep": {
"serverUrl": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
Add the SafeDep MCP server to your Codex configuration. Edit `~/.codex/config.toml` (or `.codex/config.toml` in your project root for project-scoped access):
```toml theme={null}
[mcp_servers.safedep]
url = "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp"
[mcp_servers.safedep.env_http_headers]
"Authorization" = "SAFEDEP_API_KEY"
"X-Tenant-ID" = "SAFEDEP_TENANT_ID"
```
Set the environment variables with your credentials:
```bash theme={null}
export SAFEDEP_API_KEY=""
export SAFEDEP_TENANT_ID=""
```
See the [Codex repository](https://github.com/openai/codex) for MCP configuration details.
Add the SafeDep MCP server to your Windsurf configuration. Create or edit `~/.codeium/windsurf/mcp_config.json` in your home directory:
```json theme={null}
{
"mcpServers": {
"safedep": {
"url": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
See the [Windsurf Cascade MCP documentation](https://docs.windsurf.com/windsurf/cascade/mcp) for more details.
Add the SafeDep MCP server to your Zed configuration. Create or edit `~/.config/zed/settings.json` in your home directory.
```json theme={null}
{
"context_servers": {
"safedep": {
"enabled": true,
"url": "https://mcp.safedep.io/model-context-protocol/threats/v1/mcp",
"headers": {
"Authorization": "",
"X-Tenant-ID": ""
}
}
}
}
```
See the [Zed MCP documentation](https://zed.dev/docs/ai/mcp) for more details.
## Testing
After setup, verify the integration by asking your coding agent to install one of the following test packages:
| Package | Ecosystem |
| ------------------ | --------- |
| `safedep-test-pkg` | npm |
| `safedep-test-pkg` | PyPI |
These packages are harmless but are marked as malicious in the SafeDep database for testing purposes. Your coding agent should block the installation and warn that the package is flagged.
For example, try prompting your agent with:
```
Install the npm package safedep-test-pkg
```
If the MCP server is configured correctly, the agent will check the package against SafeDep's threat intelligence and refuse to install it.
# AI Agent Security
Source: https://docs.safedep.io/ai-security/overview
Discover, audit, and control what AI coding agents access and run across your developer environments.
AI coding agents can read, write, and run almost anything on a developer's machine, and they adopt external components (packages, MCP servers, Agent Skills) faster than any human review. SafeDep helps you see what your agents do and keep them from pulling in malicious components.
**Gryph** records every file and command your AI coding agents touch, in a local audit trail.
The **SafeDep MCP server** lets agents check that a package is safe before suggesting it.
Find the AI agents, MCP servers, and tools in use across your code and machines.
Gain visibility into AI usage across your code and supply chain so you can govern it.
# Community
Source: https://docs.safedep.io/community
Join the SafeDep and Vet user community for support and discussions
Connect with other users, get support, and follow development.
## Discord Community
Discord is the primary community hub. Join to:
* Ask questions and get answers from members and maintainers
* Discuss features and use cases
* Get announcements about new releases
* Connect with other security professionals using Vet and SafeDep
**Join the server:** [https://discord.gg/kAGEj25dCn](https://discord.gg/kAGEj25dCn)
## Other Community Channels
Join conversations about Vet development and usage
Report bugs, request features, or contribute to development
Follow SafeDep for company updates and industry insights
Get quick updates and security tips
## Getting Help
### Before Asking Questions
1. **Check the documentation** - Search the guides and API reference
2. **Review existing issues** - Your question may already be answered
3. **Try debugging steps** - Use debug logs to gather more information
### How to Ask Effective Questions
* What are you trying to accomplish?
* What command did you run?
* What was the expected vs actual behavior?
* Operating system and version
* Vet version (`vet version`)
* Relevant configuration files
* Error messages and logs
* Use code blocks for commands and outputs
* Include relevant parts of manifests or config files
* Sanitize any sensitive information
### Debug Information
```bash theme={null}
# Get version information
vet version
# Enable debug logging and verbose output
vet scan -D /path/to/repo -l- -d
# Log to file for sharing
vet scan -D /path/to/repo -l /tmp/vet.log -d
```
## Contributing
### Ways to Contribute
Help improve Vet by reporting bugs and issues you encounter
Suggest new features or improvements
Help improve documentation with corrections, clarifications, or new content
Contribute bug fixes, features, or performance improvements
### Contribution Guidelines
Before contributing:
1. **Check existing issues** to avoid duplicates
2. **Follow the code of conduct** in all interactions
3. **Use issue templates** when reporting bugs or requesting features
4. **Test your changes** thoroughly before submitting
## Community Guidelines
### Code of Conduct
* **Be respectful** in all interactions
* **Help others** learn and grow
* **Stay on topic** in discussions
* **No spam or self-promotion** without prior approval
* **Report inappropriate behavior** to moderators
Use the right channel for your topic:
* General questions: Discord general channel
* Bug reports: GitHub issues
* Feature requests: GitHub discussions
* Support requests: Discord help channel
## Resources
Complete documentation for Vet and SafeDep Cloud
Sample configurations and use cases
Latest releases and version history
How to report security vulnerabilities
## Contact
* **General support**: [support@safedep.io](mailto:support@safedep.io)
* **Security issues**: Follow the [security policy](https://github.com/safedep/vet/security/policy)
* **Business inquiries**: [hello@safedep.io](mailto:hello@safedep.io)
# CEL
Source: https://docs.safedep.io/concepts/cel
Common Expression Language (CEL) is the syntax SafeDep uses for filters, queries, and policy rules over package data.
[Common Expression Language (CEL)](https://cel.dev/) is a safe, sandboxed expression language. SafeDep adopts it as the common syntax for filtering scan results, running queries, and writing policy rules. A CEL expression evaluates to true or false for each package, so you describe exactly which dependencies you care about.
## Why it matters
One language covers three jobs: ad-hoc filtering during a scan, repeatable queries over saved scan data, and enforceable policy. You learn the syntax once and reuse it everywhere.
## What you can reference
A CEL expression in Vet receives this data about each package:
| Variable | What it holds |
| ----------- | ----------------------------------------------------------------------------------------------- |
| `pkg` | Package coordinates: `ecosystem`, `name`, `version` |
| `vulns` | Vulnerabilities by severity: `all`, `critical`, `high`, `medium`, `low` (each item has an `id`) |
| `scorecard` | OpenSSF Scorecard data: `score` and per-check `scores["Check-Name"]` |
| `projects` | Source projects: `name`, `type`, `stars`, `forks`, `issues` |
| `licenses` | SPDX license identifiers |
A few expressions:
```cel theme={null}
vulns.critical.exists(x, true)
licenses.exists(p, p == "MIT")
projects.exists(x, x.stars < 100) && scorecard.scores.Maintained < 5
```
Hyphenated scorecard checks use the bracket form, for example `scorecard.scores["Token-Permissions"]`.
## Related
The full filter input structure and recipes.
Reuse CEL over saved scan data.
How CEL rules become enforceable policy.
Write policy files with CEL.
# Endpoint
Source: https://docs.safedep.io/concepts/endpoint
An endpoint is a developer machine, CI runner, or agent sandbox that runs SafeDep tooling and reports inventory to Endpoint Hub.
An endpoint is a managed asset, such as a developer machine, a CI runner, or an agent sandbox, that runs SafeDep's open-source tools and reports what it finds to [Endpoint Hub](/governance/cloud/endpoint-hub/overview) in SafeDep Cloud.
An endpoint here is a machine, not an API endpoint. For SafeDep's service hostnames, see [API Endpoints](/reference/endpoints).
## Why it matters
Endpoints give a team visibility into what runs across its development infrastructure: AI coding agents, MCP servers, CLI tools, IDE extensions, and the packages developers install. That inventory is what you govern.
## What an endpoint reports
* AI tooling discovered by `vet endpoint scan`: coding agents, MCP servers, CLI tools, IDE extensions, and Agent Skills.
* Package install activity captured by [PMG](/package-security/pmg/overview) through Package Guard.
Each endpoint appears by hostname in Endpoint Hub, where you browse its inventory and package events. Reporting requires SafeDep Cloud credentials; without them, scans run locally only.
## Related
The console view of your endpoints.
Package activity from each endpoint.
The org boundary endpoints report into.
SafeDep's service hostnames (different meaning).
# Malicious Package
Source: https://docs.safedep.io/concepts/malicious-package
What a malicious open-source package is, how it differs from a vulnerability, and how SafeDep detects them.
A malicious package is an open-source package built or altered to harm whoever installs it: stealing secrets, opening a backdoor, or running unwanted code. Unlike a [vulnerability](/concepts/vulnerability), which is an unintended flaw in an otherwise legitimate package, a malicious package is harmful by design.
## Common forms
* **Typosquatting and dependency confusion:** a package named to be mistaken for a popular or internal one.
* **Malicious install scripts:** code that runs the moment a package is installed, before you ever import it.
* **Backdoors and data exfiltration:** harmful behavior hidden inside otherwise working code.
* **Compromised releases:** malicious code injected into a previously trusted package, usually in a fresh version.
## How SafeDep detects them
SafeDep monitors public package registries (npm, PyPI, RubyGems, and more) and analyzes new and updated packages with:
* **Static analysis** of the package's code,
* **Dynamic analysis** of its runtime behavior (network, file system, and process activity),
* **Metadata analysis** of the package and its publisher.
Suspicious packages are verified by security experts before classification. The result feeds a real-time malicious package database that every SafeDep tool reads from.
```mermaid theme={null}
graph TD
NPM[npm Registry] --> MONITORING[SafeDep Monitoring]
PYPI[PyPI Registry] --> MONITORING
RUBYGEMS[RubyGems Registry] --> MONITORING
OTHER[Other Package Registries] --> MONITORING
MONITORING --> STATIC[Static Code Analysis]
STATIC --> DYNAMIC[Dynamic Analysis]
DYNAMIC --> EXPERT[Expert Verification]
EXPERT --> DATABASE[Real-time Malicious Package Database]
DATABASE --> VET[SafeDep Vet
CI/CD Protection]
DATABASE --> PMG[SafeDep PMG
Developer Environment Protection]
VET --> CICD_BLOCK[Block Malicious Packages in CI/CD]
PMG --> DEV_BLOCK[Block Malicious Packages at Install Time]
classDef benefit fill:#4ade80,stroke:#16a34a,stroke-width:2px,color:#000000
class CICD_BLOCK,DEV_BLOCK benefit
```
## Blocking malicious packages
Detection is how SafeDep knows a package is malicious. Blocking it is the job of [Package Security](/package-security/overview):
* [PMG](/package-security/pmg/overview) blocks them at install time on developer machines.
* [Vet](/governance/vet/overview) blocks them in CI/CD.
* The [SafeDep MCP server](/ai-security/mcp-server) lets AI coding agents check a package before suggesting it.
## Related
The other kind of dependency risk: unintended flaws in legitimate packages.
Block malicious packages at every entry point.
Analyze a package on demand in SafeDep Cloud.
Turn detection into enforceable rules.
# Policy
Source: https://docs.safedep.io/concepts/policy
A SafeDep policy is a set of CEL rules that decide which dependencies pass or fail, enforced during scans and in CI/CD.
A policy turns your supply-chain rules into configuration that Vet evaluates automatically. Instead of reviewing dependencies by hand, you write the rules once and Vet applies them to every package, including transitive ones.
## Why it matters
Manual vetting does not scale and misses transitive dependencies. A policy makes a rule like "block known malware", "no GPL licenses", or "no unmaintained packages" an automated, repeatable check that runs the same way locally and in CI/CD.
## How it works
A policy is a set of [CEL](/concepts/cel) rules, written as a filter suite in a YAML file. Vet evaluates each package against the rules, and you decide what a match does:
* During a local scan, Vet can exit non-zero when a package matches a blocking rule.
* In CI/CD with [vet-action](https://github.com/safedep/vet-action), the policy file is passed via the `policy` input (conventionally `.github/vet/policy.yml`), and `paranoid: true` fails the build on a violation.
See [Policy as Code](/reference/policy-as-code) for the full syntax and examples.
## Related
Write and structure policy files.
The expression syntax policies are built from.
The intelligence policies act on.
Enforce package blocking in JFrog Xray.
# SBOM
Source: https://docs.safedep.io/concepts/sbom
A Software Bill of Materials (SBOM) is a complete inventory of your software's components; an xBOM enriches it with AI, SaaS, and crypto usage.
A Software Bill of Materials (SBOM) is a complete inventory of the components your software depends on, along with security metadata such as known vulnerabilities and licenses. The common interchange format is [CycloneDX](https://cyclonedx.org/).
## SBOM and xBOM
* A plain **SBOM** lists the dependencies declared in your manifests and lockfiles. Vet generates a CycloneDX SBOM as part of a scan.
* An **xBOM** goes further: [xBom](/governance/xbom/overview) analyzes your source code to also surface AI SDKs, SaaS APIs, and cryptographic usage that never appear in a manifest.
Use Vet's SBOM for dependency inventory. Reach for xBom when you also need to see the AI, SaaS, and crypto components your code actually uses.
## Why it matters
An accurate inventory underpins vulnerability management, license compliance, and regulatory requirements. You cannot secure what you have not inventoried.
## Related
Generate an SBOM with Vet.
Enriched BOMs from static code analysis.
Inventory your dependencies with Vet.
# Tenant
Source: https://docs.safedep.io/concepts/tenant
A tenant is your organization's isolated space in SafeDep Cloud, identified by its tenant domain such as your-company.safedep.io.
A tenant is your organization's isolated space in SafeDep Cloud. Your users, API keys, policies, scan data, and endpoints all live inside it, separate from every other tenant.
## Your tenant ID is your tenant domain
The tenant identifier is a domain, for example `your-company.safedep.io`. There is no separate tenant ID to look up. You use the domain to:
* send the `X-Tenant-ID` header on API requests,
* configure Vet with `vet auth configure --tenant your-company.safedep.io`,
* set the `SAFEDEP_TENANT_ID` environment variable.
## Why it matters
The tenant is the boundary for isolation and governance: scan results, policies, and access are all scoped to it. You create and manage API keys inside your tenant from the SafeDep Cloud console.
## Related
Authenticate against your tenant.
Assets that report into your tenant.
Where the X-Tenant-ID header is sent.
# Vulnerability
Source: https://docs.safedep.io/concepts/vulnerability
What a vulnerability is in an open-source dependency, and how SafeDep surfaces and gates them.
A vulnerability is a disclosed security flaw in a package's code. The package is legitimate; the flaw is a mistake, not an attack. Unlike a [malicious package](/concepts/malicious-package), which is harmful by design, a vulnerable package became exploitable by accident.
Vulnerabilities are tracked under identifiers like CVEs and aggregated in open databases such as [OSV](https://osv.dev). Each carries a severity, for example `CRITICAL` or `HIGH`, that signals how urgent a fix is.
## How SafeDep surfaces them
[Vet](/governance/vet/overview) checks every dependency against OSV and reports known vulnerabilities with their severity. You decide what to do about them with [policy](/concepts/policy): for example, fail a build when any dependency has a `CRITICAL` or `HIGH` vulnerability.
## Related
The other kind of dependency risk: packages that are harmful by design.
Gate builds on vulnerability severity.
Scan a repository's dependencies for known vulnerabilities.
Inventory the components you ship.
# Vet FAQ
Source: https://docs.safedep.io/faq
Frequently asked questions about using Vet and troubleshooting common issues
## General Usage
### How do I disable the banner?
```bash theme={null}
export VET_DISABLE_BANNER=1
```
### Something is wrong! How do I debug this?
Enable debug logging:
```bash theme={null}
vet scan -D /path/to/repo -l- -d
```
```bash theme={null}
vet scan -D /path/to/repo -l /tmp/vet.log -d
```
```bash theme={null}
vet scan -D /path/to/repo -l- -v
```
## Installation and Setup
### Which version of Vet should I use?
Always use the latest stable version available:
```bash theme={null}
# Check current version
vet version
# Update via Homebrew (macOS/Linux)
brew upgrade safedep/tap/vet
# Or download latest from GitHub releases
# https://github.com/safedep/vet/releases
```
### Does Vet work offline?
Vet requires internet connectivity to:
* Download vulnerability data from OSV database
* Fetch OpenSSF Scorecard information
* Access package registry metadata
* Communicate with SafeDep Cloud (if using cloud features)
For offline environments, consider using the [JSON dump workflow](/reference/build-your-own-queries) to cache data locally.
### What package managers does Vet support?
Vet supports:
* package-lock.json (npm)
* yarn.lock (Yarn)
* pnpm-lock.yaml (pnpm)
* requirements.txt
* Pipfile.lock (Pipenv)
* poetry.lock (Poetry)
* pyproject.toml
* pom.xml (Maven)
* build.gradle (Gradle)
* gradle.lockfile
* go.mod
* go.sum
* Gemfile.lock (Ruby)
* Cargo.lock (Rust)
* composer.lock (PHP)
* And many more...
## Scanning and Analysis
### Why is my scan taking so long?
Common causes:
Use path exclusions to skip irrelevant directories:
```bash theme={null}
vet scan -D . --exclude 'node_modules/*' --exclude 'test/*'
```
The scan fetches metadata from external sources. Slow internet can impact performance.
Malware detection with `--malware-query` is a fast lookup against SafeDep's
known malicious packages database and adds negligible overhead:
```bash theme={null}
vet scan -D . --malware-query
```
Initial scans may be slower as Vet builds local caches.
### No vulnerabilities found - is this correct?
If Vet reports no vulnerabilities:
1. **Check the package versions** - Ensure you're scanning current dependency versions
2. **Verify manifest files** - Confirm Vet is finding and parsing your package manifests
3. **Check exclusions** - Make sure you haven't excluded relevant directories
4. **Review scan output** - Look for any warnings or errors during scanning
### How do I scan only specific files?
Use the `-M` flag to specify individual manifest files:
```bash theme={null}
# Single file
vet scan -M package-lock.json
# Multiple files
vet scan -M package-lock.json -M requirements.txt
```
## Policy and Filtering
### How do I create effective policies?
Start with a basic vulnerability check, then layer in additional conditions:
```bash theme={null}
--filter 'vulns.critical.size() > 0'
```
Test against known-good and known-bad packages before deploying. Enable warning-only mode first, then switch to blocking once the policy is stable. Add comments to policy files explaining the rationale for each rule.
### Why is my filter not working?
Common causes:
Verify CEL expression syntax:
```bash theme={null}
# Correct
vulns.critical.size() > 0
# Incorrect
vulns.critical.length() > 0 # Use size(), not length()
```
Check the [filter input specification](/reference/filtering) to understand available fields.
Ensure your expression evaluates to true/false:
```bash theme={null}
# Returns boolean
licenses.exists(p, p == "MIT")
# Returns array (won't work as filter)
licenses
```
## Performance and Optimization
### How can I speed up my scans?
Skip irrelevant directories:
```bash theme={null}
vet scan -D . \
--exclude 'test/*' \
--exclude 'docs/*' \
--exclude 'examples/*'
```
Target only relevant package files:
```bash theme={null}
vet scan -M package-lock.json -M requirements.txt
```
Cache enriched data for repeated analysis:
```bash theme={null}
vet scan -D . --json-dump-dir /tmp/cache
vet query --from /tmp/cache --filter 'your-filter'
```
For multiple projects, run scans in parallel or use CI/CD matrix builds.
## CI/CD Integration
### My GitHub Action is failing - what should I check?
Ensure you're using the latest version of vet-action:
```yaml theme={null}
uses: safedep/vet-action@v1 # Use latest stable
```
Check GitHub token permissions:
```yaml theme={null}
permissions:
contents: read
security-events: write # For SARIF upload
pull-requests: write # For PR comments
```
Verify required secrets are set if using SafeDep Cloud:
* `SAFEDEP_CLOUD_API_KEY`
* `SAFEDEP_CLOUD_TENANT_DOMAIN`
### How do I handle false positives in CI?
Create an exceptions file for known false positives:
```yaml theme={null}
- name: Run vet with exceptions
uses: safedep/vet-action@v1
with:
exception-file: '.github/vet-exceptions.yml'
```
Refine your filter expressions to reduce noise:
```bash theme={null}
# Be more specific about severity
--filter 'vulns.critical.size() > 0'
# Instead of
--filter 'vulns.all.size() > 0'
```
The action does not fail the build by default, so you can surface findings without blocking while you tune policies. Just leave `paranoid` off (its default is `false`):
```yaml theme={null}
with:
paranoid: false
```
## Data and Privacy
### What data does Vet collect?
Vet collects:
* **Package metadata** from public registries
* **Vulnerability data** from public databases (OSV, NVD)
* **OpenSSF Scorecard** metrics from public repositories
Only **package coordinates** (ecosystem, name, version) leave your machine. Your source code is never transmitted.
### Does Vet send my code anywhere?
No. Vet reads your manifests, lockfiles, and (when [code analysis](/governance/vet/code-analysis) is enabled) your source code **locally** to identify and trace dependencies. Only package coordinates are sent to SafeDep for vulnerability and malware analysis; your source code never leaves your machine.
### Can I use Vet in air-gapped environments?
Vet requires internet access for vulnerability data and package metadata. For air-gapped environments:
1. **Pre-cache data** using the [JSON dump workflow](/reference/build-your-own-queries)
2. **Use proxy servers** to control external access
3. **Consider enterprise solutions** for offline vulnerability databases
## Troubleshooting
### Common error messages and solutions
* Check that you're in the correct directory
* Verify manifest files exist (package-lock.json, requirements.txt, etc.)
* Use `-M` flag to specify files explicitly
* Check internet connectivity
* Verify firewall/proxy settings
* Try again later (service might be temporarily unavailable)
* Use path exclusions to reduce scope
* Scan smaller directory trees
* Increase available memory in CI/CD
* Check CEL syntax
* Verify field names in filter input spec
* Test expressions incrementally
## Getting More Help
Real-time help and discussions
Report bugs or search existing issues
Guides and API reference
Direct support for complex issues
***
Can't find your question here? Check our [community page](/community) for more ways to get help!
# Choose Your Path
Source: https://docs.safedep.io/get-started/choose-your-path
Start free with the open source tool that fits your use case. Add SafeDep Cloud when your team needs one view.
You do not need all of SafeDep on day one. Start with one free tool that solves your problem today. Add SafeDep Cloud when your team needs shared visibility and control. This page shows you where to start.
## Start free
Pick the task you want to do today. Everything in this list is free. Most items are open source tools; the cards say when an item is a cloud service instead.
PMG wraps your package manager and blocks known malicious packages before their code runs. Open source, no account.
Vet scans your dependencies for malicious packages, vulnerabilities, and policy violations, locally or in CI. Open source, no account.
Query SafeDep's known malicious packages database for a specific package through Vet. A free cloud service, no account needed.
Audit what agents read, write, and run, and discover AI tooling, with open source tools. The SafeDep MCP server adds package vetting for agents; it is a cloud service and needs a SafeDep Cloud account.
xBom builds a bill of materials that also detects AI libraries and SaaS usage in your code. Open source, no account.
Install the SafeDep skill so your coding agent answers and acts from these docs. Open source, no account.
## Add SafeDep Cloud when the need is team-wide
Move to SafeDep Cloud when you need to protect more than one developer, apply one policy across your organization, or answer questions from one place: which machines run PMG, what was blocked last week, which projects have critical vulnerabilities. SafeDep Cloud connects the same tools to a tenant that you can see and query.
SafeDep Cloud is the hosted control plane. The open source tools keep working without it, and nothing above stops working when you add it.
Create a tenant, sign in the safedep CLI, connect a source, and run your first query.
Feed GitHub, CI/CD, PMG, and MCP activity into one tenant.
See package activity and AI tooling across developer machines, CI runners, and agent sandboxes.
Ask your tenant questions in plain English through your AI coding agent.
# SafeDep CLI Tools
Source: https://docs.safedep.io/get-started/cli-tools
Which SafeDep command-line tool to use: Vet, PMG, Gryph, and the safedep CLI, and how they fit together.
SafeDep ships several command-line tools. Each one solves a different supply-chain problem, and they run independently, so you install only what you need. This page helps you pick the right tool and points you to its setup guide.
## Which tool do I need?
Use **Vet** to scan repositories, lockfiles, and SBOMs for malicious packages, known vulnerabilities, and policy violations. It is the engine behind SafeDep's CI/CD scanning.
Use **PMG**, a guard around `npm`, `pip`, and other package managers that blocks known-malicious packages before they install. No account or API key required.
Use **Gryph** to record every file read, write, and command your AI coding agent runs. It keeps a local audit log you can query.
Use **xBom** to inventory dependencies plus AI and SaaS usage detected from your source code, as a CycloneDX BOM.
Use **safedep package scan** to run an on-demand malware analysis of any package, IDE extension, or GitHub repository. Needs a SafeDep Cloud paid plan or trial.
Use **safedep**, the unified CLI for SafeDep Cloud: authentication, on-demand package scanning, endpoint telemetry queries, and AI agent hardening. It is new and still evolving.
## The tools at a glance
| Tool | Solves | Needs an account? | Open source |
| ----------- | -------------------------------------------------------------------------------------- | ------------------- | ----------- |
| **Vet** | Detect malicious and vulnerable dependencies in code and CI/CD | No (Cloud optional) | Yes |
| **PMG** | Block malicious packages at install time on the dev machine | No | Yes |
| **Gryph** | Local audit trail for AI coding agents | No (fully local) | Yes |
| **xBom** | Generate a BOM enriched with AI and SaaS usage from source code | No | Yes |
| **safedep** | On-demand package scanning, plus managing and querying SafeDep Cloud from the terminal | Yes (SafeDep Cloud) | Yes |
Vet, PMG, and Gryph are free, open source, and work with no SafeDep account. The **safedep** CLI is the client for SafeDep Cloud's hosted features. See [pricing](https://safedep.io/pricing).
## How they relate
* **Vet** is the scanning engine. It analyzes dependencies and produces risk reports, queries, and SBOMs. It runs standalone or syncs results to SafeDep Cloud.
* **PMG** and **Gryph** are standalone, single-purpose guards. PMG works at package-install time, Gryph around AI coding agents. Neither needs Vet or a SafeDep account.
* **safedep** is an emerging unified CLI that brings SafeDep Cloud's workflows (auth, [on-demand package scanning](/package-security/scan/overview), endpoint telemetry, agent hardening) to the terminal. It does not re-implement local scanning: repository analysis stays in Vet, and on-demand package analysis runs in SafeDep Cloud.
These tools have no "v1 to v2" relationship. `safedep` is a new Cloud-focused CLI, not a replacement for `vet`. Vet stays the standalone scanner and the recommended starting point for most users.
## Install
Each tool is on the SafeDep Homebrew tap. Vet, PMG, Gryph, and safedep are also published to npm; Vet, PMG, Gryph, and xBom ship as pre-built binaries. The most common installs:
```bash theme={null}
brew install safedep/tap/vet
brew install safedep/tap/pmg
brew install safedep/tap/gryph
brew install safedep/tap/xbom
brew install --cask safedep/tap/cli # the `safedep` command
```
```bash theme={null}
npm install -g @safedep/vet
npm install -g @safedep/pmg
npm install -g @safedep/gryph
npm install -g @safedep/cli # the `safedep` command
```
For every install method, current versions, and the full command surface, see each tool's repository: [Vet](https://github.com/safedep/vet), [PMG](https://github.com/safedep/pmg), [Gryph](https://github.com/safedep/gryph), [safedep](https://github.com/safedep/cli).
## Next steps
Scan your first repository for supply-chain risk.
Guard your package installs in minutes.
Set up an audit trail for your AI coding agents.
Generate an enriched Bill of Materials from your code.
Onboard to the hosted platform for org-wide visibility.
# Install the SafeDep Skill
Source: https://docs.safedep.io/get-started/safedep-skill
Add the SafeDep Agent Skill to Claude Code or any skills-capable AI coding agent.
The [SafeDep skill](https://github.com/safedep/skills) makes your AI coding agent fluent in SafeDep. With the skill installed, the agent answers SafeDep questions from the official sources instead of memory, picks the right SafeDep tool for the task, and helps you install, configure, and use it. Answers stay current because the agent reads the live documentation, not its training data.
## Install
Add the marketplace, then install the plugin:
```bash theme={null}
/plugin marketplace add safedep/skills
/plugin install safedep-agent-skills@safedep-agent-skills
```
Turn on auto-update in Claude Code settings to stay on the current version.
Works with any agent that supports Agent Skills:
```bash theme={null}
npx skills add safedep/skills
```
Run `npx skills update` to refresh it later.
Copy the skill into your project:
```bash theme={null}
git clone https://github.com/safedep/skills.git
cp -r skills/skills/safedep .cursor/skills/
```
Cursor discovers skills from `.cursor/skills/` on startup.
## Verify
Ask your agent a SafeDep question:
```text theme={null}
What does SafeDep PMG do?
```
The agent should load the skill, fetch the answer from docs.safedep.io, and cite the page it used.
## Set up the safedep CLI
The skill answers product questions without an account. Questions about *your* data, such as "what did PMG block this week", run through the [safedep CLI](https://github.com/safedep/cli) against your [SafeDep Cloud tenant](/governance/cloud/quickstart). The skill tells the agent to check for the CLI and your sign-in state, and to hand a step back to you when it needs a human:
The [CLI repository](https://github.com/safedep/cli#install) lists more options.
```bash Homebrew theme={null}
brew install safedep/tap/cli
```
```bash npm theme={null}
npm install -g @safedep/cli
```
Sign-in opens a browser, so the agent asks you to run it.
```bash theme={null}
safedep auth login
```
```bash theme={null}
safedep auth status
```
See [Authentication](/governance/cloud/authentication).
Ask your supply chain questions in plain English.
Investigate package activity across your endpoints with an agent.
# AI Governance
Source: https://docs.safedep.io/governance/ai-governance
Gain visibility into AI usage across your codebase and developer environments
Developers integrate AI SDKs into application code and adopt AI-powered development tools, often without centralized oversight. This creates blind spots for security and compliance teams.
`vet` provides two complementary capabilities to address this:
1. **Shadow AI in Code** detects AI and LLM SDK usage in your source code through static analysis and produces a CycloneDX SBOM with AI component evidence. It answers: *what AI services does your application call?*
2. **AI Tools Discovery** scans developer machines and project repositories to inventory AI coding agents, MCP servers, CLI tools, and IDE extensions. It answers: *what AI tools are developers using to write code?*
Together, these give security teams a complete picture of AI usage across both the software supply chain and the development environment.
Detect AI SDK usage in source code and generate AI-enriched SBOMs
Discover AI coding agents, MCP servers, and IDE extensions on developer machines
# Send Alerts from SafeDep Cloud
Source: https://docs.safedep.io/governance/cloud/alerts
Turn any safedep query exec result into a Slack, Discord, Teams, PagerDuty, or custom HTTP alert.
Any SafeDep Cloud table you can query with `safedep query exec` can drive an
alert. The recipe stays the same for every destination:
1. Write the SQL for the events you care about.
2. Run `safedep query exec -o json` to get rows.
3. Format the rows into your destination's payload shape.
4. `POST` to the webhook.
The rest of this page is a **worked example**: PMG block events, formatted
as a Slack message, sent to a Slack Incoming Webhook, every 5 minutes.
Everything below is a stand-in: swap the query, the formatter, or the
`curl` target for your own destination.
## Example: Slack alert for PMG blocks
Every 5 minutes, a scheduled job queries SafeDep Cloud for packages PMG
blocked in the last 5 minutes and posts a Slack message like:
```
🛡️ PMG blocked 2 package(s)
🟡 COOLDOWN • lodash@0.1.0 • npm
Endpoint: MacBook-Pro.local
🔴 MALICIOUS • safedep-test-pkg@0.1.3 • PyPI
Endpoint: runnervmkkn4f
```
Both the package and endpoint are clickable: the package links to its
SafeDep community report, the endpoint links to its page in SafeDep Cloud.
## Prerequisites
You always need:
* `safedep` CLI installed and signed in. See the [SafeDep Cloud Quickstart](/governance/cloud/quickstart).
For the Slack + Python example below, you also need:
* `python3` and `curl` on the host running the script.
* A Slack Incoming Webhook URL. See Slack's [Sending messages using incoming webhooks](https://api.slack.com/messaging/webhooks).
Confirm the CLI can reach SafeDep Cloud:
```bash theme={null}
safedep auth status
```
## Step 1: Write the query
Save the SQL to a file so you can version it in git and rerun it:
```sql blocks.sql theme={null}
SELECT
package_guard_events.package_ecosystem,
package_guard_events.package_name,
package_guard_events.package_version,
package_guard_events.package_action,
package_guard_events.timestamp,
endpoints.id,
endpoints.identifier
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_action IN (
'PMG_PACKAGE_ACTION_BLOCKED',
'PMG_PACKAGE_ACTION_COOLDOWN_BLOCKED'
)
ORDER BY package_guard_events.timestamp DESC
```
Two things to notice:
* **`package_action` filter.** `PMG_PACKAGE_ACTION_BLOCKED` is a malicious-package
block. `PMG_PACKAGE_ACTION_COOLDOWN_BLOCKED` is a cooldown-window block
(packages held back until they age past your cooldown threshold).
* **`JOIN endpoints`.** Each block event was produced by a PMG invocation on
some developer machine or CI runner. Joining `endpoints` gives you the
endpoint's human-readable name (`endpoints.identifier`) and its ID
(`endpoints.id`, used to build the Cloud deep-link). The `ON` clause is a
required placeholder: SafeDep Cloud applies the real join from its
catalog. See the [SQL reference](/reference/sql-query).
Run it once to see the shape of the data:
```bash theme={null}
safedep query exec -o json --sql-file blocks.sql --limit 5
```
Enum columns (like `package_action` and `package_ecosystem`) return numeric
ordinals in JSON; the formatter decodes them in Step 2. For the full schema,
tables, and enum values, see the [SafeDep Cloud SQL reference](/reference/sql-query).
## Step 2: Format events for Slack
Slack's Incoming Webhook takes a JSON payload with a `blocks` array. The
script below reads rows from stdin, decodes the enum ordinals, builds one
Slack section block per event, and prints the payload on stdout.
Save this as `format.py`:
```python format.py theme={null}
#!/usr/bin/env python3
"""Read safedep query exec JSON on stdin, print a Slack payload on stdout."""
import json, sys
from urllib.parse import quote
ACTION = {1: ("🔴", "MALICIOUS"), 4: ("🟡", "COOLDOWN")}
ECOSYSTEM = {
1: ("ECOSYSTEM_MAVEN", "Maven"),
2: ("ECOSYSTEM_NPM", "npm"),
3: ("ECOSYSTEM_PYPI", "PyPI"),
4: ("ECOSYSTEM_RUBYGEMS", "RubyGems"),
5: ("ECOSYSTEM_NUGET", "NuGet"),
6: ("ECOSYSTEM_CARGO", "Cargo"),
7: ("ECOSYSTEM_GO", "Go"),
8: ("ECOSYSTEM_GITHUB_ACTIONS", "GitHub Actions"),
9: ("ECOSYSTEM_PACKAGIST", "Packagist"),
}
rows = json.load(sys.stdin).get("rows", [])
if not rows:
sys.exit(0) # empty stdout, curl skips the POST
def section(r):
icon, reason = ACTION[r["package_guard_events.package_action"]]
eco_enum, eco_label = ECOSYSTEM[r["package_guard_events.package_ecosystem"]]
name, version = r["package_guard_events.package_name"], r["package_guard_events.package_version"]
ep_id, ep_name = r["endpoints.id"], r["endpoints.identifier"]
report = f"https://app.safedep.io/community/packages/{eco_enum}/{quote(name, safe='')}/{quote(version, safe='')}"
endpoint = f"https://app.safedep.io/endpoints/{ep_id}"
text = (f"{icon} *{reason}* • <{report}|`{name}@{version}`> • {eco_label}"
f"\n\nEndpoint: <{endpoint}|`{ep_name}`>")
return {"type": "section", "text": {"type": "mrkdwn", "text": text}}
payload = {"blocks": [
{"type": "header", "text": {"type": "plain_text", "text": f"🛡️ PMG blocked {len(rows)} package(s)"}},
*[section(r) for r in rows],
]}
json.dump(payload, sys.stdout)
```
Add ecosystems you use to the `ECOSYSTEM` dict; nothing else needs to change.
## Step 3: Post to Slack
Pipe the query into the formatter and the formatter into `curl`:
```bash theme={null}
export SLACK_WEBHOOK_URL='https://hooks.slack.com/services/...'
safedep query exec -o json --sql-file blocks.sql --limit 10 \
| python3 format.py \
| curl -sS -X POST -H 'Content-Type: application/json' \
--data @- "$SLACK_WEBHOOK_URL"
```
If there are no matching events, `format.py` exits with empty stdout and
`curl` sends nothing. On a successful post, Slack replies with `ok`.
Slack caps a message at 50 blocks, so keep `--limit` around 10. See Slack's [block limits](https://api.slack.com/reference/block-kit/blocks).
## Step 4: Run it on a schedule
To make this an alerting pipeline, filter to a rolling window and run
periodically. The window and the schedule interval must match so events
aren't dropped or duplicated.
Save this as `safedep-alerts.sh` next to `format.py`. It's the same query as
`blocks.sql`, plus one extra `AND` clause for the time window, an explicit
`PATH` (schedulers run with a minimal `PATH`), and a skip when the window
has no events (avoids Slack's `invalid_payload` on empty POSTs).
```bash safedep-alerts.sh theme={null}
#!/usr/bin/env bash
set -euo pipefail
# Schedulers run with a minimal PATH. Add the location of `safedep` and `python3`.
export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
cd "$(dirname "$0")"
# macOS / BSD date. On Linux: SINCE=$(date -u -d '5 minutes ago' '+%Y-%m-%dT%H:%M:%SZ')
SINCE=$(date -u -v-5M '+%Y-%m-%dT%H:%M:%SZ')
PAYLOAD=$(safedep query exec -o json --limit 10 --sql "
SELECT package_guard_events.package_ecosystem,
package_guard_events.package_name,
package_guard_events.package_version,
package_guard_events.package_action,
package_guard_events.timestamp,
endpoints.id,
endpoints.identifier
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_action IN (
'PMG_PACKAGE_ACTION_BLOCKED',
'PMG_PACKAGE_ACTION_COOLDOWN_BLOCKED'
) AND package_guard_events.timestamp > '$SINCE'
ORDER BY package_guard_events.timestamp DESC
" | python3 format.py)
[ -n "$PAYLOAD" ] && printf '%s' "$PAYLOAD" | curl -sS -X POST \
-H 'Content-Type: application/json' --data @- "$SLACK_WEBHOOK_URL"
```
Then wire it up with your OS's native scheduler:
On macOS, `cron` can't reach the keychain where `safedep auth login`
stores its OAuth token, so scheduled `safedep query exec` calls fail
with `not authenticated`. Use a **launchd user agent** instead: it runs
in your logged-in user session and inherits keychain access.
Save this as `~/Library/LaunchAgents/io.safedep.alerts.plist`:
```xml io.safedep.alerts.plist highlight={20-20} theme={null}
Label
io.safedep.alerts
ProgramArguments
/Users/you/safedep-alerts/safedep-alerts.sh
StartInterval
300
EnvironmentVariables
SLACK_WEBHOOK_URL
PASTE_YOUR_SLACK_WEBHOOK_URL_HERE
StandardOutPath
/tmp/safedep-alerts.log
StandardErrorPath
/tmp/safedep-alerts.log
RunAtLoad
```
Load, verify, and tail the log:
```bash theme={null}
launchctl load ~/Library/LaunchAgents/io.safedep.alerts.plist
launchctl list | grep io.safedep.alerts
tail -f /tmp/safedep-alerts.log
```
`RunAtLoad` fires the job immediately. `StartInterval` is in seconds
(`300` = 5 minutes) and must match the `-v-5M` window in the script.
To stop it:
```bash theme={null}
launchctl unload ~/Library/LaunchAgents/io.safedep.alerts.plist
```
On Linux, `cron` works fine for this use case. Install with
`crontab -e` and add:
```
*/5 * * * * SLACK_WEBHOOK_URL="https://hooks.slack.com/services/..." /home/you/safedep-alerts/safedep-alerts.sh >> /tmp/safedep-alerts.log 2>&1
```
Change `date -u -v-5M ...` in the script to
`date -u -d '5 minutes ago' ...` (GNU date syntax). Keep the cron
interval (`*/5`) and the `SINCE` window in the script aligned.
If you're on a desktop with `gnome-keyring` / `kwallet` and hit
"not authenticated" from cron, either run the script under a
systemd user timer (`systemctl --user enable --now safedep-alerts.timer`)
or unlock the keyring for the cron session.
The design is stateless: no cursor file to keep in sync, no drift across
restarts.
`--limit 10` is set for Slack's 50-block ceiling. Events beyond the limit in
a single window are silently dropped. For burstier traffic, raise `--limit`
(the CLI allows up to 100) and page through the JSON `next_page_token`,
sending one Slack message per page.
## Send to other destinations
Only the last two things change: the payload shape (in `format.py`) and the
URL (in `curl`). Any JSON-accepting HTTP endpoint works.
Emit a simple content payload and POST to your channel's webhook URL.
Create a channel Workflow with a webhook trigger and POST an Adaptive
Card payload to its URL.
Emit an Events API v2 payload and POST to the enqueue endpoint.
## Query other events
Swap `blocks.sql` for any question you can ask SafeDep Cloud:
* **Insecure bypasses:** filter `package_guard_events` on `event_type = 'PMG_EVENT_TYPE_INSECURE_BYPASS'`.
* **Endpoint activity:** `JOIN endpoints` and group by
`endpoints.identifier` to see which machines produced the most events.
* **Malicious packages across projects:** query `component_malicious_packages`
with `is_verified = true`.
See the [SafeDep Cloud SQL guide](/reference/sql-query) for the full schema,
query syntax, and the paging model.
# Authentication
Source: https://docs.safedep.io/governance/cloud/authentication
How SafeDep Cloud authentication works and how to authenticate the safedep CLI, vet, and CI/CD pipelines
Every tool that talks to SafeDep Cloud authenticates with two values: a **tenant domain** (for example `your-company.safedep.io`) and a credential. The credential type depends on which API plane the tool calls:
* **Data plane** (`api.safedep.io`): package insights, known-malicious package queries, and sync. Authenticates with an **API key**.
* **Control plane** (`cloud.safedep.io`): tenant, policy, and management operations, including SQL queries and [on-demand package scans](/package-security/scan/overview). Authenticates with a **JWT** from an OAuth2 login.
Generate API keys at [app.safedep.io/settings/api-keys](https://app.safedep.io/settings/api-keys). For request headers, OAuth2/OIDC endpoints, and rate limits, see the [API reference](/reference/api-introduction). For the full hostname list, see the [endpoints reference](/reference/endpoints).
## safedep CLI
`safedep auth login` runs an OAuth2 device flow in your browser, selects a tenant, creates an API key, and stores the credentials in your OS keychain:
```bash theme={null}
safedep auth login
safedep auth status
```
For non-interactive environments, log in with a static API key instead of the device flow:
```bash theme={null}
safedep auth login --api-key --tenant your-company.safedep.io
```
The key is read from `--api-key-value`, stdin (with `--from-stdin`), the `SAFEDEP_API_KEY` environment variable, or an interactive prompt, in that order. Work with multiple tenants using `--profile` and `safedep auth profile list`. See the [CLI command reference](https://github.com/safedep/cli/tree/main/docs/cmd) for all flags.
API-key login covers data-plane commands only. [On-demand package scanning](/package-security/scan/overview) is a control-plane operation: it requires the OAuth2 device-flow session, and an API key cannot submit scans, by design. See [Scanning from CI and AI Agents](/package-security/scan/automation) for the reasoning and what this means for automation.
## vet
vet uses an API key for scanning and [sync](/governance/cloud/sync):
```bash theme={null}
vet auth configure --tenant your-company.safedep.io
```
You will be prompted to enter the API key. Verify the connection:
```bash theme={null}
vet auth verify
```
Control plane commands under `vet cloud` use the OAuth2 device flow instead:
```bash theme={null}
vet cloud login --tenant your-company.safedep.io
vet cloud whoami
```
To log out, delete the stored credentials: `rm ~/.safedep/vet-auth.yml`.
## CI/CD pipelines
Tools read credentials from environment variables, so pipelines need no interactive login:
```bash theme={null}
export SAFEDEP_API_KEY=your-api-key
export SAFEDEP_TENANT_ID=your-company.safedep.io
```
Store both as CI secrets. Across SafeDep docs, the secret names are `SAFEDEP_CLOUD_API_KEY` and `SAFEDEP_CLOUD_TENANT_DOMAIN`. [vet-action](https://github.com/safedep/vet-action) reads them through its `cloud-key` and `cloud-tenant` inputs instead of environment variables.
For working pipeline configurations (GitHub Actions, GitLab, Jenkins, Azure DevOps), see [Cloud Sync](/governance/cloud/sync#sync-from-github-actions).
## Troubleshooting
### Identity not registered
```
ERRO[0001] Failed to execute whoami: rpc error: code = Unauthenticated desc = unauthenticated: Token auth failed: No user: record not found
```
Your identity is not registered with SafeDep Cloud. Sign up first: see the [quickstart](/governance/cloud/quickstart).
### Tenant not found
```
ERRO[0001] Failed to execute query: rpc error: code = Unknown desc = failed to resolve tenant: record not found
```
No tenant is configured. Set it with `vet auth configure --tenant ` or `vet cloud login --tenant `. If you've forgotten your tenant domain, run `vet cloud login` followed by `vet cloud whoami` to list the tenants you can access.
### Checking credentials
```bash theme={null}
safedep auth status # safedep CLI session
vet auth verify # vet API key
vet cloud whoami # vet OAuth identity and tenants
```
Transport, request headers, OAuth2/OIDC, and rate limits
Create a tenant and log in with the safedep CLI
Send data to your tenant from vet, PMG, and endpoint scans
Canonical gRPC/ConnectRPC schemas and generated SDKs
# Agentic Endpoint Investigation
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/agentic-investigation
Investigate package activity and AI tooling across your developer endpoints with an AI coding agent and the safedep CLI.
Your endpoints report their activity to [Endpoint Hub](/governance/cloud/endpoint-hub/overview): every package install [PMG](/package-security/pmg/overview) allowed or blocked, and the AI tooling `vet` discovered. When something needs investigation, you do not write queries yourself. You ask your AI coding agent. The agent turns your question into a tenant-scoped [SafeDep Cloud SQL](/reference/sql-query) query, runs it with the `safedep` CLI, and reports what it found.
This guide gives you investigation playbooks: the question to ask, what the agent runs, and how to verify the answer.
## Before you start
* Endpoints that sync data to your tenant. Set up [Package Guard sync](/governance/cloud/endpoint-hub/package-guard) for package events, and optionally [Inventory](/governance/cloud/endpoint-hub/inventory) for AI tooling.
* The `safedep` CLI, signed in with `safedep auth login`. See [Install the safedep CLI](/governance/cloud/quickstart#install-the-safedep-cli) and [Authentication](/governance/cloud/authentication).
* An AI coding agent with the [SafeDep skill](/get-started/safedep-skill) installed.
The agent cannot sign in for you. Sign-in opens a browser and needs a human. If the agent reports an authentication error, run `safedep auth login` yourself, confirm with `safedep auth status`, and tell the agent to continue.
## How the agent investigates
The agent works in a loop:
1. It discovers the queryable tables with `safedep query schema get`. The schema describes the tables, their columns, the allowed joins, and the query rules.
2. It writes a query and runs it with `safedep query exec`.
3. If the server rejects the query, the error says why. The agent corrects the query and retries.
Every query is scoped to your authenticated tenant. The agent reads data; it cannot change it. Each answer traces back to a real query, so you can ask the agent to show the SQL it ran. The query language has no relative dates, so for questions like "in the last 30 days" the agent computes the cutoff timestamp from today's date. The playbooks below include the SQL the agent runs, collapsed by default since you do not need it to investigate. Expand it when you want to see or verify the details.
For the query language, the full table list, and the query rules, see [SafeDep Cloud SQL](/reference/sql-query).
## Playbooks
### Check exposure to a compromised package
A package you use was compromised upstream. Find out if any endpoint installed it.
```text theme={null}
The npm package eslint-config-prettier was compromised. Was it installed
on any of our endpoints in the last 30 days? Which versions, and was it
blocked or allowed?
```
The agent runs a query like this:
```sql theme={null}
SELECT endpoints.identifier, package_guard_events.package_version,
package_guard_events.package_action, package_guard_events.timestamp
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_name = 'eslint-config-prettier'
AND package_guard_events.timestamp >= '2026-07-11T00:00:00Z'
ORDER BY package_guard_events.timestamp DESC
```
Each row is one decision on one endpoint. `PMG_PACKAGE_ACTION_BLOCKED` means PMG stopped the install. `PMG_PACKAGE_ACTION_CONFIRMED` means a person approved it after a warning. An allowed install of a compromised version is your incident to respond to.
### Review what was blocked, and where
```text theme={null}
What did PMG block across our endpoints this month? Group it by endpoint.
```
The agent filters `package_guard_events` on both block actions, bounds the time range to the month, and joins to `endpoints`:
```sql theme={null}
SELECT endpoints.identifier, package_guard_events.package_name,
package_guard_events.package_ecosystem, package_guard_events.package_action,
package_guard_events.timestamp
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_action IN ('PMG_PACKAGE_ACTION_BLOCKED', 'PMG_PACKAGE_ACTION_COOLDOWN_BLOCKED')
AND package_guard_events.timestamp >= '2026-08-01T00:00:00Z'
ORDER BY package_guard_events.timestamp DESC
```
`PMG_PACKAGE_ACTION_BLOCKED` is a block on a known malicious package. `PMG_PACKAGE_ACTION_COOLDOWN_BLOCKED` is a block on a package too new to trust under the cooldown policy. Both stopped an install.
A block on one endpoint is often the first visible event of a campaign. Follow up with the exposure playbook above for the same package across the fleet.
### Audit protection bypasses
PMG records when a person bypasses protection. Review these regularly.
```text theme={null}
Did anyone bypass PMG protection in the last two weeks? Show the endpoint,
the package, and when it happened.
```
```sql theme={null}
SELECT endpoints.identifier, package_guard_events.package_name,
package_guard_events.timestamp
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.event_type = 'PMG_EVENT_TYPE_INSECURE_BYPASS'
AND package_guard_events.timestamp >= '2026-07-27T00:00:00Z'
ORDER BY package_guard_events.timestamp DESC
```
The related `PMG_EVENT_TYPE_SANDBOX_OVERRIDE` event records sandbox policy overrides. Ask the agent to check both.
### Find endpoints that stopped reporting
An endpoint that stopped syncing is a blind spot: it still installs packages, and you no longer see them.
```text theme={null}
Which endpoints have not synced in the last 30 days?
```
```sql theme={null}
SELECT endpoints.identifier, endpoints.endpoint_type, endpoints.last_sync_at
FROM endpoints
WHERE endpoints.last_sync_at < '2026-07-11T00:00:00Z'
ORDER BY endpoints.last_sync_at ASC
```
An endpoint can stop reporting for good reasons, such as a decommissioned laptop. Confirm before you treat it as an incident.
### Get a current verdict on a suspect package
Package Guard events record the decision PMG made at install time. Verdicts change: a package that passed last month may be known malware today. To judge a package during an investigation, get a current verdict. Two paths exist, and the agent should take the fast one first:
```text theme={null}
An endpoint installed left-pad-utils 2.1.0 from npm last week. Is it safe?
Check the known malicious packages database first. Run a full scan only if
I ask for a deep dive.
```
The fast path checks SafeDep's [known malicious packages database](/governance/cloud/malware-analysis). It is free and answers in milliseconds:
```bash theme={null}
vet scan --purl pkg:npm/left-pad-utils@2.1.0 --malware-query
```
The slow path is an [on-demand package scan](/package-security/scan/overview): a full malware analysis of the exact version. It takes minutes and draws down your plan's scan allowance. Use it when the database has no verdict and the package deserves a deep dive:
```bash theme={null}
safedep package scan run "pkg:npm/left-pad-utils@2.1.0" -o json
```
The scan returns `malware`, `benign`, or `inconclusive`, with evidence. Treat `inconclusive` as needs-review. See [Scanning from CI and AI Agents](/package-security/scan/automation) for the scan contract.
### See what AI tooling runs on an endpoint
If your endpoints also run [Inventory](/governance/cloud/endpoint-hub/inventory) scans, the same loop answers questions about AI tooling:
```text theme={null}
Which MCP servers were observed on our endpoints, and on which machines?
```
```sql theme={null}
SELECT inventory_events.item_identity, endpoints.identifier
FROM inventory_events
JOIN endpoints ON endpoints.id = inventory_events.app
WHERE inventory_events.item_kind = 'INVENTORY_ITEM_KIND_MCP_SERVER'
ORDER BY inventory_events.item_identity
```
Other item kinds cover coding agents, IDE extensions, and Agent Skills.
## Ask narrow questions
Results return one page at a time, up to 100 rows. Do not ask the agent to dump a table and search the output. Ask a narrow question, and let the agent filter server-side: a package name, a time range, one endpoint, one event type. When you need totals, ask for counts; the agent aggregates with `GROUP BY` instead of paging through rows.
## Verify the answer
Every agent answer maps to a query you can run yourself:
1. Ask the agent to show the SQL it ran.
2. Run it: `safedep query exec --sql ""`.
3. Compare the result with the agent's summary.
If the agent's answer looks wrong, the query is the first place to look. A missing time bound or a wrong enum value changes the result silently.
The query language, full table list, and query rules.
Set up your AI coding agent for SafeDep.
Sync package events from your endpoints.
Ask your tenant questions in plain English.
# Endpoint Inventory
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/inventory
View AI tools, Agent Skills, MCP servers, and coding agents discovered on your endpoints in SafeDep Cloud
Endpoint inventory shows what AI tooling is active across your endpoints (developer machines, CI runners, agent sandboxes). It is powered by `vet endpoint scan`, which runs locally to discover AI tools, Agent Skills, MCP servers, coding agents, and IDE extensions, then delivers them to SafeDep Cloud when credentials are configured.
## Prerequisites
* `vet` [installed](/governance/vet/quickstart)
* A SafeDep Cloud account with an API key and tenant domain (see [Cloud quickstart](/governance/cloud/quickstart))
## Enable inventory sync
Run the interactive auth setup, passing your tenant domain as a flag:
```bash theme={null}
vet auth configure --tenant
```
The tenant domain looks like `your-team.your-org.safedep.io` and is shown on your SafeDep Cloud settings page. You will be prompted to enter your **API key** interactively.
Alternatively, set environment variables:
```bash theme={null}
export SAFEDEP_API_KEY=
export SAFEDEP_TENANT_ID= # e.g. your-team.your-org.safedep.io
```
Verify credentials are working:
```bash theme={null}
vet auth verify
```
A successful response means Vet can reach SafeDep Cloud. If verification fails, check that your API key and tenant domain are correct.
```bash theme={null}
vet endpoint scan
```
```text Example output theme={null}
Discovered 6 item(s) across 3 app(s)
┌────────────────┬──────────────────┬───────────────┬─────────┬──────────────────────────────────────────────────┐
│ TYPE │ NAME │ APP │ SCOPE │ DETAIL │
├────────────────┼──────────────────┼───────────────┼─────────┼──────────────────────────────────────────────────┤
│ Coding Agent │ Claude Code │ Claude Code │ System │ /home/user/.claude/settings.json │
│ MCP Server │ my-server │ Claude Code │ System │ stdio: npx my-server │
│ CLI Tool │ Claude Code │ Claude Code │ System │ /usr/local/bin/claude v2.1.0 │
│ Coding Agent │ Cursor │ Cursor │ System │ /home/user/.cursor │
│ Agent Skill │ golang-pro │ claude-code │ Project │ /home/user/project/.claude/skills/golang-pro │
│ Agent Skill │ security-review │ Global Skills │ System │ /home/user/.agents/skills/security-review │
└────────────────┴──────────────────┴───────────────┴─────────┴──────────────────────────────────────────────────┘
```
`vet endpoint scan` delivers discovered items to SafeDep Cloud in the background. The command waits up to 30 seconds for delivery; if the timeout is reached, any undelivered items are retried automatically on the next run.
Without credentials, `vet endpoint scan` still runs and prints the local table. No data leaves the machine.
Open [app.safedep.io](https://app.safedep.io), select **Endpoint Hub** in the sidebar, and pick your endpoint. Your endpoint appears in the list by your machine's hostname. Open the **Inventory** tab to see discovered items.
If your endpoint is not listed, confirm credentials with `vet auth verify` and re-run the scan.
## Common scan options
* **Project scope only:** `vet endpoint scan --scope project -D /path/to/repo`: limits discovery to project-level configs (e.g. `.mcp.json`, `CLAUDE.md`).
* **System scope only:** `vet endpoint scan --scope system`: skips project configs; faster on large repositories.
* **AI tools only:** `vet endpoint scan --kind ai-tool`: discovers coding agents, MCP servers, CLI tools, IDE extensions, and project config files; skips skill directories.
* **Agent skills only:** `vet endpoint scan --kind agent-skill`: discovers agent skill directories only.
* **JSON report:** `vet endpoint scan --report-json inventory.json`: writes a local file in addition to syncing.
* **CI/CD:** Set `SAFEDEP_API_KEY` and `SAFEDEP_TENANT_ID` as pipeline secrets. Ephemeral runners (e.g. GitHub-hosted) register as a new endpoint per run; self-hosted runners on a persistent host update the same endpoint.
* **Suppress table output:** `vet endpoint scan --silent`: runs and syncs without printing the local table.
## Next steps
Learn what gets discovered and how scopes work
Track package installs across your endpoints
# Endpoint Scan Catalog
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/inventory-catalog
Catalog of all inventory items collected from endpoint scans
## Scanner Kinds
`vet endpoint scan` runs two independent scanners, each covering a different class of signals.
| Kind | What it finds |
| ------------- | --------------------------------------------------------------------------- |
| `ai-tool` | MCP servers, coding agents, CLI tools, IDE extensions, project config files |
| `agent-skill` | Agent skill directories inside well-known skill paths |
## Item Types
Each discovered item is classified as one of the following types:
| Type | Description |
| ---------------- | --------------------------------------------------------------------------------- |
| `mcp_server` | Model Context Protocol server entry found in a config file |
| `coding_agent` | Coding agent detected via a system-level config directory or installed binary |
| `cli_tool` | AI CLI binary found on `$PATH`, version-verified by executing with a version flag |
| `ai_extension` | AI-related IDE extension detected from installed extension manifests |
| `project_config` | AI tool instruction or config file found in a project repository |
| `agent_skill` | Skill subdirectory found inside a well-known agent skills path |
## Scopes
| Scope | Base path |
| --------- | ---------------------------------------------------- |
| `system` | User home directory (global configs and binaries) |
| `project` | Project directory (default: cwd, override with `-D`) |
## What Gets Scanned
### MCP Server Config Files
MCP server entries are read from JSON config files at well-known paths for each supported application.
**System scope**
| Path |
| -------------------------------------- |
| `~/.claude/settings.json` |
| `~/.claude/projects/*/settings.json` |
| `~/.claude.json` |
| `~/.claude/plugins/cache/**/.mcp.json` |
**Project scope**
| Path |
| ------------------------------------ |
| `/.mcp.json` |
| `/.claude/settings.json` |
**System scope**
| Path |
| -------------------- |
| `~/.cursor/mcp.json` |
**Project scope**
| Path |
| ------------------------------- |
| `/.cursor/mcp.json` |
**System scope only**
| Path |
| ------------------------------------- |
| `~/.codeium/windsurf/mcp_config.json` |
**System scope**
| Path |
| --------------------------------------- |
| `~/.gemini/antigravity/mcp_config.json` |
**System scope**
| Platform | Path |
| -------- | -------------------------------------------------- |
| Linux | `~/.config/Code/User/mcp.json` |
| macOS | `~/Library/Application Support/Code/User/mcp.json` |
| Windows | `%APPDATA%\Code\User\mcp.json` |
**Project scope**
| Path |
| -------------------------------------- |
| `/.vscode/mcp.json` |
| `/.vscode/mcpservers.json` |
| `/.vscode/mcp_config.json` |
### Project Config Files
Project-level instruction and rules files are reported as `project_config` items.
| Application | Path |
| ----------- | ------------------------------ |
| Claude Code | `/CLAUDE.md` |
| Cursor | `/.cursorrules` |
| Cursor | `/.cursor/rules/*` |
### CLI Tools
AI CLI binaries are discovered by searching `$PATH` for known names. Each candidate is executed
with a version flag and its output is matched against a known pattern to confirm it is the expected tool.
| Tool | Binary(s) | Verification |
| ------------------------ | ----------------------- | ------------------------------------------ |
| Claude Code CLI | `claude` | Output matches `claude v` |
| Cursor CLI | `cursor` | Semver on first line of `--version` |
| Windsurf CLI | `windsurf` | Semver on first line of `--version` |
| Antigravity / Gemini CLI | `antigravity`, `ag-kit` | Semver on first line of `--version` |
| VS Code CLI | `code` | Semver on first line of `--version` |
| Aider | `aider` | Output contains `aider v` |
| GitHub Copilot | `gh` | `github/gh-copilot` in `gh extension list` |
| Amazon Q | `q`, `amazon-q` | Output contains `amazon` or `aws` + semver |
### IDE Extensions
Extension manifests are read from the following directories for each supported IDE distribution.
Entries are matched against a curated list of known AI extension identifiers.
**Extension directories scanned:**
```
~/.vscode/extensions/
~/.vscode-oss/extensions/
~/.cursor/extensions/
~/.windsurf/extensions/
~/.antigravity/extensions/
```
**Recognized AI extensions:**
| Extension ID | Tool |
| ----------------------------------- | ------------------- |
| `github.copilot` | GitHub Copilot |
| `github.copilot-chat` | GitHub Copilot Chat |
| `sourcegraph.cody-ai` | Cody |
| `continue.continue` | Continue |
| `tabnine.tabnine-vscode` | Tabnine |
| `amazonwebservices.amazon-q-vscode` | Amazon Q |
| `saoudrizwan.claude-dev` | Cline |
| `rooveterinaryinc.roo-cline` | Roo Code |
| `codeium.codeium` | Codeium |
| `supermaven.supermaven` | Supermaven |
### Agent Skills
The `agent-skill` scanner checks for skill subdirectories inside well-known paths for
each supported agent. Every subdirectory found is reported as one `agent_skill` item.
Resolved relative to the user home directory (`~/`).
| Agent(s) | Path |
| -------------------------------- | ------------------------------------------------------ |
| amp, kimi-cli, replit, universal | `.config/agents/skills` |
| cline, warp | `.agents/skills` |
| antigravity | `.gemini/antigravity/skills` |
| augment | `.augment/skills` |
| bob | `.bob/skills` |
| claude | `.claude/skills` |
| codebuddy | `.codebuddy/skills` |
| commandcode | `.commandcode/skills` |
| crush | `.config/crush/skills` |
| goose | `.config/goose/skills` |
| opencode | `.config/opencode/skills` |
| continue | `.continue/skills` |
| copilot | `.copilot/skills` |
| codex | `.codex/skills` |
| cursor | `.cursor/skills` |
| deepagents | `.deepagents/agent/skills` |
| factory | `.factory/skills` |
| firebender | `.firebender/skills` |
| gemini | `.gemini/skills` |
| iflow | `.iflow/skills` |
| junie | `.junie/skills` |
| kilocode | `.kilocode/skills` |
| kiro | `.kiro/skills` |
| kode | `.kode/skills` |
| mcpjam | `.mcpjam/skills` |
| mux | `.mux/skills` |
| openclaw | `.openclaw/skills` |
| openhands | `.openhands/skills` |
| pi | `.pi/agent/skills` |
| qoder | `.qoder/skills` |
| qwen | `.qwen/skills` |
| roo | `.roo/skills` |
| snowflake cortex | `.snowflake/cortex/skills` |
| trae | `.trae/skills` |
| trae-cn | `.trae-cn/skills` |
| vibe | `.vibe/skills` |
| windsurf | `.codeium/windsurf/skills` |
| zencoder | `.zencoder/skills` |
| neovate | `.neovate/skills` |
| pochi | `.pochi/skills` |
| adal | `.adal/skills` |
| Claude plugins | `.claude/plugins/cache////skills/` |
| Claude marketplace | `.claude/plugins/marketplaces//plugins//skills/` |
Resolved relative to the project directory.
| Agent(s) | Path |
| --------------------------------------------------------------------------------------------------------------------------------------- | --------------------- |
| amp, kimi-cli, replit, universal, antigravity, cline, warp, codex, cursor, deepagents, firebender, gemini-cli, github-copilot, opencode | `.agents/skills` |
| augment | `.augment/skills` |
| bob | `.bob/skills` |
| claude | `.claude/skills` |
| codebuddy | `.codebuddy/skills` |
| commandcode | `.commandcode/skills` |
| continue | `.continue/skills` |
| cortex | `.cortex/skills` |
| crush | `.crush/skills` |
| factory | `.factory/skills` |
| goose | `.goose/skills` |
| iflow | `.iflow/skills` |
| junie | `.junie/skills` |
| kilocode | `.kilocode/skills` |
| kiro | `.kiro/skills` |
| kode | `.kode/skills` |
| mcpjam | `.mcpjam/skills` |
| mux | `.mux/skills` |
| neovate | `.neovate/skills` |
| openhands | `.openhands/skills` |
| pi | `.pi/skills` |
| pochi | `.pochi/skills` |
| qoder | `.qoder/skills` |
| qwen | `.qwen/skills` |
| roo | `.roo/skills` |
| trae | `.trae/skills` |
| vibe | `.vibe/skills` |
| windsurf | `.windsurf/skills` |
| zencoder | `.zencoder/skills` |
| adal | `.adal/skills` |
| openclaw | `skills/` |
## MCP Server Details
For each discovered MCP server, the following fields are captured:
| Field | Description |
| ---------------- | --------------------------------------------------------------- |
| Transport | `stdio`, `sse`, or `streamable_http` |
| Command (stdio) | Binary path and arguments |
| URL (HTTP) | Server endpoint URL |
| EnvVarNames | Names of environment variables referenced (values not captured) |
| HeaderNames | Names of HTTP headers referenced (values not captured) |
| AllowedTools | Explicit tool allowlist if configured |
| AllowedResources | Explicit resource allowlist if configured |
What Endpoint Hub covers across your fleet.
Discover AI tooling on each endpoint.
Track package installs across endpoints.
Find AI agents and MCP servers in use.
# MCP Advisor
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/mcp-advisor
Review the packages your AI coding agents checked through the SafeDep MCP server, per endpoint in SafeDep Cloud
MCP Advisor lists the packages your AI coding agents checked through the [SafeDep MCP server](/ai-security/mcp-server) and the verdict each one got. Once the MCP server is set up, SafeDep records an advisory each time an agent vets a package, with no extra configuration.
MCP Advisor is a SafeDep Cloud feature and requires [endpoint sync](/governance/cloud/sync). If your endpoint has no **Advisor** tab, it is not enabled for your tenant yet. Contact the [SafeDep team](/community) to join early access.
## Prerequisites
* Configure the [SafeDep MCP server](/ai-security/mcp-server) in your AI coding agent.
* Have a SafeDep Cloud tenant ID and API key (see the [Cloud quickstart](/governance/cloud/quickstart)).
The SafeDep CLI is the fastest way to configure the MCP server: `npx @safedep/cli setup mcp install`.
## View advisories
Go to [app.safedep.io](https://app.safedep.io) and select **Endpoint Hub** in the sidebar.
Choose the endpoint from the list.
Select the **Advisor** tab to see that endpoint's advisories.
The tab shows summary counts, then the advisory list.
### Summary counts
| Card | Counts |
| ------------- | ------------------------------------------ |
| Advisories | All advisories for this endpoint |
| Malicious | Packages flagged as malicious |
| Suspicious | Packages showing suspicious signals |
| Vulnerable | Packages with known vulnerabilities |
| Clean | Packages that came back safe |
| Last advisory | When the most recent advisory was recorded |
### Advisory list
Each row is one package an agent asked about:
| Column | Description |
| -------- | -------------------------------------------------------- |
| Package | Name, version, and ecosystem |
| Verdict | The verdict SafeDep returned (see [Verdicts](#verdicts)) |
| Agent | The AI coding agent that asked |
| Analysed | When the advisory was recorded |
Filter by verdict or date range, for example only Malicious results in the last week. Select a row to see the package, the verdict, the MCP call, and the source endpoint.
## Verdicts
| Verdict | Meaning |
| ---------------- | ------------------------------------------------------------- |
| Safe | No known security issues |
| Suspicious | Signals suggest the package may be risky |
| Malicious | Confirmed malicious by SafeDep |
| Vulnerable | Has one or more known vulnerabilities |
| Newly registered | Published very recently, a common trait of malicious packages |
| Unknown | Not enough information to classify it |
## Related
Configure the MCP server that produces these advisories.
Browse all the views available for your endpoints.
See package installs captured by PMG on each endpoint.
What an endpoint is and what it reports.
# Endpoint Hub
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/overview
Visibility into AI tooling and package activity across your developer machines, CI runners, and agent sandboxes
Endpoint Hub gives you visibility into what is running across your **endpoints**: developer machines, CI runners, and agent sandboxes. It brings together three views in SafeDep Cloud: **Inventory** (the AI tooling found on each endpoint), **Package Guard** (package installs and usage over time), and **MCP Advisor** (packages your AI coding agents vetted through the SafeDep MCP server). Each view is powered by a SafeDep tool that reports activity to the cloud.
## What Endpoint Hub covers
### Inventory
Inventory shows the AI tooling active on each endpoint: coding agents, MCP servers, Agent Skills, CLI tools, and IDE extensions. It is powered by `vet endpoint scan`, which discovers these locally and delivers them to SafeDep Cloud when credentials are configured.
### Package Guard
Package Guard shows package installs and usage across your endpoints as a timeline of events. It runs on [PMG](/package-security/pmg/quickstart), the open-source Package Manager Guard CLI that intercepts package manager commands and syncs the activity to SafeDep Cloud.
### MCP Advisor
MCP Advisor shows the packages your AI coding agents checked through the [SafeDep MCP server](/ai-security/mcp-server) and the verdict each one received. SafeDep records an advisory each time an agent vets a package.
## How it works
Inventory and Package Guard follow the same model: an open source CLI runs on the endpoint and, once SafeDep Cloud credentials are configured, syncs its findings to your tenant. MCP Advisor needs no sync, since the MCP server records advisories as agents use it. You browse each endpoint under **Endpoint Hub** in the [app.safedep.io](https://app.safedep.io) sidebar. Without SafeDep Cloud credentials, the local tools still run and no data leaves the machine.
## Get started
Discover AI tools, Agent Skills, MCP servers, and coding agents on your endpoints
Track package installs and usage across your endpoints
Review packages your AI coding agents vetted through the SafeDep MCP server
# Package Guard
Source: https://docs.safedep.io/governance/cloud/endpoint-hub/package-guard
Monitor and control package installations across your endpoints using SafeDep Cloud
Package Guard shows package installs and usage across your endpoints. It runs on [PMG](/package-security/pmg/quickstart), SafeDep's open-source Package Manager Guard CLI that intercepts package manager commands and syncs the activity to SafeDep Cloud.
## Prerequisites
* Install PMG using the [quickstart guide](/package-security/pmg/quickstart)
* Have a SafeDep Cloud tenant ID and API key (see [Cloud quickstart](/governance/cloud/quickstart))
## Enable cloud sync
```bash theme={null}
pmg setup install
```
```text Example output theme={null}
█▀█ █▀▄▀█ █▀▀ From SafeDep (github.com/safedep/pmg)
█▀▀ █░▀░█ █▄█ version: v0.9.0 commit: c3a351
✓ PMG aliases installed successfully
Installed to: /home/user/.pmg.rc
Config at: /home/user/.config/safedep/pmg
Restart your terminal or source your shell to use the new aliases
```
```bash theme={null}
pmg config set cloud.enabled true
```
```bash theme={null}
pmg cloud login
```
Enter your **tenant ID** and **API key** when prompted.
With cloud sync enabled, PMG syncs automatically as you use it (see [Automatic sync](#automatic-sync) below). To push local activity immediately, run:
```bash theme={null}
pmg cloud sync
```
## Automatic sync
Once cloud sync is enabled, PMG syncs events on its own, so you don't need to run `pmg cloud sync` by hand. At the end of each PMG invocation, PMG drains its local event log to SafeDep Cloud in a short-lived detached process, so your command returns immediately.
This applies whether you invoke PMG directly (`pmg npm install ...`) or indirectly through the shell aliases and path shims installed by `pmg setup install`, which route plain `npm`, `pip`, and other package manager commands through PMG.
To avoid syncing on every command, auto-sync is gated by a per-host cooldown. The cooldown timestamp updates on every attempt, success or failure, so a temporarily unreachable cloud endpoint won't make every command retry.
Auto-sync is on by default. Tune or disable it under `cloud.auto_sync` in the PMG config:
```yaml theme={null}
cloud:
enabled: true
auto_sync:
enabled: true # set to false to disable automatic sync
min_interval: 15m # example: minimum gap between sync attempts
timeout: 5m # example: hard timeout for a single sync attempt
```
PMG ships with sensible defaults for `min_interval` and `timeout`. See the [PMG config template](https://github.com/safedep/pmg/blob/main/config/config.template.yml) for the current values.
Disable auto-sync in ephemeral environments such as CI runners and throwaway VMs. The detached process may be torn down before it finishes draining. Run an explicit `pmg cloud sync` at job-end instead.
## Manual sync
Run `pmg cloud sync` to push local events immediately instead of waiting for the next automatic sync:
* **In CI/CD pipelines:** Disable auto-sync and add `pmg cloud sync` as a post-step after PMG runs, so events are flushed before the runner is torn down.
* **Forcing an immediate push:** When you don't want to wait out the cooldown, for example right after a large dependency update.
## View package events in SafeDep Cloud
After syncing, open [app.safedep.io](https://app.safedep.io), select **Endpoint Hub** in the sidebar, and pick your endpoint to see the **Package Events** timeline.
## Next steps
Install PMG and learn the basics
# Cloud FAQ
Source: https://docs.safedep.io/governance/cloud/faq
Frequently asked questions about SafeDep Cloud authentication and usage
## Authentication Errors
### User Not Found
**Error Message:**
```
ERRO[0001] Failed to execute whoami: rpc error: code = Unauthenticated desc = unauthenticated: Token auth failed: No user: record not found
```
**Cause:** The user account is not registered with SafeDep Cloud.
**Solution:**
Follow the [quickstart guide](/governance/cloud/quickstart) to register with SafeDep Cloud
Ensure you've completed the full onboarding process including tenant creation
Verify your email address if required during registration
### Tenant Not Found
**Error Message:**
```
ERRO[0001] Failed to execute query: rpc error: code = Unknown desc = failed to resolve tenant: record not found
```
**Cause:** The tenant is not properly configured in Vet, or the tenant domain is incorrect.
**Solution:**
```bash theme={null}
vet auth configure --tenant
```
```bash theme={null}
vet cloud login --tenant
```
If you've forgotten your tenant domain:
```bash theme={null}
vet cloud login
vet cloud whoami # Shows your identity and accessible tenants
```
## API and Connectivity Issues
### Rate Limiting
**Error Message:**
```
ERRO[0001] Request failed: rpc error: code = ResourceExhausted desc = rate limit exceeded
```
**Solution:**
* Back off briefly and retry. SafeDep enforces per-second rate limits, so a short pause clears the error (see the [API reference](/reference/api-introduction#rate-limiting) for the current limits)
* Reduce the frequency of API calls and use batch operations where possible
* Contact support for increased rate limits if needed
### Network Connectivity
**Error Message:**
```
ERRO[0001] Failed to connect: dial tcp: lookup api.safedep.io: no such host
```
**Solution:**
* Check internet connectivity
* Verify DNS resolution for `api.safedep.io`
* Check firewall settings and proxy configuration
* Ensure HTTPS traffic on port 443 is allowed
### SSL/TLS Issues
**Error Message:**
```
ERRO[0001] Failed to connect: x509: certificate verify failed
```
**Solution:**
* Update your system's CA certificates
* Check system clock accuracy
* Verify no proxy is interfering with SSL
* Try updating Vet to the latest version
## Configuration Issues
### Invalid API Key
**Error Message:**
```
ERRO[0001] Authentication failed: invalid API key
```
**Solution:**
Create a new API key in your SafeDep Cloud tenant settings
```bash theme={null}
export SAFEDEP_API_KEY=your-new-api-key
vet auth configure --tenant your-tenant
```
Ensure the API key has the necessary permissions for your operations
### Expired Tokens
**Error Message:**
```
ERRO[0001] Token expired: please re-authenticate
```
**Solution:**
```bash theme={null}
# Re-authenticate with device flow
vet cloud login --tenant your-tenant
# Verify authentication
vet cloud whoami
```
### Configuration File Issues
**Error Message:**
```
ERRO[0001] Failed to load config: permission denied
```
**Solution:**
* Check file permissions on Vet configuration directory
* Ensure user has write access to `~/.config/vet/`
* Try running with appropriate permissions
* Clear and recreate configuration if corrupted
## Data Sync Issues
### Sync Failures
**Error Message:**
```
WARN[0001] Failed to sync data to cloud: project not found
```
**Solution:**
* Verify project name and version are correctly specified
* Check that tenant has permissions for data sync
* Ensure API key includes sync permissions
* Retry with proper project identification
### Missing Data
**Issue:** Scanned data doesn't appear in SafeDep Cloud
**Solution:**
Ensure `--report-sync` is included in your scan command
```bash theme={null}
vet scan -D . --report-sync \
--report-sync-project "your-project" \
--report-sync-project-version "main"
```
```bash theme={null}
vet auth verify
```
### Query Failures
**Error Message:**
```
ERRO[0001] Query execution failed: syntax error
```
**Solution:**
* Verify SQL syntax is correct
* Check table and column names in schema
* Use `safedep query schema list` to view available tables
* Ensure proper quoting for identifiers with special characters
## GitHub Actions Issues
### Secret Configuration
**Issue:** GitHub Action fails with authentication errors
**Solution:**
Verify these secrets are set in your repository:
* `SAFEDEP_CLOUD_API_KEY`
* `SAFEDEP_CLOUD_TENANT_DOMAIN`
```yaml theme={null}
- name: Run vet
uses: safedep/vet-action@v1
with:
cloud: true
cloud-key: ${{ secrets.SAFEDEP_CLOUD_API_KEY }}
cloud-tenant: ${{ secrets.SAFEDEP_CLOUD_TENANT_DOMAIN }}
```
### Action Version Issues
**Issue:** Action fails with "unknown parameter" errors
**Solution:**
* Update to the latest version of vet-action
* Check the action documentation for parameter changes
* Verify you're using supported parameters for your action version
## Performance Issues
### Slow Scans
**Issue:** Scans take too long to complete
**Solution:**
* Use path exclusions to skip irrelevant directories
* Scan specific manifest files instead of entire directories
* Adjust timeout settings for malware analysis
* Use JSON dump workflow for repeated analysis
### Memory Issues
**Issue:** Vet runs out of memory during scans
**Solution:**
* Scan smaller directory trees
* Use exclusions to skip large dependency directories
* Increase available memory in CI/CD environments
* Process large monorepos in batches
## Getting Additional Help
Report bugs and request features
Setup guides
Join our Discord community for help
Direct support
## Common Debugging Commands
### Check Authentication Status
```bash theme={null}
vet cloud whoami
```
### Verify API Configuration
```bash theme={null}
vet auth verify
```
### Test Cloud Connectivity
```bash theme={null}
vet cloud ping
```
### Clear Configuration
```bash theme={null}
rm ~/.safedep/vet-auth.yml
```
### Re-authenticate
```bash theme={null}
vet cloud login --tenant your-tenant
```
### View Detailed Logs
```bash theme={null}
vet --debug scan -D . --report-sync
```
# Malware Analysis
Source: https://docs.safedep.io/governance/cloud/malware-analysis
Detect malicious packages in your dependencies by querying SafeDep's known malicious packages database
Malware analysis is available for free. No API key is required to query SafeDep's known malicious packages database. See [pricing](https://safedep.io/pricing) for SafeDep Cloud features.
Check your open source dependencies against SafeDep's continuously updated database of known malicious packages using [Vet](https://github.com/safedep/vet). Vet queries SafeDep's threat intelligence service, which is populated through continuous static and dynamic analysis of packages from public registries.
This page covers the **fast path**: a free lookup against SafeDep's database of known malicious packages, built into Vet, PMG, and the SafeDep MCP server. It answers in milliseconds but only covers packages SafeDep has already analyzed. To run a **new analysis** of a specific component (any package version, an IDE extension, or a GitHub repository), use [On-Demand Package Scanning](/package-security/scan/overview), an independent paid feature. Vet itself no longer submits packages for analysis. On-demand scanning moved to `safedep package scan`.
## Supported Ecosystems
JavaScript and TypeScript packages
Python packages and wheels
Go language modules
Ruby packages and gems
GitHub Action workflows
Visual Studio Code extensions
## Requirements
Install the latest version of [Vet](https://github.com/safedep/vet/#installation).
No API key is required. Querying known malicious packages works out of the box, with no SafeDep Cloud onboarding. A SafeDep Cloud account is optional and enables cloud features such as report sync and higher rate limits. See the [SafeDep Cloud Quickstart](/governance/cloud/quickstart).
## Repository Scanning
### Basic Malware Scanning
Enable malware analysis with the `--malware-query` flag:
```bash theme={null}
vet scan -D /path/to/code --malware-query
```
Because Vet performs a lookup against known malicious packages, results are returned quickly, which works well for pull requests and CI/CD pipelines.
The `--malware` flag is a deprecated alias for `--malware-query` and behaves identically. It will be removed in a future release.
### Specific Manifest Scanning
Scan individual package manifest files:
```bash theme={null}
# npm projects
vet scan -M package-lock.json --malware-query
# Python projects
vet scan -M requirements.txt --malware-query
# Go projects
vet scan -M go.mod --malware-query
# Ruby projects
vet scan -M Gemfile.lock --malware-query
```
### PURL-Based Scanning
Scan specific packages using Package URLs:
```bash theme={null}
vet scan --purl pkg:npm/llm-oracle@1.0.2 --malware-query
```
## Visual Studio Code Extensions
Scan locally installed VS Code extensions:
```bash theme={null}
vet scan --vsx --malware-query
```
VS Code extension scanning is supported only for local developer machines, not in CI/CD environments.
## GitHub Actions Integration
### vet-action
Enable malicious package protection in GitHub repositories using [vet-action](https://github.com/safedep/vet-action):
```yaml theme={null}
name: Malware Protection
on:
pull_request:
branches: [ main ]
jobs:
malware-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run malware analysis
uses: safedep/vet-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
Known malicious package detection does not require an API key. SafeDep Cloud configuration (`cloud`, `cloud-key`, `cloud-tenant`) is optional and only enables cloud reporting and sync. See the [vet-action documentation](https://github.com/safedep/vet-action?tab=readme-ov-file#cloud-mode) for cloud mode.
### Pull Request Integration
When enabled, Vet scans changed packages for malware and provides results directly in pull requests:
Expand comments to view detailed package analysis results:
## Understanding Results
### Classification Levels
* **SAFE**: No malicious behavior detected
* **SUSPICIOUS**: Potentially risky patterns identified
* **MALICIOUS**: Confirmed malicious behavior found
### How Packages Are Analyzed
SafeDep continuously analyzes packages from public registries and records the results in its known malicious packages database. Vet queries this database during a scan. The analysis behind these records combines:
* Code pattern analysis
* Suspicious function detection
* Obfuscation identification
* Network communication patterns
* File system access patterns
* Process execution analysis
* Package metadata anomalies
* Publisher reputation analysis
* Distribution pattern analysis
## CI/CD Integration Examples
### GitLab CI
```yaml theme={null}
stages:
- security
malware-scan:
stage: security
image: ghcr.io/safedep/vet:latest
script:
- vet scan -D . --malware-query --report-json malware-report.json
artifacts:
reports:
security: malware-report.json
```
### Jenkins Pipeline
```groovy theme={null}
pipeline {
agent any
stages {
stage('Malware Scan') {
steps {
sh 'vet scan -D . --malware-query --report-json malware-results.json'
archiveArtifacts artifacts: 'malware-results.json'
}
}
}
}
```
## Troubleshooting
* Review the analysis details
* Contact SafeDep support with the package details
* Use exceptions management for temporary overrides
* Only packages already present in SafeDep's known malicious packages database are reported. Packages not yet analyzed are not flagged.
* Ensure you are using the latest version of Vet
* Check network connectivity to SafeDep Cloud
Run an on-demand analysis of a package, IDE extension, or GitHub repository not yet in the database
Complete GitHub Actions integration guide
Optional cloud features such as report sync and higher rate limits
Report bugs or request features for malware analysis
# SafeDep Cloud
Source: https://docs.safedep.io/governance/cloud/overview
Hosted control plane that centralizes policy, malicious-package intelligence, and fleet-wide visibility across your organization.
SafeDep Cloud is the hosted control plane for managing open source software supply chain risk across your organization. It centralizes policy, malicious-package intelligence, and fleet-wide visibility on top of SafeDep's open source tools. It is optional: [Vet](/governance/vet/overview), [PMG](/package-security/pmg/overview), and [xBom](/governance/xbom/overview) all work without it. Reach for Cloud when you want to govern risk across many repositories and machines from one place.
## Set up Cloud
Create a tenant and connect your first project in minutes.
Cloud authentication methods and API access patterns.
## Govern across your fleet
Sync Vet scan data and policy violations to Cloud for centralized query and reporting.
See AI tooling and package activity across developer machines, CI runners, and agent sandboxes.
Detect malicious packages in your dependencies with Cloud code analysis.
Compare the Free, Pro, and Enterprise plans.
# Malicious Package Exclusions
Source: https://docs.safedep.io/governance/cloud/package-exclusions
Manage package exclusions for malicious package analysis in SafeDep Cloud
Malicious Package Exclusions are available in SafeDep Cloud **Pro and above**.
See [pricing](https://safedep.io/pricing).
Only tenant owners can create, edit, or delete exclusions. If you can view package analysis results but cannot manage an exclusion, contact your tenant owner.
Malicious Package Exclusions let your tenant suppress specific package findings from malicious package analysis after review. Use them when your team has reviewed a package and decided it is expected in your environment, so SafeDep stops surfacing the same finding repeatedly.
Exclusions are tenant-specific: a package trusted in one SafeDep Cloud tenant is not automatically trusted in another. An exclusion does not mark a package as globally safe. It suppresses the finding only for the package identity you excluded (ecosystem, name, and version) within your tenant.
## Where Exclusions Are Respected
Exclusions act as a tenant-level source of truth across SafeDep tools and integrations connected to SafeDep Cloud. This currently includes:
* SafeDep Cloud package analysis views in `app.safedep.io`
* GitHub App
* `vet` in cloud mode
* `vet-action` in cloud mode
## When To Use An Exclusion
Use an exclusion when:
* a package repeatedly appears as suspicious or malicious and your team has already reviewed it
* you want to reduce noise without disabling malicious package protection
* you want to trust a package temporarily by setting an expiry date
Do not use exclusions as a workaround for packages SafeDep has already verified as malicious.
## What An Exclusion Applies To
Each exclusion is scoped to a package ecosystem, name, and version. You can also add a reason and an optional expiry date for temporary exceptions.
Malicious Package Exclusions apply only to malicious package analysis. They do not change vulnerability findings or other SafeDep checks.
## Create An Exclusion From Settings
The main place to manage exclusions is the SafeDep Cloud settings page:
Go to [app.safedep.io/settings/package-exclusions](https://app.safedep.io/settings/package-exclusions)
Click **Create Exclusion**
Fill in the package details
Save the exclusion
The page shows your existing exclusions in a table and a **Create Exclusion** button in the top-right corner.
### Fields
When creating an exclusion, SafeDep Cloud asks for:
* **Ecosystem**: The package ecosystem, such as npm or PyPI
* **Package Name**: The package to exclude
* **Version**: The specific version to exclude
* **Reason**: Why your team is excluding this package
* **Expires At**: Optional expiry date for temporary exceptions
Use `0` in the **Version** field to exclude all versions of a package.
## Create Or Manage An Exclusion From Package Analysis
You can also start from a package analysis result:
Open a malicious package result in SafeDep Cloud
Use the header action to create or manage an exclusion
Depending on the current state, SafeDep Cloud shows one of these actions:
* **Create Exclusion** if no exclusion exists yet
* **Manage Exclusion** if an exclusion already exists for that package and version
This is a convenient path when you are already investigating a package and want to create or review an exclusion without navigating back to Settings.
## Manage Existing Exclusions
The exclusions table helps you review and maintain the exclusions already configured in your tenant.
Each row shows the ecosystem, package name, version, reason, current status, expiry date, and available actions for that exclusion.
You can filter exclusions by:
* ecosystem
* package name
* version
* expiry status
* expiry date
From the table, you can also:
* edit an exclusion
* delete an exclusion
* review the reason and expiry date attached to each exclusion
* quickly see whether an exclusion is active or close to expiring from the status badge
### Edit An Exclusion
Use **Edit Exclusion** when you need to change:
* the version
* the reason
* the expiry date
* the package identity
This is useful when an exclusion started as a short-term exception and later needs to be extended, narrowed, or documented more clearly.
### Delete An Exclusion
Delete an exclusion when you want malicious package analysis to apply normally again.
After you delete an exclusion, later scans or package analysis results may surface that package again if it is still detected as suspicious or malicious.
## How Exclusions Work
SafeDep Cloud applies exclusions using the package identity you provide.
### Exact Version Vs All Versions
Version is matched exactly. Enter `4.17.21` to exclude only that version, or `0` to exclude all versions. (`0` is a special value meaning "all versions," not the literal version `0`.)
If both an exact-version exclusion and an all-version exclusion could match, the exact version takes precedence.
### Expiry
An exclusion with an expiry date stops applying automatically after the expiry time passes. This is useful for temporary investigation windows, migrations, and short-lived exceptions.
### Verified Malicious Packages
SafeDep Cloud does not allow exclusions for packages it has already verified as malicious. If you try to create or update such an exclusion, SafeDep Cloud returns an error instead of saving it.
## What To Expect After Adding An Exclusion
After an exclusion is added:
* future malicious package analysis for that package stops surfacing the excluded result across SafeDep tools and integrations that respect tenant exclusions
* the exclusion remains listed on the settings page so authorized users can review, update, or delete it later
* you may need to refresh the page or rerun the scan to confirm the updated behavior
If you later delete the exclusion or let it expire, the package can reappear in analysis results.
## Troubleshooting
### I can see the page, but I cannot create an exclusion
Exclusion management is currently limited to tenant owners.
If the button is disabled, ask your tenant owner to create or manage the exclusion for you.
### I see a paywall instead of the exclusions table
Malicious Package Exclusions are available in SafeDep Cloud Pro and above.
Upgrade your plan or contact your SafeDep representative if you need access.
### Why did my exclusion stop working?
The most common reasons are:
* the exclusion expired
* the package ecosystem, name, or version does not match the current finding
* you may need to refresh the page or rerun the relevant scan to confirm the latest result state
If SafeDep blocks you while creating or updating an exclusion, check whether the package has already been verified as malicious.
### When should I use version `0`?
Use version `0` when you want to exclude all versions of a package instead of just one specific version.
## Next Steps
See how SafeDep respects exclusions in GitHub pull request checks
Learn how SafeDep Cloud analyzes packages for malicious behavior
Configure access to your SafeDep Cloud tenant
Find answers to common SafeDep Cloud questions
# SafeDep Cloud Quickstart
Source: https://docs.safedep.io/governance/cloud/quickstart
Create a SafeDep Cloud tenant, authenticate the safedep CLI, connect a data source, and run your first query
SafeDep Cloud aggregates findings from SafeDep tools into a tenant you can query and govern from one place. The [`safedep` CLI](https://github.com/safedep/cli) is the command line client for your tenant: it handles login, endpoint fleet visibility, and SQL queries over synced data. This guide takes you from a new account to your first query.
## Create your tenant
Sign up at [app.safedep.io](https://app.safedep.io/).
Complete onboarding and create your tenant. Note the **tenant domain** (for example `your-company.safedep.io`). Tools use it to identify your tenant.
You do not need to create an API key by hand. `safedep auth login` creates one during login. CI/CD integrations that need a static key can generate one at [app.safedep.io/settings/api-keys](https://app.safedep.io/settings/api-keys).
## Install the safedep CLI
```bash theme={null}
brew install safedep/tap/cli
```
```bash theme={null}
npm install -g @safedep/cli
```
Prebuilt binaries for Linux, macOS, and Windows are on the [releases page](https://github.com/safedep/cli/releases).
## Log in
```bash theme={null}
safedep auth login
```
The command runs an OAuth device flow in your browser, selects a tenant you have access to, creates an API key, and stores the credentials in your OS keychain.
Confirm the session:
```bash theme={null}
safedep auth status
```
For static API key login, credential profiles, and non-interactive use, see the [authentication guide](/governance/cloud/authentication) and the [CLI command reference](https://github.com/safedep/cli/tree/main/docs/cmd).
## Connect a data source
A new tenant is empty until a tool syncs data into it. Pick the source that matches what you want to see:
Sync packages, vulnerabilities, and policy violations from `vet scan`, locally or in CI/CD
Track package manager activity on developer machines with PMG
Discover coding agents, MCP servers, and Agent Skills on endpoints with `vet endpoint scan`
Scan pull requests with zero setup and link the installation to your tenant
Once PMG or `vet endpoint scan` reports from a machine, check your fleet from the CLI:
```bash theme={null}
safedep endpoint status
```
## Query your data
Query your tenant's aggregated inventory and findings with SQL: projects, packages, endpoints, and security findings enriched with threat intelligence (vulnerabilities, EPSS, CISA KEV, OpenSSF Scorecard).
For example, find your top remediation targets by critical CVE count:
```bash theme={null}
safedep query exec --sql "
SELECT packages.name, COUNT(DISTINCT vulnerabilities.vuln_id) AS critical_vulns
FROM packages
JOIN component_vulnerabilities ON component_vulnerabilities.component_id = packages.id
JOIN vulnerabilities ON vulnerabilities.vuln_id = component_vulnerabilities.vulnerability_id
WHERE vulnerabilities.severity_rating = 'CRITICAL'
GROUP BY packages.name
ORDER BY critical_vulns DESC" --limit 15
```
Prefer a UI? Run the same queries from the web console at [app.safedep.io](https://app.safedep.io).
See the [SQL query guide](/reference/sql-query) for the full schema, query rules, and worked examples: severity breakdowns, EPSS enrichment, malware findings, endpoint audit trails, and more.
## Next steps
Cloud authentication methods and API access patterns
Detect malicious packages in your dependencies with Cloud code analysis
Answers to common questions
Integrate SafeDep Cloud with your own systems
# Cloud Sync
Source: https://docs.safedep.io/governance/cloud/sync
How vet, PMG, and vet endpoint scan send data to your SafeDep Cloud tenant, and how to set up each source
Your SafeDep Cloud tenant receives data from more than one tool. Each source syncs through its own path and shows up in a different part of the product:
| Source | What it sends | Where it appears |
| ---------------------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| [`vet scan`](#sync-vet-scan-results) | Packages, vulnerabilities, and policy violations per project | Projects and the [SQL query tables](/reference/sql-query) |
| [PMG](#sync-package-events-from-pmg) | Package install and usage events from endpoints | [Endpoint Hub › Package Guard](/governance/cloud/endpoint-hub/package-guard) |
| [`vet endpoint scan`](#sync-ai-tool-inventory) | AI tools, coding agents, MCP servers, and Agent Skills found on endpoints | [Endpoint Hub › Inventory](/governance/cloud/endpoint-hub/inventory) |
| [GitHub App](/governance/integrations/github) | Pull request scan results from repositories it is installed on | Projects |
Every source needs SafeDep Cloud credentials: a tenant domain and an API key. New to Cloud? Start with the [quickstart](/governance/cloud/quickstart).
This page covers `vet scan` sync in full. PMG and endpoint inventory sync have their own setup guides, summarized below.
## Sync vet scan results
`vet scan --report-sync` uploads scan results to your tenant, grouped by project and version.
### Prerequisites
vet needs SafeDep Cloud credentials before it can sync. The fastest path is the guided setup, which walks through account, tenant, and credentials in one command:
```bash theme={null}
vet cloud quickstart
```
Already onboarded? See the [authentication guide](/governance/cloud/authentication#vet) to configure vet with an existing tenant and API key.
### Sync a scan
```bash theme={null}
vet scan -M /path/to/package-lock.json --report-sync \
--report-sync-project my-project \
--report-sync-project-version my-project-version
```
* `--report-sync-project`: project identifier. Use a consistent convention, like the repository path (`github.com/org/repo`), so names stay unique across teams.
* `--report-sync-project-version`: project version, typically a branch, tag, or commit
Scan a whole repository the same way:
```bash theme={null}
vet scan -D /path/to/repository \
--report-sync \
--report-sync-project github.com/org/repo \
--report-sync-project-version main
```
The examples use `package-lock.json`, but vet supports many package manifest formats and code analysis.
### Separate environments
The version field also separates environments. Sync the same project as `production`, `staging`, or a feature branch name, then compare them side by side in SafeDep Cloud:
```bash theme={null}
vet scan -D . \
--report-sync \
--report-sync-project myapp \
--report-sync-project-version staging
```
### Enforce policy while syncing
Sync and policy enforcement run in the same scan. Add a policy suite and `--filter-fail` to gate the pipeline while results still sync to your tenant:
```bash theme={null}
vet scan -D . \
--policy-suite policy.yml \
--filter-fail \
--report-sync \
--report-sync-project critical-app \
--report-sync-project-version main
```
`--policy-suite` takes a v2 policy file (see the [example in the vet repository](https://github.com/safedep/vet/blob/main/samples/policy-v2.yml)); `--filter-fail` exits non-zero on a violation. The older `--filter-suite` format is documented in the [policy reference](/reference/policy-as-code).
### Sync from GitHub Actions
[vet-action](https://github.com/safedep/vet-action) syncs automatically when its cloud inputs are set. Add `SAFEDEP_CLOUD_API_KEY` and `SAFEDEP_CLOUD_TENANT_DOMAIN` as [GitHub Actions secrets](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions), then enable cloud sync in your workflow:
```yaml theme={null}
- name: Run vet
uses: safedep/vet-action@v1
with:
cloud: true
cloud-key: ${{ secrets.SAFEDEP_CLOUD_API_KEY }}
cloud-tenant: ${{ secrets.SAFEDEP_CLOUD_TENANT_DOMAIN }}
```
vet-action sets project identification from the repository: the project name is `${{ github.repository }}` and the version is `${{ github.ref_name }}`.
### Sync from other CI systems
The pattern is the same on any CI system: provide `SAFEDEP_API_KEY` and `SAFEDEP_TENANT_ID` as environment variables and run `vet scan` with the `--report-sync` flags. See [CI/CD & Platform Integrations](/governance/integrations/overview) for the full GitLab and Bitbucket guides.
```yaml theme={null}
stages:
- security
security-scan:
stage: security
image: ghcr.io/safedep/vet:latest
script:
- vet scan -D . --report-sync --report-sync-project $CI_PROJECT_PATH --report-sync-project-version $CI_COMMIT_REF_NAME
variables:
SAFEDEP_API_KEY: $SAFEDEP_API_KEY
SAFEDEP_TENANT_ID: $SAFEDEP_TENANT_ID
only:
- main
- develop
- merge_requests
```
```groovy theme={null}
pipeline {
agent any
environment {
SAFEDEP_API_KEY = credentials('safedep-api-key')
SAFEDEP_TENANT_ID = credentials('safedep-tenant-id')
}
stages {
stage('Security Scan') {
steps {
sh """
vet scan -D . \
--report-sync \
--report-sync-project ${env.JOB_NAME} \
--report-sync-project-version ${env.BRANCH_NAME}
"""
}
}
}
}
```
```yaml theme={null}
trigger:
branches:
include:
- main
- develop
variables:
- group: safedep-credentials
jobs:
- job: SecurityScan
displayName: 'Security Scan and Sync'
pool:
vmImage: 'ubuntu-latest'
steps:
- script: |
vet scan -D . \
--report-sync \
--report-sync-project $(Build.Repository.Name) \
--report-sync-project-version $(Build.SourceBranchName)
displayName: 'Run vet security scan'
env:
SAFEDEP_API_KEY: $(safedep-api-key)
SAFEDEP_TENANT_ID: $(safedep-tenant-id)
```
### What gets synced
* Discovered packages and versions
* Dependency relationships and metadata
* Package manifest locations and types
* Vulnerability information and severity levels
* OpenSSF Scorecard metrics
* License compliance data
* Malware analysis results (if enabled)
* Policy rule violations and details
* Exception applications and status
* Project identification and versioning
* Scan timestamps and environment info
* Git commit information (when available)
## Sync package events from PMG
[PMG](/package-security/pmg/overview) intercepts package manager commands on the endpoint and records each install as an event. With cloud sync enabled, PMG drains its local event log to your tenant automatically after each invocation, and `pmg cloud sync` pushes events immediately.
Setup, login, and auto-sync tuning live on the Package Guard page:
Enable PMG cloud sync and view package events in Endpoint Hub
## Sync AI tool inventory
`vet endpoint scan` discovers AI tooling on a machine (coding agents, MCP servers, CLI tools, IDE extensions, Agent Skills) and delivers the inventory to your tenant when vet has credentials configured.
Enable inventory sync and browse discovered AI tooling in Endpoint Hub
## Query synced data
Once data lands in your tenant, query it with `safedep query exec` or the web console. The [quickstart](/governance/cloud/quickstart#query-your-data) has a worked example, and the [SQL query guide](/reference/sql-query) covers the full schema, join model, and queries across vulnerabilities, licenses, malware findings, and endpoint events.
## Troubleshooting
* Verify the API key and tenant configuration (`vet auth verify`)
* Check network connectivity to SafeDep Cloud
* Ensure project names don't contain invalid characters
* Confirm `--report-sync` and the project flags are set
* Check that the scan completed successfully
* Verify the project name and version identifiers match what you expect
* Verify the API key has sync permissions
* Check the tenant domain configuration
* Confirm credentials are set correctly in your CI/CD environment
Set up the safedep CLI and run your first query
SafeDep Cloud authentication methods
AI tooling and package activity across your endpoints
Complete GitHub Actions integration guide
# Talk to SafeDep
Source: https://docs.safedep.io/governance/cloud/talk-to-safedep
Ask your supply chain questions in plain English and let an AI agent answer from your SafeDep Cloud tenant using the safedep CLI.
Install the SafeDep skill and your AI coding agent can answer questions about your tenant in plain English. Ask "which projects have critical vulnerabilities?" and the agent translates it into a [SafeDep Cloud SQL](/reference/sql-query) query, runs it through the `safedep` CLI, and hands back the result. You skip the SQL and the schema lookup.
## Prerequisites
* A [SafeDep Cloud tenant](/governance/cloud/quickstart) with data synced from your tools. The agent can only answer what your tenant holds, so [connect at least one source](/governance/cloud/sync) first.
* The `safedep` CLI installed and signed in. Run `safedep auth login`, then `safedep auth status` to confirm the tenant. See the [authentication guide](/governance/cloud/authentication).
* An AI coding agent that supports skills, such as Claude Code.
## Install the skill
Install the SafeDep skill in your agent. See [Install the SafeDep Skill](/get-started/safedep-skill) for Claude Code, Cursor, and other agents.
## Ask a question
Type your question the way you would ask a teammate:
```text theme={null}
How many projects are being monitored?
```
The agent loads the SafeDep skill, writes a tenant-scoped query, runs `safedep query exec`, and replies with the count. Every answer traces back to a real query, so you can ask the agent to show the SQL it ran.
Questions that map cleanly to your synced data:
| Ask | Reads from |
| -------------------------------------------------------------- | -------------------------------------------------------------------- |
| How many projects are we monitoring? | Projects |
| Which projects have critical vulnerabilities? | Projects, packages, vulnerabilities |
| Any malicious packages blocked across dev endpoints last week? | [Package Guard events](/governance/cloud/endpoint-hub/package-guard) |
| Which developer machines are running PMG? | [Endpoints](/governance/cloud/endpoint-hub/inventory) |
| Which endpoints have not synced in 30 days? | Endpoints |
## Write your own queries
The skill runs the same query interface you can drive by hand. To see the full table list, join edges, and query rules, read [SafeDep Cloud SQL](/reference/sql-query). When an agent answer looks off, run the query yourself with `safedep query exec` and compare.
Queries are scoped to your authenticated tenant. The agent never sees data outside it, and you never write a tenant filter yourself.
Investigation playbooks for developer endpoints: exposure checks, block reviews, bypass audits.
# Usage & On-Demand Billing
Source: https://docs.safedep.io/governance/cloud/usage-billing
How SafeDep Cloud seat allowances and usage-based on-demand billing work, and the CLI commands to inspect and manage them.
Paid SafeDep Cloud plans include a monthly usage allowance for metered features. This page explains how the allowance is counted, what happens when you reach it, and how to opt in to usage-based billing beyond it.
The commands on this page use the `safedep` CLI. New to it? See [SafeDep CLI Tools](/get-started/cli-tools) for what it is and how to install it.
## How allowances work
A metered feature's monthly allowance is a per-seat amount multiplied by your subscription's seats, pooled across the whole tenant. Any member can consume from the shared pool. There is no per-user split. The allowance resets each billing period.
[On-demand package scans](/package-security/scan/overview) are metered this way today. Per-seat amounts and unit prices are listed on the [pricing page](https://safedep.io/pricing).
## Check your usage
```bash theme={null}
safedep subscription ondemand status
```
The output shows, per metered feature:
* **Included limit and consumed**: the pooled allowance for the current period and how much of it is used.
* **Period end**: when the allowance resets.
* **Overage**: units consumed beyond the allowance this period, and their billed value, when on-demand billing is enabled.
It also shows the account-level state: whether on-demand billing is enabled, whether a payment method is on file, and the payment posture.
## What happens at the allowance limit
* **On-demand billing disabled** (the default): requests beyond the allowance are denied until the period resets or you add seats.
* **On-demand billing enabled**: usage beyond the allowance is billed per unit, up to a monthly spending cap.
## Enable on-demand billing
Prerequisites: an active paid subscription with a payment method on file. Add a payment method through the billing portal if needed:
```bash theme={null}
safedep subscription portal open
```
Then opt in:
```bash theme={null}
safedep subscription ondemand enable --accept-terms
```
The `--accept-terms` flag records your acceptance of the on-demand billing [terms](https://safedep.io/terms/), including the terms version, for the tenant account. Without the flag, the command prints the terms location and makes no change.
## The spending cap
On-demand billing has a monthly spending cap that bounds how much overage can be billed in a period. When the cap is reached, further over-allowance requests are denied until the period resets, so a runaway automation cannot create an unbounded bill. The default cap is listed on the [pricing page](https://safedep.io/pricing). Contact [support](mailto:support@safedep.io) to adjust it.
## Disable on-demand billing
```bash theme={null}
safedep subscription ondemand disable
```
Overage billing stops and the included allowance limits apply again. Usage already billed in the current period remains payable.
## Invoices and payment methods
The billing portal handles payment methods, invoices, and billing history:
```bash theme={null}
safedep subscription portal open
```
The metered feature this page's allowances apply to.
Plans, per-seat allowances, and unit prices.
# CycloneDX SBOM
Source: https://docs.safedep.io/governance/cyclonedx-sbom
Generate a Software Bill of Materials (SBOM) with security metadata using Vet
`vet` supports [CycloneDX v1.6](https://cyclonedx.org/docs/1.6/json) SBOM generation. The generated SBOM lists all packages and their dependencies, including security metadata: detected vulnerabilities, malware, and license information.
## Quick Start
Generate an SBOM with a custom application name:
```bash theme={null}
vet scan -D /path/to/project \
--report-cdx report.cdx.json \
--report-cdx-app-name myproject
```
The `--report-cdx-app-name` parameter is optional. If omitted, Vet will use a default application name.
## What's Included in the SBOM
The generated CycloneDX SBOM contains:
Complete list of all direct and transitive dependencies
Known vulnerabilities from OSV database and other sources
License identifiers and compliance data for each component
Results from malware analysis and threat detection
## Advanced Usage
### Custom Application Metadata
Provide detailed metadata about your application:
```bash theme={null}
vet scan -D /path/to/project \
--report-cdx myapp-v1.2.3.cdx.json \
--report-cdx-app-name "MyApplication"
```
### Combined with Other Reports
Generate multiple report formats simultaneously:
```bash theme={null}
vet scan -D /path/to/project \
--report-cdx sbom.cdx.json \
--report-json results.json \
--report-markdown report.md \
--report-cdx-app-name "production-app"
```
### Integration with CI/CD
```yaml theme={null}
name: Generate SBOM
on: [push, pull_request]
jobs:
sbom:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Generate SBOM
run: |
docker run --rm -v "$PWD:/app" ghcr.io/safedep/vet:latest \
scan -D /app \
--report-cdx /app/sbom.cdx.json \
--report-cdx-app-name "${{ github.repository }}"
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.cdx.json
```
```yaml theme={null}
generate-sbom:
image: ghcr.io/safedep/vet:latest
script:
- vet scan -D . --report-cdx sbom.cdx.json --report-cdx-app-name "$CI_PROJECT_NAME"
artifacts:
reports:
cyclonedx: sbom.cdx.json
paths:
- sbom.cdx.json
expire_in: 30 days
```
## Sample SBOMs
Example SBOM for a Node.js chat application
Example SBOM for an Express.js web application
## SBOM Analysis and Consumption
### Viewing SBOM Content
Install the [CycloneDX CLI](https://github.com/CycloneDX/cyclonedx-cli) (a .NET global tool) to validate and convert your SBOM:
```bash theme={null}
dotnet tool install --global CycloneDX
# Validate SBOM
cyclonedx validate --input-file sbom.cdx.json
# Convert to other formats
cyclonedx convert --input-file sbom.cdx.json --output-file sbom.xml --output-format xml
```
### Integration with Security Tools
Many security tools can consume CycloneDX SBOMs:
* **Dependency Track**: Import SBOMs for vulnerability monitoring
* **FOSSA**: License compliance analysis
* **Snyk**: Security scanning and monitoring
* **JFrog Xray**: Artifact analysis and security scanning
## Naming Convention
Use a consistent pattern for SBOM filenames:
```
{app-name}-{version}-{environment}.cdx.json
myapp-1.2.3-production.cdx.json
```
## Troubleshooting
For projects with many dependencies, SBOMs can become large. Consider:
* Filtering out development dependencies in production SBOMs
* Using compressed storage formats
* Implementing SBOM splitting for microservices
If components are missing from your SBOM:
* Ensure all package manifest files are included in the scan
* Check that Vet supports your package manager
* Verify dependencies are properly declared in manifest files
If SBOM validation fails:
* Check the CycloneDX schema version compatibility
* Verify all required fields are present
* Use cyclonedx-cli for detailed validation errors
Learn more about the CycloneDX v1.6 JSON specification
Explore Vet's complete SBOM generation capabilities
Learn how to create accurate dependency inventories
# Bitbucket Pipes
Source: https://docs.safedep.io/governance/integrations/bitbucket
Native Bitbucket Cloud integration for SafeDep
SafeDep integrates with **Bitbucket Cloud** via [Bitbucket Pipes](https://bitbucket.org/product/features/pipelines/integrations), so you can add dependency scanning to any Bitbucket CI/CD pipeline.
## Prerequisites
Bitbucket account with access to your project
A source code repository to integrate SafeDep in `pipelines`.
## Quick Start
### 1. Enable CI on Your Project
If you don't already have a `bitbucket-pipelines.yml`, create one
```bash theme={null}
touch bitbucket-pipelines.yml
```
### 2. Add SafeDep in your pipeline
```yaml theme={null}
image: atlassian/default-image:3
pipelines:
default:
- step:
name: Run vet pipe
script:
- pipe: safedep/vet-pipe:v1.2.0
```
That's it. Default values are used for dependency scanning. For policy customization and [SafeDep Cloud](/governance/cloud/sync) integration, see the [Inputs](#inputs), [Policy](#policy-customization), and [Cloud Sync](#cloud-sync) sections.
### On Pull Request
`vet-pipe` includes a feature to scan only the packages changed within a **Pull Request**. However, this functionality relies on environment variables (such as `BITBUCKET_PR_DESTINATION_BRANCH`) that are only populated when using Bitbucket's `pull-requests` pipeline trigger.
To enable changed packages scanning for **PRs** while still supporting **Push** and **Merge** events, you must configure both the `pull-requests` and `default` (or branches) triggers. The most efficient way to implement this without code redundancy is as follows:
```yml theme={null}
image: atlassian/default-image:3
definitions:
steps:
- step: &safedep-vet-pipe
name: "Execute Vet Scan Pipe"
script:
- pipe: safedep/vet-pipe:v1.2.0
pipelines:
branches:
main:
- step: *safedep-vet-pipe
pull-requests:
'**':
- step: *safedep-vet-pipe
```
## Reports
`vet-pipe` supports [Bitbucket Native Code Insights Reports](https://support.atlassian.com/bitbucket-cloud/docs/code-insights/). Each **Pull Request** or **Push** gets a report, and **findings** are attached to their respective files and visible in the Bitbucket UI.
## Inputs
`vet-pipe` accepts the following variables.
### Cloud Sync
Cloud Sync requires a subscription to [SafeDep Cloud](/governance/cloud/overview).
[Cloud Sync](https://docs.safedep.io/governance/cloud/sync#cloud-sync) synchronizes scan data and policy violations with SafeDep Cloud for centralized analysis, query and reporting.
Set the following variables to enable cloud sync:
```yml theme={null}
image: atlassian/default-image:3
pipelines:
default:
- step:
name: Run vet pipe
script:
- pipe: safedep/vet-pipe:v1.2.0
variables:
CLOUD: "true"
CLOUD_KEY: $CLOUD_KEY
CLOUD_TENANT: $CLOUD_TENANT
```
You can generate your `CLOUD_KEY` and `CLOUD_TENANT` values from [https://app.safedep.io](https://app.safedep.io)
To create these:
* Sign Up / Login to [https://app.safedep.io](https://app.safedep.io)
* Create your **Tenant**
* Go to **Settings**
* Go to **API Keys**
* Then create `API Key`
### Policy Customization
Policy customization is optional. SafeDep Pipe comes with default policies.
[Policy as Code](https://docs.safedep.io/reference/policy-as-code#what-is-policy-as-code) treats security policies as configuration files evaluated by tools to make runtime decisions.
To use your own policies, specify them with the `POLICY` variable.
See [Policy as Code](/reference/policy-as-code) for more details.
```yml theme={null}
image: alpine:latest
pipelines:
default:
- step:
name: "Run Vet Scan"
script:
- pipe: safedep/vet-pipe:v1.2.0
variables:
POLICY: "./safedep/policy.yml"
```
When a policy violation occurs, the pipeline fails. To overwrite this, set `SKIP_FILTER_CI_FAIL: "true"` in **variables**, to skip **fail** when a policy violation happens.
### Other Inputs
See the [`vet-pipe` Bitbucket repo](https://bitbucket.org/safedep/vet-pipe/) for more detail about other available inputs.
## Artifact
Each [`vet`](https://github.com/safedep/vet) execution produces a `vet-report.json` file via the `--report-json` flag. To make this file downloadable, set the `artifacts` property in `bitbucket-pipelines.yml`:
```yaml theme={null}
- step:
name: "Run Vet Scan"
script:
- pipe: safedep/vet-pipe:v1.2.0
artifacts:
- vet-report.json
```
This file will be available to download at **Pipelines > Select a Pipeline > Artifacts** in the Bitbucket UI.
## Support
Raise an issue on the [vet-pipe GitHub repo](https://github.com/safedep/vet-bitbucket-pipe/issues) or the [vet-pipe Bitbucket mirror](https://bitbucket.org/safedep/vet-pipe/).
# DefectDojo Integration
Source: https://docs.safedep.io/governance/integrations/defectdojo
Integrate Vet with DefectDojo for centralized vulnerability tracking and management
`vet` integrates with [DefectDojo](https://github.com/DefectDojo/django-DefectDojo) to export vulnerabilities, policy violations, and other findings. Each scan is reported as a new engagement in DefectDojo.
## Prerequisites
Required for running DefectDojo locally
Either local or cloud-hosted DefectDojo installation
Install Vet following the quickstart guide
DefectDojo API key for authentication
If you don't have Vet installed yet, follow the [quickstart guide](/governance/vet/quickstart) to get started.
## Quick Setup with Docker
The steps below use Docker Compose to run DefectDojo locally and scan the [demo-client-python](https://github.com/safedep/demo-client-python) repository as a worked example.
### Setup DefectDojo
Download the DefectDojo repository:
```bash theme={null}
git clone https://github.com/DefectDojo/django-DefectDojo.git --depth 1
cd django-DefectDojo
```
Launch DefectDojo with Docker Compose:
```bash theme={null}
docker compose up -d
```
This will take a while as it builds images and downloads dependencies.
Retrieve the admin password from the logs:
```bash theme={null}
docker compose logs initializer | grep "Admin password:"
```
The initializer container runs migrations and creates initial data, which may take several minutes.
Navigate to `http://localhost:8080` and login with:
* **Username**: `admin`
* **Password**: (from previous step)
### Configure Your Project
Create a new product called `demo-client-python` and note the product ID:
Navigate to `http://localhost:8080/api/key-v2` to generate an API key for Vet integration.
Configure the API key for Vet usage:
```bash theme={null}
export DEFECT_DOJO_APIV2_KEY=
```
## Scanning with Vet
Now you can scan a project and send results to DefectDojo:
```bash theme={null}
vet scan --github https://github.com/safedep/demo-client-python \
--filter-suite /path/to/your/policy-suite.yml \
--report-defect-dojo \
--defect-dojo-host-url http://localhost:8080/ \
--defect-dojo-product-id
```
Each scan creates a new engagement in DefectDojo; policy violations are reported as findings and visible in DefectDojo's dashboard.
Currently, Vet reports only policy violations to DefectDojo. Support for reporting vulnerabilities and malicious package information is planned in [GitHub issue #430](https://github.com/safedep/vet/issues/430).
## Advanced Configuration
### Custom Policy Suites
Example policy suite for DefectDojo integration:
```yaml theme={null}
# defectdojo-policy.yml
name: DefectDojo Security Policy
description: Comprehensive policy for DefectDojo integration
filters:
- name: critical-vulnerabilities
value: |
vulns.critical.size() > 0
- name: high-risk-packages
value: |
vulns.high.size() > 3
- name: license-violations
value: |
!licenses.exists(p, p in ["MIT", "Apache-2.0", "BSD-3-Clause"])
- name: unmaintained-packages
value: |
scorecard.scores.Maintained < 5
```
### CI/CD Integration
```yaml theme={null}
name: Security Scan to DefectDojo
on: [push, pull_request]
jobs:
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run vet security scan
run: |
vet scan -D . \
--filter-suite .github/security-policy.yml \
--report-defect-dojo \
--defect-dojo-host-url ${{ secrets.DEFECT_DOJO_URL }} \
--defect-dojo-product-id ${{ secrets.DEFECT_DOJO_PRODUCT_ID }}
env:
DEFECT_DOJO_APIV2_KEY: ${{ secrets.DEFECT_DOJO_API_KEY }}
```
```yaml theme={null}
security-scan:
image: ghcr.io/safedep/vet:latest
script:
- vet scan -D .
--filter-suite security-policy.yml
--report-defect-dojo
--defect-dojo-host-url $DEFECT_DOJO_URL
--defect-dojo-product-id $DEFECT_DOJO_PRODUCT_ID
variables:
DEFECT_DOJO_APIV2_KEY: $DEFECT_DOJO_API_KEY
```
### Multiple Projects
For organizations with multiple projects, create separate products in DefectDojo:
```bash theme={null}
# Project A
vet scan -D ./project-a \
--report-defect-dojo \
--defect-dojo-product-id 1
# Project B
vet scan -D ./project-b \
--report-defect-dojo \
--defect-dojo-product-id 2
```
## Troubleshooting
If authentication fails:
* Verify the API key is correctly set in the environment
* Check that the API key has sufficient permissions
* Ensure the DefectDojo URL is accessible from your environment
If the product ID is invalid:
* Verify the product exists in DefectDojo
* Check that you have access to the specified product
* Ensure the product ID is numeric, not the product name
If no findings appear in DefectDojo:
* Confirm that policy violations exist in your scan
* Check the Vet scan output for errors
* Verify the DefectDojo integration is properly configured
Learn more about DefectDojo features and configuration
Create effective security policies for DefectDojo integration
Track progress on enhanced DefectDojo integration features
Use the demo repository to test your DefectDojo integration
# GitHub App
Source: https://docs.safedep.io/governance/integrations/github
Set up the SafeDep GitHub App to scan pull requests for supply-chain risk.
The [SafeDep GitHub App](https://github.com/apps/safedep) scans pull requests for supply-chain risk directly in GitHub. It is a hosted service run by SafeDep, so unlike the [GitHub Action](https://github.com/safedep/vet-action) there is nothing to configure or run yourself: it activates immediately after installation.
* Zero-configuration installation with immediate visibility of security findings
* Protects against malicious open source packages, known vulnerabilities, and risky licenses
* Free for public open source repositories. Private (commercial) repositories need a [SafeDep subscription](https://safedep.io/pricing)
* Optionally link the installation to your [SafeDep Cloud](/governance/cloud/overview) tenant for centralized policy and reporting across repositories
## How to Install
1. Navigate to [SafeDep GitHub App](https://github.com/apps/safedep)
2. Click *Install*
3. Follow the instructions to install the app in your GitHub organization or repository
## How to Use
The SafeDep GitHub App automatically scans pull requests for open source dependency changes. Newly introduced or updated dependencies are checked for vulnerabilities and malware.
### Reports
On every pull request, the app scans updated packages and reports on:
* [Malicious / Suspicious](/governance/cloud/malware-analysis)
* [Vulnerabilities](#appendix)
* [Risky Licenses](#appendix)
### Active Protection
When any report fails, the GitHub App **Check** fails and blocks the branch from merging.
The check fails if any *Verified Malicious Package*, *Vulnerability*, or *Risky License* is found.
## Appendix
### Vulnerabilities
* Checks for `CRITICAL` or `HIGH` severity vulnerabilities.
* Uses [OSV](https://osv.dev) as the vulnerability database.
### Risky Licenses
* The app currently classifies the following licenses as **Risky**:
* `GPL-2.0`
* `GPL-2.0-only`
* `GPL-2.0-or-later`
* `GPL-3.0`
* `GPL-3.0-only`
* `GPL-3.0-or-later`
* `AGPL-3.0`
* `AGPL-3.0-only`
* `AGPL-3.0-or-later`
### Supported Lockfiles
Supported lockfiles and ecosystems:
1. **NPM**
* `package-lock.json`
* `pnpm-lock.yaml`
* `yarn.lock`
2. **GoLang**
* `go.mod`
3. **PyPI**
* `requirements.txt`
* `uv.lock`
* `poetry.lock`
* `Pipfile.lock`
4. **RubyGems**
* `Gemfile.lock`
5. **Cargo (Rust)**
* `Cargo.lock`
6. **Packagist (PHP)**
* `composer.lock`
7. **Maven (Java)**
* `pom.xml`
* `gradle.lockfile`
Surface Vet findings in GitHub code scanning via SARIF.
Wire Vet into other CI/CD platforms.
Scan a repository from the CLI.
Define the policy the app enforces.
# GitHub Code Scanning
Source: https://docs.safedep.io/governance/integrations/github-code-scanning
Integrate Vet with GitHub Actions and Code Scanning for automated security alerts.
GitHub supports [uploading SARIF](https://docs.github.com/en/code-security/code-scanning/integrating-with-code-scanning/sarif-support-for-code-scanning) reports for repository and organization-wide visibility of security events. `vet` exports policy violation reports as SARIF for upload to GitHub Code Scanning.
## Quick Setup with GitHub Action
`vet` has a dedicated GitHub Action, which is the recommended approach for most teams.
### Basic Configuration
Create `.github/workflows/vet.yml` in your repository:
```yaml theme={null}
name: OSS Security Scan
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
permissions:
contents: read
security-events: write
pull-requests: write
jobs:
vet-scan:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Run vet
id: vet
uses: safedep/vet-action@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload SARIF
if: steps.vet.outputs.report != ''
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: ${{ steps.vet.outputs.report }}
category: vet
```
SARIF reports work when you enable GitHub Code Scanning in your repository. [Learn more](https://docs.github.com/en/code-security/code-scanning/enabling-code-scanning)
### Advanced Configuration
To use a custom policy:
```yaml theme={null}
- name: Run vet with custom policy
id: vet
uses: safedep/vet-action@v1
with:
policy: '.github/vet/policy.yml'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```
## Manual SARIF Generation
To generate a SARIF report using the `vet` CLI:
```bash theme={null}
vet scan -D /path/to/project --report-sarif /path/to/report.sarif
```
By default the SARIF report includes vulnerabilities and malware findings. To also report policy violations, pass a policy with `--policy` or `--policy-suite` during the scan.
## Viewing Results
Once uploaded, policy violations appear in the GitHub Security tab, giving a centralized view across repositories.
## Pull Request Integration
The GitHub Action adds a comment to pull requests when security issues are found:
## Best Practices
For production workflows, pin third-party GitHub Actions to a full commit SHA rather than a mutable tag. This protects against tag-moving attacks, where a compromised upstream action could inject malicious code.
```yaml theme={null}
# Instead of:
uses: actions/checkout@v4
# Pin to a specific commit SHA:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
```
The [actions/checkout](https://github.com/actions/checkout/releases) and [github/codeql-action](https://github.com/github/codeql-action/releases) release pages list the commit SHA for each release. `safedep/vet-action` uses [semantic versioning tags](https://github.com/safedep/vet-action/releases) maintained directly by SafeDep.
## Troubleshooting
Ensure the `security-events: write` permission is set in your workflow file and that Code Scanning is enabled for your repository.
Check that your policy configuration is correct and that violations actually exist. Use `--report-json` locally to debug.
View the complete documentation and examples
See a complete GitHub Actions workflow example
# GitLab Dependency Scanning
Source: https://docs.safedep.io/governance/integrations/gitlab
Native GitLab integration for dependency security scanning with Vet.
`vet` integrates with GitLab Dependency Scanning to detect malicious and vulnerable dependencies on every push and merge request.
## Prerequisites
Active GitLab account with access to your project
GitLab Group with Ultimate Plan for security scanning features
Security scanning features are only available to GitLab Ultimate plans. Free users can still use the **Vet CI component** to find vulnerabilities and check policy violations. See the [demo video](https://www.youtube.com/watch?v=QJfSRc4p-z4) for free usage.
## Quick Setup
### 1. Enable CI on Your Project
Create a `.gitlab-ci.yml` file in the root of your project:
```bash theme={null}
touch .gitlab-ci.yml
```
### 2. Add Vet as a CI Component
Add the following to your `.gitlab-ci.yml` file:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
```
Commit and push to trigger your first scan.
## Viewing Results
Once configured, the `vet` job appears in your pipeline with a security tab:
View vulnerabilities and malware findings in the security tab:
Access detailed reports at **Project > Secure > Vulnerability Report**:
## Configuration Options
### Cloud Sync Integration
Enable [SafeDep Cloud](/governance/cloud/quickstart) synchronization:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
cloud: true
cloud-key: $SAFEDEP_CLOUD_API_KEY
cloud-tenant: $SAFEDEP_CLOUD_TENANT_DOMAIN
```
Store `SAFEDEP_CLOUD_API_KEY` and `SAFEDEP_CLOUD_TENANT_DOMAIN` as GitLab CI/CD variables for security.
### Policy Configuration
Use custom policies for advanced filtering:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
policy: '.gitlab/vet/policy.yml'
```
The CI job fails if any policy violations are found. Check the logs to identify which policies were violated.
### Version Control
Specify which version of `vet` to use:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
version: v1.9.0
```
These are two independent versions: the component tag (`@v1.5.1`) pins the GitLab CI component, and the `version` input pins the `vet` binary the component downloads and runs.
### Trusted Registries
Configure trusted registry URLs for package verification:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
trusted-registries:
- https://registry.npmjs.org
- https://pypi.org
```
### Artifact Access
Control who can access scan artifacts:
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
artifact-access: 'developer' # Options: all, developer, none
```
Only use `all` if you are comfortable exposing security scan results publicly.
## Advanced Examples
### Multi-Stage Pipeline
```yaml theme={null}
stages:
- security
- build
- deploy
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
inputs:
stage: security
policy: '.gitlab/security-policy.yml'
cloud: true
cloud-key: $SAFEDEP_CLOUD_API_KEY
cloud-tenant: $SAFEDEP_CLOUD_TENANT_DOMAIN
build:
stage: build
script:
- echo "Building application..."
needs: ["vet"]
```
### Conditional Scanning
```yaml theme={null}
include:
- component: gitlab.com/safedep/ci-components/vet/scan@v1.5.1
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
```
## Troubleshooting
Ensure your GitLab plan includes security scanning features. Ultimate plan is required for the security dashboard.
Verify you're using the correct component path and version. Check the [GitLab Component Catalog](https://gitlab.com/explore/catalog/safedep/ci-components/vet) for the latest version.
This is expected behavior when violations are found. Review the job logs to see which policies were violated, then fix the issues or adjust your policy configuration.
View complete configuration options and examples
Report bugs or request improvements
# Platform Integrations
Source: https://docs.safedep.io/governance/integrations/overview
Integrate Vet into your CI/CD and developer platforms for automated supply-chain scanning.
Run SafeDep where your code already lives. Vet plugs into common CI/CD and developer platforms to scan dependencies automatically on every change.
Install the SafeDep GitHub App to scan pull requests.
Run Vet in GitHub Actions and surface findings in code scanning.
Scan dependencies in GitLab CI.
Scan with Bitbucket Pipes.
Send findings to DefectDojo for tracking.
Audit Terraform providers for supply-chain risk.
# Visibility & Governance
Source: https://docs.safedep.io/governance/overview
Scan repositories, SBOMs, and CI/CD for supply-chain risk, and govern policy and visibility across your org.
See the open-source risk in everything you depend on, then govern it across your organization. Scan repositories and pipelines, generate bills of materials, and manage policy and fleet-wide visibility from SafeDep Cloud.
**Vet** finds malicious, vulnerable, and risky dependencies in code, lockfiles, and SBOMs.
**xBom** inventories dependencies plus AI, SaaS, and crypto usage.
Wire SafeDep into GitHub, GitLab, Bitbucket, and more.
**SafeDep Cloud** centralizes policy, endpoints, and visibility.
# Shadow AI in Code
Source: https://docs.safedep.io/governance/shadow-ai-detection
Detect AI and LLM SDK usage in your codebase using static code analysis and generate an SBOM with AI component evidence
Shadow AI is the unauthorized use of AI services and SDKs within a codebase. Developers may integrate OpenAI, Anthropic, LangChain, or other AI services without security review, creating blind spots in your software supply chain.
This guide walks you through detecting Shadow AI using `vet`'s static code analysis, querying the results, and generating a CycloneDX SBOM enriched with AI component evidence.
## Prerequisites
* `vet` [installed](/governance/vet/quickstart)
* Access to the source code you want to analyze
## Workflow
Analyze your source code and build a code analysis database. Use `--app` to specify your application directories and `--import-dir` for vendored or third-party dependencies.
```bash theme={null}
vet code scan --db code.db \
--app ./src \
--import-dir ./vendor
```
This parses source files, builds call graphs, and matches function calls against embedded signature patterns. Results are stored in a SQLite database.
Use `--exclude` to skip test files or generated code:
```bash theme={null}
vet code scan --db code.db \
--app ./src \
--exclude ".*test.*" --exclude ".*__pycache__.*"
```
Inspect what AI and LLM SDKs were detected using the `--tag ai` filter:
```bash theme={null}
vet code query --db code.db --tag ai
```
This lists all signature matches tagged as AI, showing the file path, line number, and matched call pattern. You can also combine tags for a broader view:
```bash theme={null}
vet code query --db code.db --tag ai --tag ml
```
To see more results or filter by language:
```bash theme={null}
vet code query --db code.db --tag ai --language python --limit 200
```
Run `vet scan` with the code analysis database to produce a CycloneDX SBOM enriched with AI component evidence:
```bash theme={null}
vet scan -D ./src --code code.db --report-cdx sbom.json
```
The generated SBOM includes AI components as evidence-backed entries, making Shadow AI usage visible to downstream security and compliance tooling.
## Understanding the Output
### Package-level AI usage
When an AI SDK is both declared as a dependency and used in code, it appears with `source-code-analysis` evidence:
```json theme={null}
{
"bom-ref": "pkg:pypi/openai@1.0.0",
"evidence": {
"identity": [
{ "methods": [{ "technique": "source-code-analysis", "confidence": 1.0 }] }
],
"occurrences": [
{ "location": "src/ai.py", "line": 42, "additionalContext": "openai.OpenAI" }
]
},
"properties": [
{ "name": "ai", "value": "true" }
]
}
```
### Application-level AI usage
AI capabilities detected in first-party code (e.g., direct standard library HTTP calls to AI endpoints) appear as standalone xBOM components:
```json theme={null}
{
"bom-ref": "xbom:anthropic.ai.claude",
"type": "library",
"name": "Anthropic Claude",
"publisher": "Anthropic",
"evidence": {
"occurrences": [
{ "location": "src/chatbot.py", "line": 15, "additionalContext": "anthropic.Anthropic" }
]
}
}
```
The `ai` property tag lets you filter AI components from the SBOM programmatically.
## What Gets Detected
`vet` detects AI and LLM usage across **Go**, **Python**, and **JavaScript/TypeScript**:
| Service | Examples |
| ------------- | ------------------------- |
| **OpenAI** | OpenAI client SDK |
| **Anthropic** | Claude, Bedrock, VertexAI |
| **LangChain** | LangChain framework |
| **CrewAI** | CrewAI agents |
| **Azure AI** | Azure AI services |
Detection signatures are community-maintained and embedded into `vet` at build time. Run `vet code validate` to verify all signatures are well-formed.
Learn about extended Bill of Materials and signature-based detection
Deeper dive into Vet's static code analysis capabilities
Generate and work with CycloneDX SBOMs
Enforce policies against detected components in CI/CD
# Terraform Supply Chain Audit
Source: https://docs.safedep.io/governance/terraform-audit
Audit Terraform provider inventory for supply chain risks using SafeDep Cloud
To follow this guide you need a SafeDep Cloud API Key and Tenant Identifier. See [Cloud Quickstart](/governance/cloud/quickstart) on how to onboard to SafeDep Cloud and get an API key.
This guide covers how to discover [Terraform providers](https://developer.hashicorp.com/terraform/language/providers) used in a Terraform project, synchronize the inventory (BOM) with SafeDep Cloud, and query for unofficial providers that may pose supply chain risks.
## Prerequisites
* `vet` installed (see [quickstart](/governance/vet/quickstart) for installation options)
* SafeDep Cloud account with API key and tenant identifier
* Terraform project with initialized providers (`.terraform.lock.hcl` must exist)
## Scan and Discover Terraform Providers
Run `vet` on your Terraform project to discover providers. The project must be initialized so that `.terraform.lock.hcl` is present in the project directory.
`--report-sync` uploads the provider inventory to SafeDep Cloud, so set your credentials first:
```bash theme={null}
export SAFEDEP_API_KEY=your-api-key
export SAFEDEP_TENANT_ID=your-tenant-domain
```
Then run the scan:
```bash theme={null}
vet scan -D /path/to/terraform-code \
--report-sync \
--report-sync-project gh/test/infra1 \
--report-sync-project-version main
```
### What This Command Does
Scans Terraform configuration files and lock files to identify all providers
Gathers information about provider versions, sources, and tiers (official, partner, community)
Uploads the provider inventory to SafeDep Cloud for centralized analysis
Associates findings with specific projects and versions for tracking over time
## Query Provider Inventory
Use SafeDep Cloud SQL queries to analyze your Terraform provider inventory. See [SafeDep Cloud Quickstart](/governance/cloud/quickstart) for more details on the query interface.
### Find Unofficial Terraform Providers
Query for providers that are not officially maintained by HashiCorp:
Providers are surfaced as packages in your synced inventory, joined to the global Terraform provider registry. Anchor the query on an indexed column (here `projects.origin_source`) and join out to `terraform_providers`:
```bash theme={null}
safedep query exec --sql "
SELECT projects.name, packages.name, terraform_providers.tier
FROM projects
JOIN project_versions ON project_versions.project_id = projects.id
JOIN boms ON boms.pv_id = project_versions.id
JOIN packages ON packages.bom_id = boms.id
JOIN terraform_providers ON terraform_providers.provider_name = packages.name
WHERE projects.origin_source = 'SOURCE_GITHUB'
AND terraform_providers.tier != 'official'
ORDER BY packages.name"
```
### Example Response
```
projects.name packages.name terraform_providers.tier
gh/test/infra1 registry.terraform.io/digitalocean/do community
gh/test/infra1 registry.terraform.io/hetznercloud/hcloud community
```
## Advanced Queries
Break providers down by tier across your GitHub projects:
```bash theme={null}
safedep query exec --sql "
SELECT terraform_providers.tier, COUNT(DISTINCT packages.name) AS provider_count
FROM projects
JOIN project_versions ON project_versions.project_id = projects.id
JOIN boms ON boms.pv_id = project_versions.id
JOIN packages ON packages.bom_id = boms.id
JOIN terraform_providers ON terraform_providers.provider_name = packages.name
WHERE projects.origin_source = 'SOURCE_GITHUB'
GROUP BY terraform_providers.tier
ORDER BY provider_count DESC"
```
For the full query language, schema, join model, and more worked examples, see the [SafeDep Cloud SQL guide](/reference/sql-query).
## CI/CD Integration
### GitHub Actions
Integrate Terraform provider auditing into your CI/CD pipeline:
```yaml theme={null}
name: Terraform Security Audit
on:
push:
paths:
- '**/*.tf'
- '.terraform.lock.hcl'
pull_request:
paths:
- '**/*.tf'
- '.terraform.lock.hcl'
jobs:
terraform-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
- name: Terraform Init
run: terraform init
- name: Audit Terraform Providers
run: |
vet scan -D . \
--report-sync \
--report-sync-project ${{ github.repository }} \
--report-sync-project-version ${{ github.ref_name }}
env:
SAFEDEP_API_KEY: ${{ secrets.SAFEDEP_CLOUD_API_KEY }}
SAFEDEP_TENANT_ID: ${{ secrets.SAFEDEP_CLOUD_TENANT_DOMAIN }}
- name: Check for Unofficial Providers
run: |
safedep query exec -o json --sql "
SELECT packages.name, terraform_providers.tier
FROM projects
JOIN project_versions ON project_versions.project_id = projects.id
JOIN boms ON boms.pv_id = project_versions.id
JOIN packages ON packages.bom_id = boms.id
JOIN terraform_providers ON terraform_providers.provider_name = packages.name
WHERE projects.name = '${{ github.repository }}'
AND terraform_providers.tier != 'official'
" > unofficial-providers.json
if [ "$(jq '.count' unofficial-providers.json)" -gt 0 ]; then
echo "⚠️ Unofficial providers detected:"
jq -r '.rows[] | .["packages.name"]' unofficial-providers.json
echo "Please review these providers for security compliance."
fi
env:
SAFEDEP_API_KEY: ${{ secrets.SAFEDEP_CLOUD_API_KEY }}
SAFEDEP_TENANT_ID: ${{ secrets.SAFEDEP_CLOUD_TENANT_DOMAIN }}
```
### GitLab CI
```yaml theme={null}
stages:
- init
- audit
terraform-init:
stage: init
image: hashicorp/terraform:latest
script:
- terraform init
artifacts:
paths:
- .terraform.lock.hcl
expire_in: 1 hour
terraform-audit:
stage: audit
image: ghcr.io/safedep/vet:latest
script:
- vet scan -D . --report-sync --report-sync-project $CI_PROJECT_PATH --report-sync-project-version $CI_COMMIT_REF_NAME
dependencies:
- terraform-init
variables:
SAFEDEP_API_KEY: $SAFEDEP_CLOUD_API_KEY
SAFEDEP_TENANT_ID: $SAFEDEP_CLOUD_TENANT_DOMAIN
```
## Policy Enforcement
Terraform provider tier (official, partner, community) lives in SafeDep Cloud's query layer, not in Vet's local `--filter-suite` engine, which evaluates package-level signals (vulnerabilities, licenses, OpenSSF Scorecard) and has no notion of provider tiers. To enforce provider policies, query the synced inventory and fail the build on disallowed providers.
The **Check for Unofficial Providers** step in the GitHub Actions example above demonstrates this pattern: it queries `terraform_providers.tier` and exits non-zero when any non-official provider is present. Adapt that query to encode your approved-provider rules.
## Schema Exploration
List all queryable columns for Terraform providers:
```bash theme={null}
safedep query schema show terraform_providers
```
Available fields include:
* `terraform_providers.tier` - Provider tier (official, partner, community)
* `terraform_providers.source` - Provider source URL
* `terraform_providers.namespace` - Provider namespace
* `packages.name` - Full provider name
* `packages.version` - Provider version
## Troubleshooting
If no providers are detected:
* Ensure `.terraform.lock.hcl` exists (run `terraform init`)
* Verify Terraform configuration files are present
* Check that Vet supports your Terraform version
If synchronization to SafeDep Cloud fails:
* Verify API key and tenant ID are correct
* Check network connectivity to SafeDep Cloud
* Ensure project name doesn't contain special characters
Browse official and community Terraform providers
Learn more about querying your infrastructure inventory
Learn about Terraform in the software supply chain
Complete Vet CLI documentation and examples
# Generate an AI BOM
Source: https://docs.safedep.io/governance/vet/ai-bom
Build an AI Bill of Materials from source code with vet's xBOM engine: AI SDK detections in CycloneDX with file and line evidence.
`vet` finds the AI libraries, SDKs, and agent frameworks your application actually calls. It scans source code, matches call sites against signatures for known AI SDKs, and writes each match into its [CycloneDX](/concepts/sbom) output as an extended Bill of Materials (xBOM). The AI components, each marked with an `ai` property and file and line evidence, are your AI BOM.
Because the inventory comes from call sites, it shows what the code calls, not just what the manifests declare.
Coverage tracks the AI ecosystem in current use:
* **Model providers:** OpenAI, Anthropic, Google Gemini, xAI Grok, Mistral, Cohere, Groq, Ollama, AWS Bedrock, Hugging Face, and more
* **Frameworks and agent SDKs:** LangChain, LangGraph, LlamaIndex, CrewAI, Vercel AI SDK, OpenAI Agents SDK, Claude Agent SDK, Pydantic AI, Spring AI, Semantic Kernel, and the Model Context Protocol (MCP) SDKs
* **Languages:** Python, JavaScript, TypeScript, Java, and Go. Each SDK is covered in the languages it supports.
Browse the [full signature set](https://github.com/safedep/vet/tree/main/signatures). AI is one xBOM category: the same scan also detects cryptographic, cloud, network, filesystem, and process usage (see the [vet xBOM documentation](https://github.com/safedep/vet/blob/main/docs/xbom.md)).
## Prerequisites
* `vet` [installed](/governance/vet/quickstart)
* Access to the source code you want to analyze
## Generate the AI BOM
The first pass parses your source files, matches them against the embedded signatures, and stores the results in a local SQLite database. Point `--app` at your own code:
```bash theme={null}
vet code scan --db code.db --app ./src
```
Skip test and generated code with `--exclude` (a regular expression), and restrict languages with `--lang` when you want a faster scan:
```bash theme={null}
vet code scan --db code.db \
--app ./src \
--lang python \
--exclude ".*test.*" --exclude ".*__pycache__.*"
```
The second pass runs a normal `vet` scan, enriches it with the recorded detections, and writes the BOM:
```bash theme={null}
vet scan -D ./src --code code.db --report-cdx xbom.json
```
`xbom.json` is a standard CycloneDX document that carries the code-analysis detections alongside the declared packages. Every AI component in it has the `ai` property, so downstream tooling can filter on it.
You can review what `vet` found without generating a BOM. Query the database for matches tagged `ai`:
```bash theme={null}
vet code query --db code.db --tag ai
```
Narrow the results by language, vendor, or file path:
```bash theme={null}
vet code query --db code.db --tag ai --language python --limit 200
vet code query --db code.db --vendor OpenAI
vet code query --db code.db --file src/agents/
```
## Read the AI BOM
AI SDK calls detected in your code appear as standalone components with an `xbom:` bom-ref. The signature's vendor becomes the publisher, the `ai` property marks the component, and each occurrence records where the call sits in your source. This component records an Anthropic client constructed on line 3 of `src/app.py`:
```json theme={null}
{
"bom-ref": "xbom:anthropic.client",
"type": "library",
"name": "Anthropic API - AI client",
"publisher": "Anthropic",
"evidence": {
"occurrences": [
{
"location": "src/app.py",
"line": 3,
"additionalContext": "anthropic//Anthropic"
}
]
},
"properties": [
{ "name": "ai", "value": "true" }
]
}
```
`additionalContext` shows the matched call in the analyzer's namespace notation, with `//` separating the module path from the symbol. Detection follows calls, so an SDK that is imported but never called produces no component.
If you scan vendored dependency source through `--import-dir`, `vet` attempts to attach those detections to the corresponding declared package instead of a standalone component. More AI SDKs and libraries are added to `vet` xBOM coverage over time.
### Filter the AI components
The `ai` property makes the AI BOM easy to extract from the full document. With `jq`:
```bash theme={null}
jq '.components[] | select(.properties[]? | .name == "ai" and .value == "true") | .name' xbom.json
```
Providers reached through the OpenAI-compatible mode of the `openai` SDK (DeepSeek and Moonshot document this as their official path) appear as OpenAI usage, because the code calls the `openai` SDK.
## Run it in CI
Generate the BOM on each build and keep it as an artifact:
```yaml theme={null}
- name: Build code analysis database
run: vet code scan --app ./src --db code.db
- name: Generate xBOM with AI components
run: vet scan -D ./src --code code.db --report-cdx xbom.json
```
Generate a standard CycloneDX SBOM with vet.
How CycloneDX BOMs and xBOMs relate.
The static analysis engine behind the xBOM.
# Code Analysis
Source: https://docs.safedep.io/governance/vet/code-analysis
Analyze code and dependency usage patterns with Vet's code analysis features
**EXPERIMENTAL**: This feature is experimental and may introduce breaking changes.
`vet` uses the [code](https://github.com/safedep/code/) analysis framework built on [tree-sitter](https://tree-sitter.github.io/tree-sitter/) parsers. The framework supports multiple languages and source repositories (local and remote), and writes findings to a SQLite database that `vet scan` uses to enrich manifest analysis.
## Build a Code Analysis Database
Build a SQLite database from your source code. The database is a prerequisite for code analysis features in `vet scan`.
```bash theme={null}
vet code scan --app /path/to/app \
--db /tmp/code.db \
--lang python
```
This command analyzes application code recursively in the specified directory and creates a SQLite database with the findings. Omit `--lang` to scan all supported languages.
### Supported Languages
The code analysis framework supports these languages through tree-sitter parsers:
* Python
* JavaScript/TypeScript
* Java
* Go
* And more...
## Scan with Dependency Usage Analysis
Pass the database to `vet scan` via the `--code` flag. Dependency usage analysis is enabled by default when a code database is provided.
```bash theme={null}
vet scan -D /path/to/code --code /tmp/code.db
```
With a code database, `vet scan` adds:
1. **Manifest Analysis**: scans package manifests in the specified directory
2. **Usage Enrichment**: enriches packages with dependency usage data from the database
3. **Evidence-Based Results**: shows a scan summary with usage evidence and `used-in-code` tags for packages confirmed as used in code
## Practical Example
Full workflow for a Python project:
Build the code analysis database for your Python project:
```bash theme={null}
vet code scan --app ./src \
--db ./analysis/code.db \
--lang python
```
Run `vet scan` with the code database:
```bash theme={null}
vet scan -D . \
--code ./analysis/code.db \
--report-json results.json
```
Check the scan results for:
* Dependencies actually used in code vs. declared
* Unused dependencies that could be removed
* Usage patterns and import analysis
## Advanced Usage
### Multi-language Projects
For projects with multiple languages, omit the `--lang` flag:
```bash theme={null}
vet code scan --app ./src --db ./analysis/polyglot.db
```
### Custom Database Locations
Organize databases by project or environment:
```bash theme={null}
# Development environment
vet code scan --app ./src --db ./analysis/dev-code.db
# Production analysis
vet code scan --app ./dist --db ./analysis/prod-code.db
```
### Integration with CI/CD
```yaml theme={null}
# GitHub Actions example
- name: Build Code Analysis DB
run: vet code scan --app ./src --db ./code-analysis.db
- name: Enhanced Security Scan
run: vet scan -D . --code ./code-analysis.db --report-sarif security.sarif
```
## Limitations
This feature is experimental and may have breaking changes. Test before using in production.
Code analysis adds processing time to scans. Weigh the accuracy benefit against the speed cost for your use case.
Code analysis databases can grow large for extensive codebases. Monitor disk usage and remove old databases periodically.
Learn more about the underlying code analysis framework
Understand the parsing technology behind code analysis
See how to identify dependency usage in your code
Access the main Vet documentation and examples
# Dependency Inventory
Source: https://docs.safedep.io/governance/vet/dependency-inventory
Generate accurate dependency inventories using package managers and SBOM tools
Package managers such as Maven, Gradle, and npm have the most accurate view of library dependencies. They resolve exact versions and can generate an SBOM for `vet` to scan with higher fidelity. This guide uses the [CycloneDX Gradle plugin](https://github.com/CycloneDX/cyclonedx-gradle-plugin) to generate an SBOM and scan it with `vet`.
## Gradle Integration
The CycloneDX Gradle plugin generates SBOMs that `vet` can analyze for security issues.
### Plugin Configuration
Add the CycloneDX plugin to your `build.gradle` file:
```groovy theme={null}
plugins {
id 'org.cyclonedx.bom' version '1.10.0'
}
cyclonedxBom {
includeConfigs = ["runtimeClasspath"]
skipConfigs = ["compileClasspath", "testCompileClasspath"]
skipProjects = [rootProject.name, "yourTestSubProject"]
projectType = "application"
schemaVersion = "1.6"
destination = file("build/reports")
outputName = "bom"
outputFormat = "json"
includeBomSerialNumber = false
includeLicenseText = false
includeMetadataResolution = true
componentVersion = "2.0.0"
componentName = "my-component"
}
```
### Configuration Options
**includeConfigs**: Which dependency configurations to include
```groovy theme={null}
includeConfigs = [
"runtimeClasspath", // Runtime dependencies
"implementationClasspath", // Implementation dependencies
"compileClasspath" // Compile-time dependencies
]
```
**skipConfigs** and **skipProjects**: Exclude unnecessary components
```groovy theme={null}
skipConfigs = ["testCompileClasspath", "testRuntimeClasspath"]
skipProjects = ["test-utils", "benchmarks"]
```
**destination** and **outputName**: Control where SBOMs are generated
```groovy theme={null}
destination = file("security/sboms")
outputName = "${project.name}-${project.version}-sbom"
outputFormat = "json" // or "xml"
```
### SBOM Generation
Generate SBOM artifacts with a clean build:
```bash theme={null}
gradle clean build cyclonedxBom
```
After a successful build, SBOM artifacts are stored in the `build/reports` directory.
## Multi-Project Configuration
For multi-module projects, configure the plugin in each module or use a shared configuration in the root `build.gradle`:
```groovy theme={null}
subprojects {
apply plugin: 'org.cyclonedx.bom'
cyclonedxBom {
includeConfigs = ["runtimeClasspath"]
projectType = "library"
destination = file("${rootProject.buildDir}/reports/sboms")
outputName = "${project.name}-bom"
}
}
```
## Maven Integration
For Maven projects, use the CycloneDX Maven plugin:
```xml theme={null}
org.cyclonedx
cyclonedx-maven-plugin
2.8.0
application
1.6
false
true
bom
json
package
makeAggregateBom
```
Generate the SBOM:
```bash theme={null}
mvn clean package cyclonedx:makeAggregateBom
```
## Scanning SBOMs with Vet
Once you have generated SBOM files, scan them with `vet`:
### CycloneDX Format
```bash theme={null}
vet scan --lockfiles build/reports/bom.json \
--lockfile-as bom-cyclonedx \
--report-markdown=report.md
```
## npm/Node.js Integration
For Node.js projects, use the CycloneDX npm plugin:
```bash theme={null}
# Install globally
npm install -g @cyclonedx/cyclonedx-npm
# Generate SBOM
cyclonedx-npm --output-file sbom.json
# Scan with vet
vet scan --lockfiles sbom.json --lockfile-as bom-cyclonedx
```
## Python Integration
For Python projects, use cyclonedx-python:
```bash theme={null}
# Install
pip install cyclonedx-bom
# Generate SBOM (subcommand depends on project type: environment, requirements, poetry, pipenv)
cyclonedx-py environment -o sbom.json
# Scan with vet
vet scan --lockfiles sbom.json --lockfile-as bom-cyclonedx
```
## CI/CD Integration
```yaml theme={null}
name: Dependency Inventory Scan
on: [push, pull_request]
jobs:
inventory-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Generate SBOM
run: ./gradlew cyclonedxBom
- name: Scan SBOM with vet
run: |
docker run --rm -v "$PWD:/app" ghcr.io/safedep/vet:latest \
scan --lockfiles /app/build/reports/bom.json \
--lockfile-as bom-cyclonedx
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: build/reports/bom.json
```
```yaml theme={null}
stages:
- build
- security
generate-sbom:
stage: build
script:
- ./gradlew clean build cyclonedxBom
artifacts:
paths:
- build/reports/bom.json
expire_in: 1 hour
security-scan:
stage: security
image: ghcr.io/safedep/vet:latest
script:
- vet scan --lockfiles build/reports/bom.json --lockfile-as bom-cyclonedx --report-json security-report.json
dependencies:
- generate-sbom
artifacts:
reports:
security: security-report.json
```
## Configuration Notes
Control which dependencies appear in the SBOM by setting `includeConfigs` and `skipConfigs` in your `build.gradle` (shown above). For example, list only `runtimeClasspath` for a production SBOM, or add `testRuntimeClasspath` to include test dependencies.
Store SBOMs alongside releases for compliance and audit:
```bash theme={null}
# Tag SBOMs with version information
cp build/reports/bom.json "release-artifacts/sbom-v${VERSION}.json"
```
Complete documentation for the Gradle plugin
Maven plugin documentation and examples
Learn more about generating SBOMs with Vet
See all supported package managers in Vet
# Dependency Usage
Source: https://docs.safedep.io/governance/vet/dependency-usage
Identify which dependencies are actually used in your code using static code analysis
`vet` can identify which dependencies your code actually uses via static code analysis. When triaging vulnerabilities, this lets you focus on packages that are imported and deprioritize those that are only declared.
**EXPERIMENTAL**: This feature is experimental and may introduce breaking changes.
This page covers the dependency-usage workflow. For the underlying [Code Analysis](/governance/vet/code-analysis) feature, including supported languages and options, see that page.
## Demo
## Quick Start
### Step 1: Create Code Analysis Database
Build a code analysis database for your source code:
```bash theme={null}
vet code scan --app src --db /tmp/dump/vet-test.db
```
This analyzes code in the `src` directory, extracts import statements and usage patterns, and stores the results in a SQLite database.
### Step 2: Scan with Usage Enrichment
Run a Vet scan enriched with the dependency usage database:
```bash theme={null}
vet scan --code /tmp/dump/vet-test.db
```
Results now include:
* **Usage Evidence**: which dependencies are actually imported and used
* **Used-in-Code Tags**: markers on packages confirmed as used in code
* **Prioritized Results**: packages with real usage are highlighted
## Advanced Usage Patterns
### Language-Specific Analysis
To target a specific language:
```bash theme={null}
# Python projects
vet code scan --app src --db python-analysis.db --lang python
# JavaScript/TypeScript projects
vet code scan --app src --db js-analysis.db --lang javascript
# Multi-language projects
vet code scan --app src --db full-analysis.db # Auto-detect all languages
```
## Understanding the Results
### Usage Evidence Types
The code analysis records several types of evidence:
```python theme={null}
# Python example
import requests
from flask import Flask
```
These direct imports are tracked as usage evidence.
```javascript theme={null}
// JavaScript example
const axios = require('axios');
axios.get('https://api.example.com');
```
Actual usage of imported modules is recorded.
```java theme={null}
// Java example
import org.apache.commons.lang3.StringUtils;
String result = StringUtils.capitalize(input);
```
References to specific classes and methods are tracked.
### Tags and Annotations
Dependencies found in your source code are marked with the `used-in-code` tag, so you can prioritize them over packages that are only declared in a manifest.
## Scope Separation
Create separate databases for different scopes:
```bash theme={null}
# Production code only
vet code scan --app src/main --db prod-analysis.db
# Include test code
vet code scan --app src --db full-analysis.db
```
Learn more about Vet's code analysis capabilities
Create policies that leverage usage information
Access complete documentation and examples
Learn about the parsing technology behind code analysis
# Vet
Source: https://docs.safedep.io/governance/vet/overview
Vet is a free, open-source SCA scanner that finds malicious, vulnerable, and risky open-source dependencies in code and CI/CD.
[Vet](https://github.com/safedep/vet) is a free, open-source software composition analysis (SCA) scanner. It checks your open-source dependencies for malicious packages, known vulnerabilities, license issues, and weak project health, then lets you enforce policy on what it finds. Run it locally, in CI/CD, or against a whole repository.
## What Vet does
Replace manual dependency reviews with policy-driven analysis.
Define and version security policy with [CEL](/concepts/cel).
Run in any CI tool or your local workflow.
Built on OSV, OpenSSF Scorecard, deps.dev, and SafeDep malware intelligence.
## Get started
Scan your first project in minutes.
Generate an accurate dependency inventory.
See how dependencies are actually used.
Prioritize findings by which dependencies your code actually uses.
Query results and enforce policy with CEL.
# Vet Quickstart
Source: https://docs.safedep.io/governance/vet/quickstart
Get started with Vet in under 5 minutes
For SafeDep Cloud, refer to [Cloud Quickstart](/governance/cloud/quickstart)
Use [SafeDep Vet](https://github.com/safedep/vet) to detect security risks and apply policy-based controls that block vulnerable or malicious open source dependencies.
## Installation
```bash theme={null}
brew tap safedep/tap
brew install safedep/tap/vet
```
```bash theme={null}
docker run --rm -it ghcr.io/safedep/vet:latest version
```
Download a pre-built binary suitable for your OS at [GitHub Releases](https://github.com/safedep/vet/releases)
For additional installation options, see [Vet's README](https://github.com/safedep/vet).
## Running Your First Scan
Scan a source code repository, auto-discovering well-known manifest files:
```bash theme={null}
vet scan -D /path/to/dir
```
### Other scanning options
```bash theme={null}
vet scan -M package-lock.json
```
```bash theme={null}
vet scan --purl pkg:npm/express@4.18.2
```
```bash theme={null}
vet scan -M /path/to/my-app.jar
```
```bash theme={null}
vet scan --image ghcr.io/safedep/vet:latest
```
For more scanning options, see [Vet's README](https://github.com/safedep/vet).
## Policy as Code
`vet` supports a [CEL](https://cel.dev/)-based policy language for identifying risks. Scan and fail on critical or high vulnerabilities:
```bash theme={null}
vet scan -D /path/to/dir \
--filter '(vulns.critical.size() > 0) || (vulns.high.size() > 0)' \
--filter-fail
```
### Advanced Policy Configuration
Combine multiple CEL queries into a policy suite ([example](https://github.com/safedep/vet/blob/main/samples/filter-suites/fs-generic.yml)) and audit your application:
```bash theme={null}
vet scan -D /path/to/dir \
--filter-suite /path/to/policy.yml \
--filter-fail
```
## Setup CI/CD Guardrails
`vet` includes a native [GitHub Action](https://github.com/safedep/vet-action) for policy-driven guardrails against risky OSS components.
Setup Vet with GitHub Actions and Code Scanning
Integrate Vet with GitLab CI/CD pipelines
Learn advanced policy configuration with CEL
Scale across your organization with SafeDep Cloud
## What's Next?
* See the [Vet GitHub Repository](https://github.com/safedep/vet) for the latest documentation on usage and advanced features.
* For `vet`'s complete commands, sub-commands, flags, and usage documentation, see the [Vet CLI Reference Manual](https://safedep.github.io/vet).
# xBom
Source: https://docs.safedep.io/governance/xbom/overview
xBom generates a Bill of Materials enriched with AI and SaaS usage by analyzing your source code, not just manifests.
Modern applications reach far beyond declared dependencies: AI SDKs, ML models, and third-party SaaS APIs. Traditional BOM tools only read manifest files like `requirements.txt` or `pom.xml`. [xBom](https://github.com/safedep/xbom) analyzes your source code to find what your application actually uses, for a more accurate [SBOM](/concepts/sbom).
## What xBom does
Finds real evidence of AI SDKs, cloud APIs, and crypto in your code, not just declared packages.
Community-driven signatures detect components; add your own for proprietary tools.
Produces standard CycloneDX BOMs for compliance and tooling.
Supports Java and Python today, with JavaScript in progress.
## Get started
Generate your first xBOM.
Generate a standard SBOM with Vet.
SBOM versus xBOM, explained.
Add detections for new components.
# xBom Quickstart
Source: https://docs.safedep.io/governance/xbom/quickstart
Get started with SafeDep xBom, an open source Bill of Materials generator enriched with AI and SaaS usage detected from source code.
View the xBom source code and contribute on GitHub
**xBom** generates a Bill of Materials (BOM) enriched with AI components, SaaS integrations, and more. It uses static code analysis to find these in your code, not just your declared dependencies.
## Installation
Install xBom using one of the following methods:
**macOS & Linux (Homebrew):**
```bash theme={null}
# Installation on macOS & Linux
brew install safedep/tap/xbom
```
**Pre-built binary:**
Download a **[pre-built binary](https://github.com/safedep/xbom/releases)** for your operating system from the GitHub releases page.
## Generating Your First BOM
To generate a BOM for your source code, use the `generate` command:
```bash theme={null}
# Generate BOM for your source code
xbom generate --dir /path/to/your/code --bom /path/to/output/bom.cdx.json
```
Replace `/path/to/your/code` with your project directory and `/path/to/output/bom.cdx.json` with your desired output path.
The output is an SBOM in **[CycloneDX v1.6 JSON format](https://cyclonedx.org/docs/1.6/json/)**, including any AI components and other supported elements detected in the codebase.
## Supported Languages
Currently, `xbom` supports the following programming languages:
| Language | Status |
| ---------- | ---------------------------- |
| Python | |
| Java | |
| JavaScript | |
We are continuously working to expand language support.
## Supported BOM Types
xBom specializes in identifying a variety of components beyond traditional libraries.
### AI Components
xBom detects usage of AI SDKs and services, including:
* LangChain
* Anthropic
* CrewAI
* OpenAI
### Cloud Services
xBom also identifies integrations with major cloud platforms:
* Google Cloud Platform (GCP)
* Microsoft Azure
To request support for a new AI framework or cloud service, please
create an issue on
our GitHub repository
## HTML report
The primary output is a CycloneDX JSON file. xBom also prints a link to an interactive HTML report so you can browse the detected components in a browser.
### Limitations
**Current focus (AI BOM generation):**
`xbom` is currently focused on AI BOM generation. It uses static code analysis to identify AI products, SaaS APIs, and similar non-library components in your codebase.
**For full dependency SBOMs:**
To generate an SBOM covering open-source library dependencies from manifest files, use [Vet](https://github.com/safedep/vet) alongside xBom. `vet` specializes in dependency analysis and vulnerability management, and the two tools together cover more of the software supply chain.
### Telemetry
**Purpose:**
`xbom` collects anonymous usage telemetry to show which integrations and use cases are common, guiding what to build next. It collects no personally identifiable information or sensitive data.
**How to disable:**
Set the `XBOM_DISABLE_TELEMETRY` environment variable to `true`:
```bash theme={null}
export XBOM_DISABLE_TELEMETRY=true
```
What xBom is and how it works.
SBOM versus xBOM, explained.
Generate a standard SBOM with Vet.
Source and signature contributions.
# What is SafeDep?
Source: https://docs.safedep.io/introduction
SafeDep protects developers and AI coding agents against malicious open source components: block them before they run, govern the rest.
SafeDep protects developers and AI coding agents against malicious open source components. You can inspect the code you write. You cannot inspect everything you depend on: packages, IDE extensions, Agent Skills, MCP servers, and GitHub repositories. Every one of them can carry a supply chain attack, and campaigns like Shai-Hulud, Miasma, and S1ngularity spread exactly this way. SafeDep closes this blindspot. It blocks malicious components before their code runs, and it gives you visibility and policy over everything else you depend on.
Its core tools ([Vet](https://github.com/safedep/vet), [PMG](https://github.com/safedep/pmg), [xBom](https://github.com/safedep/xbom), and [Gryph](/ai-security/gryph-overview)) are free, open source, and usable without a SafeDep account. [SafeDep Cloud](/governance/cloud/overview) adds hosted policy, inventory, and org-wide visibility when your team is ready.
New here? [Choose your path](/get-started/choose-your-path): start free with one tool, add Cloud when your team is ready.
## Where to start
Stop malicious and vulnerable packages at install time and in CI/CD, with PMG and Vet.
Discover, audit, and control what AI agents access and run, with Gryph and the MCP server.
Scan repositories, SBOMs, and CI/CD for risk, and govern policy across your org with Vet and SafeDep Cloud.
Understand how SafeDep detects malicious packages, plus the core terms used across these docs.
# JFrog Xray Integration
Source: https://docs.safedep.io/package-security/jfrog-xray
Stream SafeDep malware intelligence to JFrog Xray
The JFrog Xray Integration is available on **SafeDep Professional and Enterprise** plans only. [Upgrade your plan](https://safedep.io/pricing) to access this feature.
The JFrog Xray integration runs as a daemon. It polls SafeDep for verified malicious packages
and pushes them to JFrog Xray as Custom Issues. With a blocking policy in place, Xray blocks
those packages for every developer on that instance.
## Prerequisites
* SafeDep CLI installed ([install](https://github.com/safedep/cli#safedep-cli))
* JFrog instance with Xray enabled
* JFrog Xray scoped [Access Token](https://docs.jfrog.com/administration/docs/access-tokens)
* *Optional*, for blocking malicious packages on developer machines or CIs
* JFrog Xray Malware security `policy` and repository `watch` with a block action configured
## How It Works
```bash theme={null}
# Option 1: OAuth device flow
safedep auth login
# Option 2: API key login (--api-key is a flag; pass the value with --api-key-value)
safedep auth login --tenant your-tenant.safedep.io --api-key --api-key-value YOUR_API_KEY
# Option 3: Environment variables (read directly by safedep; no login command needed)
export SAFEDEP_TENANT_ID=your-tenant.safedep.io
export SAFEDEP_API_KEY=YOUR_API_KEY
```
```bash theme={null}
safedep integration jfrog run \
--instance-url https://yourcompany.jfrog.io \
--instance-access-token YOUR_JFROG_TOKEN
# or pass token via environment variable (see Environment Variables below)
```
The daemon polls SafeDep continuously (default: every 60 seconds) and pushes any newly
verified malicious packages to Xray.
```text Output theme={null}
i Validating JFrog connectivity
✓ JFrog connectivity OK (URL + token verified)
i Starting JFrog feed poller (interval: 1m0s)
✓ Pushed: @hideliar/9router@0.4.25 (npm)
i JFrog: SD-01KR3WJYFTSNZFFS5CFNYVGZFH [201]
✓ Pushed: @sheason/d-pi@0.4.3 (npm)
i JFrog: SD-01KR3XSCZ0WNAEYY6CNMD4CQH3 [201]
i Poll cycle complete, next in 1m0s
...
..
.
```
Assumes a Malicious Package blocking policy and watch configured for the target repository (e.g. `npm-remote`).
```bash theme={null}
jf npm install @sheason/d-pi@0.4.3
```
`jf npm install` triggers Xray to index `@sheason/d-pi@0.4.3`. If SafeDep has flagged that package as malicious, Xray raises a policy violation and blocks the download.
```text Install Output theme={null}
npm ERR! 403 on a server you do not have access to.
{
"error": {
"code": "E403",
"summary": "403 Forbidden - GET https://yourcompany.jfrog.io/artifactory/api/npm/npm-virtual/@sheason/d-pi/-/d-pi-0.4.3.tgz",
"detail": "In most cases, you or one of your dependencies are requesting\na package version that is forbidden by your security policy, or\non a server you do not have access to."
}
}
```
## Limitations
#### Malicious Packages Blocking
`npm` and other package managers cache packages locally on developer machines. If a developer installs a package before SafeDep flags it, the cached copy remains accessible even after the integration pushes it to Xray.
Running `npm cache clean --force` removes the cached copy, but it clears the entire local cache and forces a full re-download of all packages.
```bash theme={null}
npm cache clean --force
```
## Configuration
### CLI Flags
| Flag | Required | Default | Description |
| ------------------------- | -------- | ----------- | ------------------------------------------------- |
| `--instance-url` | Yes\* | None | JFrog instance base URL. Must use `https://`. |
| `--instance-access-token` | Yes\* | None | JFrog access token scoped to Xray. |
| `--poll-interval` | No | `60s` | Duration between poll cycles (`30s`, `5m`, `1h`). |
| `--profile` | No | `"default"` | SafeDep credential profile. |
\*Required unless the corresponding environment variable is set.
### Environment Variables
For server deployments or CI pipelines, use environment variables to avoid passing secrets as CLI flags. When both are set, flags take precedence.
| Variable | Corresponding Flag |
| ---------------------------------------------------- | ------------------------- |
| `SAFEDEP_INTEGRATION_JFROG_ARTIFACTORY_URL` | `--instance-url` |
| `SAFEDEP_INTEGRATION_JFROG_ARTIFACTORY_ACCESS_TOKEN` | `--instance-access-token` |
**Example: environment variable setup**
```bash theme={null}
export SAFEDEP_INTEGRATION_JFROG_ARTIFACTORY_URL=https://yourcompany.jfrog.io
export SAFEDEP_INTEGRATION_JFROG_ARTIFACTORY_ACCESS_TOKEN=***
safedep integration jfrog run
```
Block malicious packages at install time and in CI/CD.
Block malicious installs on the developer machine.
How SafeDep detects malicious packages.
Centralize policy and visibility across your org.
# Package Security
Source: https://docs.safedep.io/package-security/overview
Block malicious open-source packages before they reach your code, on developer machines and in CI/CD.
Stop malicious open-source components before they reach your code. SafeDep blocks known-bad packages wherever they enter: a developer's install, your CI/CD pipeline, or your artifact registry. The same risk arrives through more than packages: IDE extensions, Agent Skills, MCP servers, and GitHub repositories carry it too. When a component is not in the known-bad database yet, scan it on demand before you adopt it.
**PMG** guards `npm`, `pip`, and other package managers on the developer machine. No account or API key needed.
Stop risky dependencies in pull requests and pipelines with the GitHub App, GitLab, and Bitbucket integrations.
Stop malicious packages in your JFrog artifact registry with SafeDep.
Run an on-demand malware analysis of any package, IDE extension, or GitHub repository before you use it.
New to how SafeDep decides what is malicious? See [Malicious Package](/concepts/malicious-package). To check a package's risk from your own code, use the [Insights API](/reference/insights-api-typescript).
For teams, [**SafeDep Cloud**](/governance/cloud/overview) adds centralized policy, endpoint inventory, and org-wide visibility on top of the open-source tools.
# PMG in GitHub Actions
Source: https://docs.safedep.io/package-security/pmg/github-actions
Block malicious packages in GitHub Actions CI by routing package installs through PMG's persistent proxy.
[PMG](https://github.com/safedep/pmg) runs as a persistent proxy in CI/CD. It starts once per job, intercepts installs from every [supported package manager](https://github.com/safedep/pmg#supported-package-managers) through standard proxy environment variables, and auto-blocks any package flagged as malicious.
In CI, PMG is non-interactive: flagged packages are always blocked, never prompted. For local development use the wrapped commands (`pmg npm install`) covered in the [PMG quickstart](/package-security/pmg/quickstart).
## Use the SafeDep PMG action
Run the [`safedep/pmg`](https://github.com/safedep/pmg) action in `server-mode`, then add a final `pmg proxy stop` step to enforce the result.
```yaml theme={null}
- uses: safedep/pmg@v1
with:
server-mode: true
api-key: ${{ secrets.SAFEDEP_API_KEY }}
tenant-id: ${{ secrets.SAFEDEP_TENANT_ID }}
- run: npm ci # intercepted automatically
- name: Enforce PMG policy
if: always()
run: pmg proxy stop --fail-on-violation
```
The `pmg proxy stop --fail-on-violation` step is what fails the job on a block and flushes the final events to the cloud. Without it the proxy keeps running and events still sync in the background, but the job won't fail on a violation and the most recent events may be missed. The `if: always()` ensures it runs even when an earlier step fails.
PMG blocks malicious packages using SafeDep's free community intelligence with or without credentials. Add the optional `api-key` and `tenant-id` to connect the run to [SafeDep Cloud](/governance/cloud/overview), so block events sync to [Endpoint Hub](/governance/cloud/endpoint-hub/overview) before the runner is destroyed.
## Example workflow
A full workflow that installs dependencies through PMG on every pull request:
```yaml install-dependencies.yml expandable theme={null}
name: Install Dependencies
on:
pull_request:
branches: [main]
permissions:
contents: read
jobs:
install-deps:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
# Start PMG in server mode
- uses: safedep/pmg@v1
with:
server-mode: true
api-key: ${{ secrets.SAFEDEP_API_KEY }}
tenant-id: ${{ secrets.SAFEDEP_TENANT_ID }}
# Your usual language setup and install
- uses: actions/setup-node@v6
with:
node-version: "24"
- run: npm ci
# Enforce the result and flush events
- name: Stop proxy
if: always()
run: pmg proxy stop --fail-on-violation
```
## Use raw commands
To wire the proxy up directly without the action:
```yaml theme={null}
- run: pmg proxy start --daemon
- run: pmg proxy env >> "$GITHUB_ENV"
- run: npm ci
- run: pmg proxy stop --fail-on-violation
if: always()
```
For the proxy lifecycle, certificate trust, bind address, and cloud event sync, see the [persistent proxy server docs](https://github.com/safedep/pmg/blob/main/docs/persistent-proxy.md).
How PMG blocks malicious packages at install time.
How SafeDep detects malicious packages.
# PMG
Source: https://docs.safedep.io/package-security/pmg/overview
PMG wraps your package managers and blocks malicious packages at install time, before any code runs. Free, open source, no account required.
[PMG](https://github.com/safedep/pmg) (Package Manager Guard) blocks malicious packages at install time. It wraps the package managers you already use, so every `npm install` or `pip install` is checked against SafeDep's malware intelligence before any code runs. It is free, open source, and needs no account or API key.
## What PMG does
* **Blocks before code runs:** catches malicious packages at install time, not after they are already in your environment.
* **No workflow change:** wraps your existing package managers. You and your AI coding agents run the same commands.
* **Deep dependency analysis:** resolves and checks the full transitive dependency tree, not just the package you asked for.
* **No account needed:** uses SafeDep's free community API. Apache 2.0 licensed, no signup or API key.
## How it works
PMG intercepts each install command, resolves the dependency tree, and checks every package against SafeDep's [malicious package intelligence](/concepts/malicious-package) before allowing the install to proceed. Known malicious packages are blocked outright. An optional dependency cooldown policy can also skip package versions published inside a recent time window, when a freshly compromised release is most likely to slip through.
It supports `npm`, `pnpm`, `yarn`, `bun`, `npx`, `pnpx`, `pip`, `uv`, and `poetry`. For dependency-resolution internals and CLI flags, see the [PMG repository](https://github.com/safedep/pmg).
Connect PMG to [SafeDep Cloud](/governance/cloud/overview) and the installs it checks sync to [Endpoint Hub](/governance/cloud/endpoint-hub/overview) as Package Guard events, a timeline of package activity across your team's endpoints. Local blocking works the same with or without an account.
## Get started
Install PMG and protect your package managers in minutes.
How SafeDep detects malicious packages across registries.
Source, full documentation, and CI usage.
Stream malicious and cooldown blocks from SafeDep Cloud to Slack or any webhook.
# PMG Quickstart
Source: https://docs.safedep.io/package-security/pmg/quickstart
Install and configure PMG (Package Manager Guard) to block malicious packages at install time.
View the PMG source code and contribute on GitHub
**Package Manager Guard (PMG)** wraps your package manager and blocks malicious packages at install time.
PMG requires **no configuration** - just install and use it as you normally
would with your package managers.
For what PMG is and how it works, see the [PMG overview](/package-security/pmg/overview).
## Installation
### Using Homebrew (Recommended)
```bash theme={null}
brew install safedep/tap/pmg
```
### Using Go Install
```bash theme={null}
go install github.com/safedep/pmg@latest
```
### Download Binary
Download the latest release from our [GitHub releases page](https://github.com/safedep/pmg/releases) and add it to your PATH.
## Quick Setup
### Automated Setup (Recommended)
Run PMG's automated setup:
```bash theme={null}
# Install shell aliases automatically
pmg setup install
```
This command:
* Creates `~/.pmg.rc` with package manager aliases
* Adds a source line to your shell configuration files
* Supports bash, zsh, and fish shells
After running `pmg setup install`, restart your terminal or run `source ~/.zshrc` (or your shell's config file) to activate the aliases.
### Manual Usage (Alternative)
To run PMG without aliases:
```bash theme={null}
pmg npm install
pmg pnpm add
pmg pip install
```
If PMG detects a malicious package, it blocks the install and displays a warning.
## Supported Package Managers
PMG supports these package managers:
| Package Manager | Status | Command |
| --------------- | ------ | -------------------------------------------------------- |
| `npm` | Active | `pmg npm install ` |
| `npx` | Active | `pmg npx ` |
| `pnpm` | Active | `pmg pnpm add ` |
| `pnpx` | Active | `pmg pnpx ` |
| `bun` | Active | `pmg bun add ` |
| `pip` | Active | `pmg pip install ` |
| `uv` | Active | `pmg uv add ` or `pmg uv pip install ` |
| `poetry` | Active | `pmg poetry add ` |
| `yarn` | Active | `pmg yarn add ` |
## Troubleshooting
### If PMG isn't working after setup
1. Restart your terminal
2. Check that interception is active: `which npm` should resolve to `~/.pmg/bin/npm` (the PMG shim). If it points to system npm, make sure `~/.pmg/bin` is early in your `$PATH`, or run `type npm` to check for a shell alias.
3. Verify PMG installation: `pmg version`
### If packages are incorrectly blocked
1. Run with `--verbose` to see detection details
2. Check the [SafeDep community](https://discord.gg/kAGEj25dCn) for known issues
3. Report false positives on [GitHub Issues](https://github.com/safedep/pmg/issues)
## Next Steps
* Learn [what PMG is and how it works](/package-security/pmg/overview)
* See the [PMG repository](https://github.com/safedep/pmg) for complete documentation and examples
* Join our [Discord community](https://discord.gg/kAGEj25dCn) for support
* Check out other SafeDep tools like [Vet](/governance/vet/overview) and [SafeDep Cloud](/governance/cloud/overview)
Run `pmg --help` to see all available commands and options. PMG runs
transparently in the background and only surfaces when it blocks a
malicious package.
# PMG in Docker and Shared VMs
Source: https://docs.safedep.io/package-security/pmg/system-install
Install PMG system-wide on Linux so Docker images, CI runners, and shared VMs block malicious packages for every user account.
Use a system-wide install when one machine or image should protect every user account: shared VMs, CI runners, and golden Docker images. It installs PATH shims for all users and an authoritative config under `/etc`, so any user's `npm install` or `pip install` is checked before it runs.
For a single developer machine, use the per-user setup in the [Quickstart](/package-security/pmg/quickstart) instead. Per-user and system-wide installs can coexist.
## Requirements
* **Linux and root.** System install is Linux-only and must run as root.
* **PMG in a system path.** Every user's shim runs PMG by absolute path, so the binary must live in a root-owned system path such as `/usr/local/bin`, executable and reachable by all users.
## What gets created
| Item | Path |
| --------------------- | ----------------------------- |
| Configuration | `/etc/safedep/pmg/config.yml` |
| Package-manager shims | `/usr/local/lib/pmg/bin` |
| Shell PATH snippet | `/etc/profile.d/pmg.sh` |
System install uses PATH shims only. It does not create the `~/.pmg.rc` alias layer that per-user setup installs.
## Docker images
PMG must be installed inside the image: PMG on the Docker host cannot see installs that run during `docker build`.
The easiest path is to copy the PMG binary from the [published PMG container image](https://github.com/safedep/pmg/pkgs/container/pmg) into your application image.
Compared to a Linux VM, a Dockerfile needs two adjustments:
1. **No `sudo`.** `RUN` steps execute as root. If your base image switches to a non-root `USER`, add `USER root` before the install step and switch back after.
2. **Set `PATH` with `ENV`.** `RUN` steps do not source `/etc/profile.d`, so add the shim directory to `PATH` explicitly.
```dockerfile theme={null}
FROM ghcr.io/safedep/pmg:latest AS pmg
FROM node:22-bookworm
COPY --from=pmg /usr/local/bin/pmg /usr/local/bin/pmg
RUN pmg setup install --system
# Required: profile.d is not sourced during docker build
ENV PATH="/usr/local/lib/pmg/bin:$PATH"
# Optional: switch to a non-root user; the PATH above still applies
RUN mkdir -p /app && chown node:node /app
WORKDIR /app
USER node
RUN npm i safedep-test-pkg@0.1.3
```
Every `RUN npm install` or `RUN pip install` after the `ENV PATH` line now goes through PMG, and the running container inherits the protection, for any `USER`. Derived images inherit it too.
If your build environment cannot pull from GHCR, install PMG with `curl -fsSL https://raw.githubusercontent.com/safedep/pmg/main/install.sh | sh` before running `pmg setup install --system`.
If a later stage sets `ENV PATH` again, keep `/usr/local/lib/pmg/bin` ahead of the real `npm`/`pip` directories. Dropping it, or ordering it behind the toolchain, turns interception off without any error.
## Linux VMs
1. **Place PMG in a system path.**
Not installed yet? The install script places it in `/usr/local/bin`, prompting for sudo for the copy:
```bash theme={null}
curl -fsSL https://raw.githubusercontent.com/safedep/pmg/main/install.sh | sh
```
Already installed in a user location? Copy it over:
```bash theme={null}
sudo install -m 755 "$(which pmg)" /usr/local/bin/pmg
```
*PMG rejects a binary in a user location (`~/.local/bin`, `~/go/bin`), since any single user could modify it. The install script also picks `~/.local/bin` when that is on your `PATH`; if it did, use the copy command above.*
2. **Install system-wide:**
```bash theme={null}
sudo pmg setup install --system
```
3. **Activate the PATH.** The install writes `/etc/profile.d/pmg.sh`, which prepends the shim directory for login shells. New login sessions pick it up; for an already-open shell:
```bash theme={null}
source /etc/profile.d/pmg.sh
```
On a multi-user host where you do not trust every member of the directory's group, tighten the install directory: `sudo chmod g-w /usr/local/bin`. Debian and Ubuntu ship it group-writable for the `staff` group (empty by default).
## Verify
Works for a VM shell and inside a container (`docker run --rm -it sh`):
```bash theme={null}
pmg setup doctor # validates config, shims, PATH, and binary integrity
```
Then prove the protection end to end with a known test package. PMG must block it:
```bash theme={null}
npm install --no-cache --prefer-online safedep-test-pkg@0.1.3
```
## Configuration
The system config file is authoritative for every user. PMG ignores a per-user `config.yml` while `/etc/safedep/pmg/config.yml` exists.
`pmg config set` and `pmg config edit` are disabled under a system config. Edit the file as root, or redeploy it through your image or configuration management.
Runtime data (event logs, cloud-sync state, cache) stays per user under `~/.config/safedep/pmg` and `~/.cache/safedep/pmg`. The invoking user must be able to write their own config directory. Relocate it with `PMG_CONFIG_DIR` / `PMG_CACHE_DIR` if needed.
## Cloud sync
Cloud sync reports install events to your SafeDep Cloud tenant, where they appear in [Package Guard](/governance/cloud/endpoint-hub/package-guard). To set it up, enable cloud sync and provide your tenant ID and API key from [SafeDep Cloud](/governance/cloud/quickstart) through the `SAFEDEP_API_KEY` and `SAFEDEP_TENANT_ID` environment variables.
### In a Docker image
Enable sync and set a stable endpoint name before your install steps:
```dockerfile theme={null}
ENV PMG_CLOUD_ENABLED=true
ENV PMG_CLOUD_ENDPOINT_ID="my-golden-image"
```
Containers report a generated hostname per run, so without `PMG_CLOUD_ENDPOINT_ID` every run registers as a new endpoint. A stable name is what you will recognize in Endpoint Hub.
The credentials exist only where you run `docker`, as shell exports or CI secrets; they never go in the Dockerfile.
Never bake credentials into an image with `ENV SAFEDEP_API_KEY=...`. `ENV` values persist in the image metadata, and anyone who pulls the image can read them.
**In the running container**, pass them through at start:
```bash theme={null}
docker run -e SAFEDEP_API_KEY -e SAFEDEP_TENANT_ID my-image
```
Compose `environment:` entries and Kubernetes Secret references work the same way. If the container is short-lived, a CI job for example, run `pmg cloud sync` before it exits so buffered events are not destroyed with it.
For reproducible builds, pin PMG to a release tag from the [published PMG container image](https://github.com/safedep/pmg/pkgs/container/pmg).
**During `docker build`**, install steps need no credentials: PMG buffers each event locally. Deliver the buffer in one final `RUN` step, with the credentials mounted as BuildKit secrets. Mount them directly as environment variables so PMG can use its standard `SAFEDEP_API_KEY` and `SAFEDEP_TENANT_ID` credential resolver:
```dockerfile theme={null}
RUN npm ci
RUN --mount=type=secret,id=safedep_api_key,env=SAFEDEP_API_KEY,required=true \
--mount=type=secret,id=safedep_tenant_id,env=SAFEDEP_TENANT_ID,required=true \
pmg cloud sync
```
Each `--secret` reads the variable exported where you run the build:
```bash theme={null}
docker build \
--secret id=safedep_api_key,env=SAFEDEP_API_KEY \
--secret id=safedep_tenant_id,env=SAFEDEP_TENANT_ID .
```
A blocked package fails its `RUN` step: later steps never run, including the final sync, so a failed build uploads nothing. The block itself shows in the build output.
```dockerfile theme={null}
FROM ghcr.io/safedep/pmg:latest AS pmg
FROM node:22-bookworm
COPY --from=pmg /usr/local/bin/pmg /usr/local/bin/pmg
RUN pmg setup install --system
# Before the install steps: shim PATH, sync enabled, stable endpoint name
ENV PATH="/usr/local/lib/pmg/bin:$PATH"
ENV PMG_CLOUD_ENABLED=true
ENV PMG_CLOUD_ENDPOINT_ID="my-golden-image"
RUN mkdir -p /app && chown node:node /app
WORKDIR /app
USER node
COPY --chown=node:node package*.json ./
# No credentials needed: events buffer locally
RUN npm ci
# Final step delivers the buffer
RUN --mount=type=secret,id=safedep_api_key,env=SAFEDEP_API_KEY,required=true \
--mount=type=secret,id=safedep_tenant_id,env=SAFEDEP_TENANT_ID,required=true \
pmg cloud sync
```
Build it with the credentials exported in your shell:
```bash theme={null}
docker build \
--secret id=safedep_api_key,env=SAFEDEP_API_KEY \
--secret id=safedep_tenant_id,env=SAFEDEP_TENANT_ID \
-t my-image .
```
### On a shared VM
Enable sync in `/etc/safedep/pmg/config.yml` as root:
```yaml theme={null}
cloud:
enabled: true
```
Then put the credential values in a root-owned snippet such as `/etc/profile.d/safedep-cloud.sh`, so every login shell exports them:
```bash theme={null}
# /etc/profile.d/safedep-cloud.sh
export SAFEDEP_API_KEY=""
export SAFEDEP_TENANT_ID=""
```
Users who should sync with their own key can set the same variables in their own shell profile instead. One shared key does not blur the picture: every synced event records which OS user ran the install. It does mean every user on the host can read the key.
Installs that ran before the credentials were in place are not lost: PMG buffers those events per user and delivers them on the next sync that finds credentials.
Auto-sync cadence, manual sync, and viewing events are covered in [Package Guard](/governance/cloud/endpoint-hub/package-guard); the full set of cloud keys is in the [PMG configuration reference](https://github.com/safedep/pmg/blob/main/docs/config.md).
## Certificates
System install does not set up a MITM certificate authority. For npm and pip on Linux, PMG's default ephemeral CA and environment-variable injection are sufficient. To install a persistent CA into the OS trust store, run `pmg setup cert install --system` as your normal user.
## Uninstall
```bash theme={null}
sudo pmg setup remove --system # remove shims and profile snippet
sudo pmg setup remove --system --config-file # also remove /etc/safedep/pmg/config.yml
```
This leaves the PMG binary in place. To remove it too, run `sudo rm /usr/local/bin/pmg`, or uninstall it through your package manager if you installed it that way.
## Troubleshooting
Try running `pmg setup doctor` first: it detects each of these states and prints the matching fix.
Version managers like nvm, pyenv, volta, and asdf prepend their own bin directories from rc files that run after `/etc/profile.d`, putting the real `npm`/`pip` ahead of the shims.
* Keep `/usr/local/lib/pmg/bin` first in a durable `ENV PATH` or login PATH
* Or call `pmg npm` / `pmg pip` directly
After `source .venv/bin/activate`, bare `pip` uses the venv binary and skips the shims. Call `pmg pip` inside the venv.
Another account created or owns your config directory. Check the path in the error message.
If the path is **inside your own home**, a root run created it as root. This happens with `su` without `-` (prefer `sudo` or `su -`) and with images that set `ENV HOME` before dropping root. Restore ownership:
```bash theme={null}
sudo chown -R $(id -un) ~/.config/safedep
```
If the path is **inside another user's home**, your environment leaked that user's `HOME` or `XDG_CONFIG_HOME` (common with `sudo -u` on CI runners). Fix the environment; do not `chown` another user's directory, that breaks PMG for them.
```bash theme={null}
export XDG_CONFIG_HOME="$HOME/.config"
```
Full reference in the PMG repo: binary validation rules, user data directories, and limitations.
How PMG blocks malicious packages at install time.
# Scanning from CI and AI Agents
Source: https://docs.safedep.io/package-security/scan/automation
Use on-demand package scanning from scripts and AI agents: authentication model, JSON output, verdict gating, polling, and retries.
On-demand scanning is built to be driven by automation: platform teams wiring pre-adoption checks into internal tooling, and AI agents checking an external component before using it. This guide covers the contract that automation depends on.
## A signed-in session is required
`safedep package scan` calls a control-plane API that authenticates with your signed-in session, established by `safedep auth login`. **API keys do not submit scans.** This is a deliberate design decision, not a missing feature.
The reasoning: every scan draws down your plan's allowance, and with [on-demand billing](/governance/cloud/usage-billing) enabled it can create real charges. SafeDep treats operations that spend your money as control-plane operations, which require the stronger trust of a signed-in session. API keys are data-plane credentials: long-lived, distributed to tools and CI environments, and fine for reading curated threat intel. A leaked API key can read intel, but it cannot spend your scan budget.
What this means in practice:
* **Automation on a workstation works.** Scripts and AI agents running where a human has signed in (a developer machine, an agent sandbox with the CLI configured) submit scans normally. The CLI refreshes the session silently. Re-run `safedep auth login` when the refresh eventually expires.
* **Headless CI cannot submit scans today.** There is no non-interactive credential for scan submission. If your pipeline needs an inline malicious-package gate, use the free fast-path lookup with [Vet](/governance/cloud/malware-analysis), which works with an API key or no account at all.
## Machine-readable output
Every command takes `-o json` (also `-o plain` for tab-separated lines). A scan object looks like:
```json theme={null}
{
"scan_id": "01J...",
"target": {
"ecosystem": "npm",
"name": "express",
"version": "4.18.2"
},
"status": "completed",
"verdict": "benign",
"confidence": 0.98,
"created_at": "2026-08-07T10:00:00Z",
"completed_at": "2026-08-07T10:03:12Z"
}
```
`status` ends at `completed` or `failed`. `verdict` is present only once the scan is `completed`, and is one of `malware`, `benign`, or `inconclusive`. On failure, `failure_reason` carries the human-readable cause and `failure_code` a stable classification when one exists (for example `package-not-found`).
## Gate on the verdict, not the exit code
`scan run` exits non-zero when the scan fails or the CLI hits an error (timeout, auth). A scan that completes exits zero **whatever the verdict**: completing the analysis is the command's job. A gate must check the verdict field:
```bash theme={null}
verdict=$(safedep package scan run "pkg:npm/express@4.18.2" -o json | jq -r .verdict)
case "$verdict" in
benign) echo "ok" ;;
malware) echo "blocked: malware verdict"; exit 1 ;;
*) echo "needs review: verdict=$verdict"; exit 1 ;;
esac
```
Treat `inconclusive` as needs-review, not as a pass. The analysis completed but could not reach a confident verdict.
## Submit now, check later
For long-running pipelines or agents that should not block, submit without waiting and poll:
```bash theme={null}
scan_id=$(safedep package scan run "pkg:npm/express@4.18.2" --wait=false -o json | jq -r .scan_id)
safedep package scan get --scan-id "$scan_id" -o json
safedep package scan show --scan-id "$scan_id" -o json # full report, once completed
```
`get` returns the current status and, once completed, the verdict. `show` returns the full report and errors while the scan is still running.
## Retries are free
Submitting the same ecosystem, name, and version again returns the existing scan instead of creating a new one. Retrying after a network failure or a CLI timeout is safe and does not draw down your allowance twice. `--rescan` opts out of this and forces a new analysis, which counts against the allowance.
A CLI timeout does not fail the scan. `scan run` waits up to 5 minutes by default (`--timeout` adjusts this). If it times out, the scan keeps running server-side, and the error message includes the `scan get` command to resume polling.
## Direct API access
The CLI is a thin client over `PackageScanService` (`SubmitScan`, `GetScan`, `ListScans`, `GetScanReport`) in `safedep.services.malysis.v1`, published at [buf.build/safedep/api](https://buf.build/safedep/api). The service is ConnectRPC, so it also speaks plain HTTP/JSON: POST to `https://cloud.safedep.io/safedep.services.malysis.v1.PackageScanService/`.
Direct API access uses the same authentication model as the CLI: an `authorization` header carrying the access token from your OAuth2 session (an API key does not work), and an `x-tenant-id` header carrying your tenant domain. The CLI does not print its session token today. For most integrations, drive the CLI or use a [generated SDK](https://buf.build/safedep/api/sdks) with your own OAuth2 session.
```bash theme={null}
# Submit a scan
curl https://cloud.safedep.io/safedep.services.malysis.v1.PackageScanService/SubmitScan \
-H "authorization: $SAFEDEP_ACCESS_TOKEN" \
-H "x-tenant-id: your-company.safedep.io" \
--json '{"target":{"package_version":{"package":{"ecosystem":"ECOSYSTEM_NPM","name":"express"},"version":"4.18.2"}}}'
# Poll the scan by id
curl https://cloud.safedep.io/safedep.services.malysis.v1.PackageScanService/GetScan \
-H "authorization: $SAFEDEP_ACCESS_TOKEN" \
-H "x-tenant-id: your-company.safedep.io" \
--json '{"scan_id":""}'
```
```python theme={null}
import requests
BASE = "https://cloud.safedep.io/safedep.services.malysis.v1.PackageScanService"
HEADERS = {
"authorization": ACCESS_TOKEN, # OAuth2 session access token
"x-tenant-id": "your-company.safedep.io",
}
submit = requests.post(f"{BASE}/SubmitScan", headers=HEADERS, json={
"target": {
"package_version": {
"package": {"ecosystem": "ECOSYSTEM_NPM", "name": "express"},
"version": "4.18.2",
}
}
})
submit.raise_for_status()
scan_id = submit.json()["scanId"]
scan = requests.post(f"{BASE}/GetScan", headers=HEADERS, json={"scan_id": scan_id})
scan.raise_for_status()
print(scan.json())
```
See the [API introduction](/reference/api-introduction) for transport details and rate limits.
Allowances, spending caps, and the commands to inspect usage.
First-time setup and your first scan.
# On-Demand Package Scanning
Source: https://docs.safedep.io/package-security/scan/overview
Run an on-demand malware analysis of any package, IDE extension, or GitHub repository before you use it.
On-demand package scanning submits a software component to SafeDep Cloud for malware analysis and returns a verdict with evidence. You name an exact version of a package, an IDE extension, or a GitHub repository. SafeDep analyzes it and tells you whether it is safe to use.
It is an independent SafeDep Cloud feature, used from the `safedep` CLI or the SafeDep API. It is not coupled to Vet or PMG.
## Fast path and slow path
SafeDep answers "is this component malicious?" through two paths built on the same scanning infrastructure.
The **fast path** is automatic. SafeDep continuously scans packages from public registries and records verdicts in its known malicious packages database. SafeDep tools query that database to protect the SDLC at each stage: [PMG](/package-security/pmg/overview) at install time, [Vet](/governance/vet/overview) in CI, the [SafeDep MCP server](/ai-security/mcp-server) in AI agent sessions. It is free, answers in milliseconds, and needs no action from you.
The **slow path** is on-demand scanning: the same scanning infrastructure, made available to you on demand. Use it where continuous scanning does not reach. That means components that are not registry packages, such as an MCP server or AI agent skill adopted from a GitHub repository, or an IDE extension, and packages that are not covered by SafeDep's continuous scanning.
| | Fast path: known-malicious lookup | Slow path: on-demand scan |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------- |
| What it checks | SafeDep's database of already-analyzed, known malicious packages | The exact component you name, analyzed on demand |
| Answers in | Milliseconds | Minutes |
| Cost | Free | Metered, from your plan's scan allowance |
| Used through | [Vet](/governance/vet/overview), [PMG](/package-security/pmg/overview), the [SafeDep MCP server](/ai-security/mcp-server), automatically | The `safedep` CLI and the SafeDep API, when you ask |
## How a scan works
Every scan runs SafeDep's malware analysis pipeline against the exact component you name:
* **Static analysis** examines the component's code and metadata for malicious behavior signals: install-time hooks, obfuscation, network and filesystem access, credential theft patterns, and more.
* **AI agent triage** investigates those signals. Static analysis alone flags suspicious-looking code in many legitimate packages, so an AI agent examines each signal in context to separate real threats from benign code.
Triage works because the agent draws on SafeDep's code context for open source software: baselines and memories built from continuously analyzing packages across ecosystems. Because SafeDep knows what normal looks like for OSS code, triage rejects false alarms that a signature-only scanner would surface and digs into behavior that is genuinely out of place.
The result is a verdict backed by evidence you can inspect, not a bare score. See [What a scan produces](#what-a-scan-produces).
## What you can scan
JavaScript and TypeScript packages
Python packages
Go modules
Ruby gems
Rust crates
Visual Studio Code extensions
Extensions from the Open VSX registry
Actions used in your workflows
Any repository, at a tag, branch, or commit
A GitHub repository target covers anything distributed as a repository rather than through a package registry: a library you found on GitHub, an AI agent skill, a project template. This makes on-demand scanning usable as a pre-use check for components that no registry-based tool can see.
## What a scan produces
A completed scan carries a verdict: `malware`, `benign`, or `inconclusive`, with a confidence score. When the verdict is `malware`, the full report explains why: a summary, file-level evidence (file, line, observed behavior), project-level evidence, indicators of compromise, and warnings.
A scan that cannot run, for example because the named version does not exist in the registry, ends as `failed` with a reason. Failure is an operational outcome, not a verdict.
For how SafeDep decides what is malicious, see [Malicious Package](/concepts/malicious-package).
## Availability
On-demand package scanning requires a SafeDep Cloud **Professional** or **Enterprise** plan. Paid plans include a monthly scan allowance per seat, pooled across your tenant. Beyond the allowance, you can opt in to [usage-based on-demand billing](/governance/cloud/usage-billing). The free trial includes one seat's allowance. See [pricing](https://safedep.io/pricing).
Current limits, stated plainly:
* Scanning is available from the CLI and the API. There is no web dashboard for on-demand scans yet.
* Submitting a scan requires a signed-in session. API keys do not submit scans. See [Scanning from CI and AI Agents](/package-security/scan/automation) for the reasoning.
* A scan typically completes in a few minutes. The CLI waits up to 5 minutes by default.
## Get started
Install the CLI, sign in, and run your first scan.
Authentication model, JSON output, verdict gating, polling, and retry semantics for automation.
How allowances are counted and how to enable usage-based billing beyond them.
# Package Scan Quickstart
Source: https://docs.safedep.io/package-security/scan/quickstart
Install the safedep CLI, sign in to SafeDep Cloud, and run your first on-demand package scan.
In this guide you install the `safedep` CLI, sign in to SafeDep Cloud, activate a plan, and scan your first package. Setup takes a few minutes. Each scan takes a few minutes more.
For what on-demand scanning is and when to use it, see the [overview](/package-security/scan/overview).
```bash theme={null}
brew install --cask safedep/tap/cli
```
```bash theme={null}
npm install -g @safedep/cli
```
Verify with `safedep version`.
```bash theme={null}
safedep auth login
```
This opens a browser-based device login. If you are new to SafeDep, it also sets up your account. The CLI keeps your session refreshed after this one sign-in.
```bash theme={null}
safedep subscription status
```
On-demand scanning needs a Professional or Enterprise plan. If you do not have one yet, activate the free trial:
```bash theme={null}
safedep subscription trial enable
```
The command asks for basic billing profile details the first time. No payment method is needed for the trial. The trial includes one seat's monthly scan allowance.
```bash theme={null}
safedep package scan run pkg:npm/express@4.18.2
```
The CLI submits the scan and waits, showing status as the analysis progresses. When the scan completes you get a verdict panel: `benign`, `malware`, or `inconclusive`, with a confidence score. On a `malware` verdict the full evidence report is printed inline.
The target can be a purl (as above), a GitHub URL, or an explicit triple:
```bash theme={null}
safedep package scan run --ecosystem pypi --name requests --version 2.32.3
```
On-demand scanning also covers components no registry-based tool can check. Scan a GitHub repository at a tag, branch, or commit:
```bash theme={null}
safedep package scan run https://github.com/safedep/vet/tree/v1.18.1
```
Or a VS Code extension, named as `publisher.extension`:
```bash theme={null}
safedep package scan run --ecosystem vscode --name esbenp.prettier-vscode --version 9.9.0
```
List the tenant's scans, newest first:
```bash theme={null}
safedep package scan list
```
Fetch the full report of a completed scan, by package or by scan id:
```bash theme={null}
safedep package scan show pkg:npm/express@4.18.2
safedep package scan show --scan-id --save report.json
```
Re-running `scan run` for the same package version returns the existing scan instead of starting (and counting) a new one. Use `--rescan` to force a new analysis.
## Next steps
JSON output, verdict gating, polling, and the authentication model for automation.
Check your scan allowance and enable usage-based billing beyond it.
# API Reference
Source: https://docs.safedep.io/reference/api-introduction
SafeDep Cloud API: transport, request headers, OAuth2/OIDC endpoints, and rate limits
SafeDep supports a [gRPC](https://grpc.io/) API with
a [ConnectRPC](https://connectrpc.com/) facade allowing clients to use both
gRPC over HTTP/2 and JSON over HTTP/1.1 based clients.
The canonical API specification lives at [buf.build/safedep/api](https://buf.build/safedep/api), which also publishes generated SDKs for all supported languages.
## Planes and authentication
* Control plane APIs (`cloud.safedep.io`, e.g. creating policy) require OAuth 2.0 authentication (JWT).
* Data plane APIs (`api.safedep.io`, e.g. package insights) accept API keys or JWTs.
Control plane APIs are for management tooling; data plane APIs are for security tools that use or integrate with SafeDep. For tool-level login flows (safedep CLI, vet, CI/CD), see the [authentication guide](/governance/cloud/authentication).
## Request headers
Regardless of transport (gRPC over HTTP/2 or JSON over HTTP/1.1), requests carry the same two headers:
| Header | Value |
| --------------- | -------------------------------------------------------- |
| `Authorization` | Your API key or JWT, sent **as-is** (no `Bearer` prefix) |
| `X-Tenant-ID` | Your tenant domain (e.g. `your-company.safedep.io`) |
## OAuth2 / OIDC
The SafeDep Cloud Identity Service at `https://auth.safedep.io` provides OAuth2/OIDC authentication for the control plane.
**OpenID configuration endpoint:**
```
https://auth.safedep.io/.well-known/openid-configuration
```
Command-line tools authenticate with the OAuth2 Device Code flow. A reference implementation is available in the [vet OAuth2 client](https://github.com/safedep/vet/blob/main/cmd/cloud/login.go).
## Rate limiting
SafeDep Cloud enforces rate limits at the API gateway, measured **per second**, with no hourly quota:
* **Data plane** (`api.safedep.io`): up to **500 requests/second** per API key
* **Management API**: up to **20 requests/second**
These limits are subject to change.
API key and OAuth/JWT authentication for the safedep CLI, vet, and CI/CD.
Control plane, data plane, and other SafeDep hosts.
Query package insights from a TypeScript client.
Query your tenant data with SQL.
# Build Your Own Queries
Source: https://docs.safedep.io/reference/build-your-own-queries
Speed up filtering and reporting by working with enriched JSON data dumps
Scanning package manifests is resource-intensive: `vet` must enrich each package by querying the Insights API. Because filtering and reporting can run many times on the same manifest, you can dump the enriched data as JSON once and reload it for subsequent operations.
## Query Workflow
The BYOQ workflow consists of two main phases:
Scan and enrich package data, then dump to JSON files for reuse
Load enriched data for fast filtering, querying, and report generation
### Phase 1: Dump Enriched JSON Manifests
Collect and enrich package data, then save to a directory for reuse:
```bash theme={null}
# Single lockfile
vet scan --lockfiles /path/to/package-lock.json --json-dump-dir /tmp/dump
# Entire repository
vet scan -D /path/to/repository --json-dump-dir /tmp/dump-many
```
The JSON dump contains all enriched metadata including vulnerabilities, scorecard data, licenses, and project information.
### Phase 2: Load and Query Enriched Metadata
Use the dumped data for fast filtering and reporting:
```bash theme={null}
# Generate summary report
vet query --from /tmp/dump --report-summary
# Apply custom filters
vet query --from /tmp/dump --filter 'scorecard.scores.Maintained == 0'
```
## Security Guardrails with Filters
Implement security guardrails in CI/CD pipelines using the `--filter-fail` argument, which causes the command to fail if any package matches the given filter.
### Example: Fail Build on Unmaintained Packages
```bash theme={null}
vet query --from /path/to/json-dump \
--filter 'scorecard.scores.Maintained == 0' \
--filter-fail
```
When any package matches the filter, the command exits with a non-zero status:
```bash theme={null}
echo $?
# Output: 255
```
## Advanced Query Examples
### Multi-Criteria Security Checks
```bash theme={null}
# Fail on critical vulnerabilities OR unmaintained packages
vet query --from /tmp/dump \
--filter 'vulns.critical.size() > 0 || scorecard.scores.Maintained == 0' \
--filter-fail
```
### License Compliance Checks
```bash theme={null}
# Find packages with non-approved licenses
vet query --from /tmp/dump \
--filter '!licenses.exists(p, p in ["MIT", "Apache-2.0", "BSD-3-Clause"])' \
--report-json compliance-violations.json
```
### Risk Assessment Queries
```bash theme={null}
# Find high-risk packages (multiple criteria)
vet query --from /tmp/dump \
--filter 'vulns.high.size() > 0 && scorecard.scores["Security-Policy"] < 5 && projects.exists(p, p.stars < 100)'
```
The CEL filter input schema and syntax.
Turn these filters into reusable policy files.
Generate exception lists from query results.
Query synced data across your org.
# API Endpoints
Source: https://docs.safedep.io/reference/endpoints
Canonical list of SafeDep service hostnames: console, data plane, control plane, identity, community API, and MCP.
These are the public SafeDep hosts you work with directly: the console, the API planes, identity, the community API, and the hosted MCP server. Use this page as the canonical reference for what each host does and how it authenticates. For how to authenticate against the API planes, see [Authentication](/governance/cloud/authentication).
This list is not a complete egress allowlist. Some integrations reach additional internal hosts. Enabling vet-action's comments proxy, for example, adds `ghcp-integrations.safedep.io`. Check the relevant integration's docs when you configure strict firewall rules.
## Hosts
| Host | Role | Authentication |
| -------------------------- | ----------------------------------------------------------------------------------- | ------------------------------ |
| `app.safedep.io` | Web console: sign in, manage your tenant, and create API keys | Interactive login (OAuth/OIDC) |
| `api.safedep.io` | Data plane: package insights, scanning, and malware analysis (gRPC / ConnectRPC) | API key |
| `cloud.safedep.io` | Control plane: tenant, policy, and management operations | JWT |
| `auth.safedep.io` | Identity provider: OAuth2 / OIDC, issues and validates JWTs | OAuth2 / OIDC |
| `community-api.safedep.io` | Community API: public malware and package queries | None (keyless) |
| `mcp.safedep.io` | Hosted [Model Context Protocol](/ai-security/mcp-server) server for AI coding tools | API key |
You create API keys in the web console at [`app.safedep.io/settings/api-keys`](https://app.safedep.io/settings/api-keys). Your **tenant ID** is your tenant domain, for example `your-company.safedep.io`.
## Notes
* `app.safedep.io` is the SafeDep Cloud console. It replaces the retired `platform.safedep.io`, which is no longer in use. Update any old references to that host.
* The data plane (`api.safedep.io`) and control plane (`cloud.safedep.io`) speak gRPC with a [ConnectRPC facade](/reference/api-introduction), not REST. See the canonical schemas at [buf.build/safedep/api](https://buf.build/safedep/api).
* The community API (`community-api.safedep.io`) needs no authentication and is rate-limited under a fair-usage policy.
How to authenticate against the data and control planes.
Canonical gRPC / ConnectRPC schemas and generated SDKs.
# Exceptions
Source: https://docs.safedep.io/reference/exceptions
Reference for Vet exceptions: the file format, the flags that generate and apply them, and the matching rules.
An exception excludes a package from scan results and reports. Use it for false positives, accepted risks during remediation, or legacy dependencies being migrated. This page is the reference for the exceptions file and the flags that work with it.
An excepted package is skipped entirely, including any future issues it develops. Every exception must carry an expiry date; permanent exceptions are not allowed. Exceptions also cannot be scoped to a whole manifest, only to specific packages.
## Exceptions file format
An exceptions file lists packages by ecosystem, name, and version, each with a unique `id` and an `expires` timestamp:
```yaml theme={null}
description: Exceptions File for vet
exceptions:
- ecosystem: npm
expires: "2025-05-10T00:00:00Z"
id: 01JKMC07KAGJYEDZX1XPAC3SKP
name: '@babel/plugin-transform-function-name'
version: 7.18.9
- ecosystem: pypi
expires: "2025-05-10T00:00:00Z"
id: 01JKMC07KASSGYH1PHQY09QNZ3
name: 'pillow'
version: '12.1.0'
```
| Field | Rule |
| ----------- | --------------------------------------------------------------------------- |
| `expires` | Mandatory. RFC3339 timestamp. Expired exceptions are ignored automatically. |
| `id` | Mandatory. Any unique string. |
| `ecosystem` | Case-insensitive (`PyPi`, `pypi`, `PyPI` all match). |
| `version` | Exact version, or `*` to match any version. |
Supported ecosystems include `npm`, `PyPI`, `Maven`, `Go`, `RubyGems`, `Cargo`, `NuGet`, `Packagist`, `Hex`, `Pub`, `GitHubActions`, `Terraform`, `VSCodeExtensions`, `OpenVSXExtensions`, and `Homebrew`. For the authoritative list, see the [Vet source](https://github.com/safedep/vet/blob/main/pkg/models/models.go).
## Generating exceptions
Generate an exceptions file from a [JSON dump](/reference/build-your-own-queries) using a [CEL filter](/reference/filtering). For example, except packages that have no critical or high vulnerabilities:
```bash theme={null}
vet scan -D /path/to/repo --json-dump-dir /path/to/dump
vet query --from /path/to/dump \
--exceptions-generate /path/to/exceptions.yml \
--exceptions-filter '!vulns.critical.exists(p, true) && !vulns.high.exists(p, true)' \
--exceptions-till '2025-05-01'
```
| Flag | Purpose |
| ------------------------------ | --------------------------------------------------------------------- |
| `--exceptions-generate ` | Write the generated exceptions to ``. |
| `--exceptions-filter ` | Except only packages matching this CEL expression. |
| `--exceptions-till ` | Expiry date, parsed as `YYYY-mm-dd` (set to `00:00:00` UTC, RFC3339). |
Review the generated file before using it, and do not pass `--exceptions` while generating, or the active exceptions will skew the output.
## Applying exceptions
Pass an exceptions file to `vet` as a global flag:
```bash theme={null}
vet --exceptions /path/to/exceptions.yml scan -D /path/to/repo
```
With [vet-action](https://github.com/safedep/vet-action), commit the file (conventionally `.github/vet/exceptions.yml`) and reference it:
```yaml theme={null}
- name: Vet Scan
uses: safedep/vet-action@v1
with:
exception-file: .github/vet/exceptions.yml
```
## Matching rules
* Exceptions apply at the package level and are shared across all analyzers and reporters.
* Comparisons are case-insensitive, except `version`, which matches exactly unless set to `*`.
* The first matching exception applies.
* Expired exceptions are ignored, and an exception cannot be created without an expiry date.
The CEL expressions used in `--exceptions-filter`.
The JSON dump and query workflow exceptions build on.
Enforce policy on the packages that remain.
Configure exceptions in GitHub Actions.
# Filtering
Source: https://docs.safedep.io/reference/filtering
Reference for Vet's CEL filter expressions: the input schema, available fields, and the operators and functions you can use.
Vet filters dependencies with [CEL](https://cel.dev/) expressions. A filter is a boolean expression evaluated against each package; a package is included in the results when the expression evaluates to `true`. This page is the reference for the filter input and CEL syntax. For the concept, see [CEL](/concepts/cel).
The `vet scan --filter` examples below use Vet's original filter interface. Vet also ships a newer `--policy` engine with a different input schema; see the [Vet repository](https://github.com/safedep/vet) for its format.
## Running a filter
Filter a scan directly, or filter cached results from a [JSON dump](/reference/build-your-own-queries):
```bash theme={null}
# Filter a scan
vet scan -D /path/to/repo --filter 'vulns.critical.size() > 0'
# Filter cached results
vet query --from /tmp/dump --filter 'licenses.exists(p, p == "GPL-3.0")'
```
Add `--filter-fail` to exit non-zero when any package matches, for CI/CD gating.
## Filter input
Each expression receives these variables:
| Variable | Content |
| ----------- | ----------------------------------------------------------------------------------------------------- |
| `_` | Root variable holding the others |
| `pkg` | Package info: `pkg.ecosystem`, `pkg.name`, `pkg.version` |
| `vulns` | Vulnerabilities by severity: `vulns.all`, `vulns.critical`, `vulns.high`, `vulns.medium`, `vulns.low` |
| `scorecard` | OpenSSF Scorecard: `scorecard.score`, `scorecard.scores["Check-Name"]` |
| `projects` | Source projects, each with `stars`, `forks`, `issues`, `type` |
| `licenses` | SPDX license codes |
See the [filter input specification](https://github.com/safedep/vet/blob/main/api/filter_input_spec.proto) for the full message structure.
### Input example
A filter sees each package as a structured input:
```json theme={null}
{
"pkg": { "ecosystem": "npm", "name": "lodash.camelcase", "version": "4.3.0" },
"vulns": { "all": [], "critical": [], "high": [], "medium": [], "low": [] },
"scorecard": { "scores": { "Maintained": 0, "Dangerous-Workflow": 10, "Token-Permissions": 0 } },
"projects": [ { "name": "lodash/lodash", "type": "GITHUB", "stars": 55518, "forks": 6787, "issues": 464 } ],
"licenses": ["MIT"]
}
```
## CEL syntax
Functions: `size()` (array or map length), `exists(var, condition)` (any element matches), `in` (membership), `contains()`, `startsWith()` / `endsWith()`.
Operators: `==` `!=` `<` `<=` `>` `>=` (comparison), `&&` `||` `!` (logical), `+` `-` `*` `/` (arithmetic).
Types: booleans (`true` / `false`), double-quoted strings, numbers, arrays (`["a", "b"]`), and maps (`{"key": "value"}`).
## Example expressions
```bash theme={null}
# Any critical or high vulnerability
vulns.critical.size() > 0 || vulns.high.size() > 0
# Unmaintained per OpenSSF Scorecard
scorecard.scores.Maintained == 0
# Not an approved license
!licenses.exists(p, p in ["MIT", "Apache-2.0", "BSD-3-Clause"])
# Low-popularity GitHub project
projects.exists(x, x.type == "GITHUB" && x.stars < 100)
# Missing license information
licenses.size() == 0
```
What CEL is and how SafeDep uses it.
Combine expressions into reusable policy files.
Filter cached scan data with the query workflow.
The scorecard checks referenced in scores.
# Insights API with TypeScript
Source: https://docs.safedep.io/reference/insights-api-typescript
Build applications that leverage SafeDep Insights API using TypeScript and ConnectRPC
To follow this guide you need a SafeDep Cloud API Key and Tenant Identifier. See [Cloud Quickstart](/governance/cloud/quickstart) on how to onboard to SafeDep Cloud and get an API key.
This guide queries the SafeDep Insights API v2 for open-source package security metadata using TypeScript. Any language [supported by the API SDK](https://buf.build/safedep/api/sdks) follows the same pattern.
## Project Setup
### Initialize TypeScript Project
Create a new TypeScript project:
```bash theme={null}
npm init -y
npm install --save-dev typescript @types/node
```
### Configure Buf Registry
Configure npm to use the [Buf Registry](https://buf.build/docs/bsr/generated-sdks/npm/) for SafeDep API SDKs:
```bash theme={null}
npm config set @buf:registry https://buf.build/gen/npm/v1/
```
### Install SafeDep API SDKs
Install the required libraries:
```bash theme={null}
npm install --save @buf/safedep_api.connectrpc_es@latest @connectrpc/connect @connectrpc/connect-node
```
## Authentication Setup
### Environment Variables
Set your SafeDep Cloud credentials:
```bash theme={null}
export SAFEDEP_API_KEY=your-api-key
export SAFEDEP_TENANT_ID=your-tenant-id
```
Never hardcode API keys in your source code. Always use environment variables or secure configuration management.
## Implementation
### Import Dependencies
Set up the necessary imports for ConnectRPC client and SafeDep services. The exact import paths and client constructor track the connect-es version of the generated SDK, so validate them against the version you install:
```typescript theme={null}
import { createClient, Interceptor } from "@connectrpc/connect";
import { createConnectTransport } from "@connectrpc/connect-node";
import { InsightService } from "@buf/safedep_api.connectrpc_es/safedep/services/insights/v2/insights_connect.js";
import { Ecosystem } from "@buf/safedep_api.bufbuild_es/safedep/messages/package/v1/ecosystem_pb.js";
```
### Authentication Interceptor
This interceptor adds authentication headers to each request:
```typescript theme={null}
function authenticationInterceptor(token: string, tenant: string): Interceptor {
return (next) => async (req) => {
req.header.set("authorization", token);
req.header.set("x-tenant-id", tenant);
return await next(req);
}
}
```
### Main Application Logic
Query package insights with the main function:
```typescript theme={null}
async function main() {
// Validate environment variables
const token = process.env.SAFEDEP_API_KEY;
if (!token) {
console.error("SAFEDEP_API_KEY is required");
process.exit(1);
}
const tenantId = process.env.SAFEDEP_TENANT_ID;
if (!tenantId) {
console.error("SAFEDEP_TENANT_ID is required");
process.exit(1);
}
// Create transport with authentication
const transport = createConnectTransport({
baseUrl: "https://api.safedep.io",
httpVersion: "1.1",
interceptors: [authenticationInterceptor(token, tenantId)]
});
// Create client and make API call
const client = createClient(InsightService, transport);
const res = await client.getPackageVersionInsight({
packageVersion: {
package: {
ecosystem: Ecosystem.NPM,
name: "lodash",
},
version: "4.17.21",
}
});
console.log(res.toJson());
}
// Run the application
main().catch(console.error);
```
## API Reference
For request and response schemas, see the [Insights v2 API Specification](https://buf.build/safedep/api/docs/main:safedep.services.insights.v2#safedep.services.insights.v2.GetPackageVersionInsightRequest).
### Available Ecosystems
Supported package ecosystems:
* `NPM` - Node.js packages
* `PYPI` - Python packages
* `MAVEN` - Java/JVM packages
* `CARGO` - Rust packages
* `NUGET` - .NET packages
### Response Data
The response includes:
* **Vulnerabilities**: Known security vulnerabilities
* **Licenses**: License information and compliance data
* **Scorecard**: OpenSSF Scorecard metrics
* **Malware**: Malware detection results
* **Metadata**: Package information and statistics
Complete API specification and schema documentation
SDKs available for multiple programming languages
Get started with SafeDep Cloud and API access
Learn more about the ConnectRPC framework
# Path Exclusion
Source: https://docs.safedep.io/reference/path-exclusion
Exclude specific directories and files from security scans using pattern matching
`vet` supports path exclusions when scanning a directory: use the `--exclude` flag to skip path patterns within the target. Path exclusions are available only for the `scan` command.
## Basic Usage
Exclude a single path pattern during directory scanning:
```bash theme={null}
vet scan -D /path/to/target --exclude 'docs/*'
```
## Multiple Exclusions
Specify multiple path patterns for exclusion:
```bash theme={null}
vet scan -D /path/to/target \
--exclude 'docs/*' \
--exclude 'sub/dir/path/*' \
--exclude '*.test.js'
```
Exclusion patterns use [glob matching](https://github.com/bmatcuk/doublestar) (the `doublestar` library), not regular expressions. Use `*` to match within a single path segment and `**` to match across directories.
## Common Exclusion Patterns
### Documentation and Build Artifacts
```bash theme={null}
vet scan -D . \
--exclude 'docs/*' \
--exclude 'build/*' \
--exclude 'dist/*' \
--exclude 'target/*' \
--exclude '*.generated.*'
```
### Test Files and Directories
```bash theme={null}
vet scan -D . \
--exclude 'test/*' \
--exclude 'tests/*' \
--exclude '*_test.go' \
--exclude '*.test.js' \
--exclude 'spec/*'
```
### Version Control and Dependencies
```bash theme={null}
vet scan -D . \
--exclude '.git/*' \
--exclude 'node_modules/*' \
--exclude 'vendor/*' \
--exclude '.venv/*' \
--exclude '__pycache__/*'
```
### Development Tools
```bash theme={null}
vet scan -D . \
--exclude '.idea/*' \
--exclude '.vscode/*' \
--exclude '*.log' \
--exclude 'tmp/*' \
--exclude 'temp/*'
```
## Advanced Pattern Examples
### File Extension Exclusions
```bash theme={null}
# Exclude specific file types
vet scan -D . \
--exclude '**/*.md' \
--exclude '**/*.txt' \
--exclude '**/*.png' \
--exclude '**/*.jpg'
```
### Environment-Specific Exclusions
```bash theme={null}
# Development environment
vet scan -D . \
--exclude 'dev-tools/**' \
--exclude 'local-config/**' \
--exclude '**/*.dev.*'
# Production build
vet scan -D . \
--exclude 'test/*' \
--exclude 'examples/*' \
--exclude 'dev-dependencies/*'
```
### Complex Pattern Matching
```bash theme={null}
# Exclude multiple similar patterns
vet scan -D . \
--exclude '**/test/**' \
--exclude '**/tests/**' \
--exclude '**/spec/**' \
--exclude '**/mocks/**'
```
## Makefile Integration
```makefile theme={null}
# Makefile with different scan targets
.PHONY: scan-prod scan-dev scan-all
scan-prod:
vet scan -D . \
--exclude 'test/*' \
--exclude 'dev-tools/*' \
--exclude 'docs/*' \
--report-json production-scan.json
scan-dev:
vet scan -D . \
--exclude 'node_modules/*' \
--exclude '.git/*' \
--report-json development-scan.json
scan-all:
vet scan -D . \
--exclude '.git/*' \
--report-json complete-scan.json
```
# Policy as Code
Source: https://docs.safedep.io/reference/policy-as-code
Reference for SafeDep policy files: the filter suite format, the flags that apply it, and the CEL fields available to rules.
A policy file (a *filter suite*) is a YAML document of [CEL](/concepts/cel) rules that Vet evaluates against every package in a scan. For what policies are and why they matter, see [Policy](/concepts/policy). This page is the syntax reference.
This page documents the `--filter-suite` format, Vet's original policy interface. Vet also ships a newer `--policy-suite` engine that uses a different rule schema; see the [Vet repository](https://github.com/safedep/vet) for its format.
## Filter suite format
A filter suite has a `name`, a `description`, and an ordered list of `filters`. Each filter has a `name` and a `value` (a CEL expression). A package matches the suite when any filter's expression evaluates to true.
```yaml theme={null}
name: Enterprise Security Policy
description: Comprehensive security policy for open source components
filters:
- name: critical-vulnerabilities
value: |
vulns.critical.size() > 0
- name: approved-licenses
value: |
!licenses.exists(p, p in ["MIT", "Apache-2.0", "BSD-3-Clause", "ISC"])
- name: minimum-maintenance
value: |
scorecard.scores["Maintained"] < 5
- name: known-malware
value: |
vulns.all.exists(v, v.id.startsWith("MAL-"))
```
Browse complete, ready-to-use suites in the [Vet samples directory](https://github.com/safedep/vet/tree/main/samples/filter-suites).
## Applying a filter suite
Pass the suite to `vet scan` and fail the scan when any package matches:
```bash theme={null}
vet scan -D /path/to/project \
--filter-suite /path/to/policy.yml \
--filter-fail
```
| Flag | Purpose |
| ----------------------- | ------------------------------------------------------- |
| `--filter-suite ` | Evaluate packages against the filter suite in ``. |
| `--filter-fail` | Exit non-zero if any package matches, for CI/CD gating. |
In CI/CD with [vet-action](/governance/integrations/github-code-scanning), pass the file via the `policy` input and set `paranoid: true` to fail the build on a violation.
## Evaluation
Filters are evaluated as an ordered list. Vet stops at the first match per package and reports it as a violation. Order filters from most specific to least specific.
## CEL fields
The expression in each filter's `value` is written in [CEL](/concepts/cel). These fields are available:
* `pkg` - package metadata: `pkg.ecosystem`, `pkg.name`, `pkg.version`
* `vulns.all`, `vulns.critical`, `vulns.high`, `vulns.medium`, `vulns.low` - vulnerability arrays; each item has an `id` (e.g. `vulns.all.exists(v, v.id.startsWith("MAL-"))`)
* `licenses` - array of SPDX license identifiers
* `scorecard.score` - aggregate OpenSSF Scorecard score; `scorecard.scores["Check-Name"]` - per-check score (e.g. `"Maintained"`, `"Dangerous-Workflow"`, `"Token-Permissions"`)
* `projects` - source project info (e.g. `projects.exists(p, p.type == "GITHUB" && p.stars < 10)`)
Common operations: `size()` (array length), `exists(item, condition)`, `in` (membership), `contains()` (string contains).
What policies are and why they matter.
The expression syntax rules are built from.
Query scan results with one-off CEL expressions.
Ready-to-use filter suites in the Vet repository.
# SafeDep Cloud SQL
Source: https://docs.safedep.io/reference/sql-query
Run SQL-like queries against your SafeDep Cloud data with the safedep CLI.
The `safedep query` command runs SQL-like queries against SafeDep Cloud's analytics surface:
the packages, projects, endpoints, events, and security findings collected across
your tenant, enriched with global threat intelligence (vulnerabilities, EPSS,
CISA KEV, OpenSSF Scorecard, and more).
## Prerequisites
[Install the CLI](https://github.com/safedep/cli#safedep-cli) and sign in to SafeDep Cloud:
```bash theme={null}
safedep auth login
safedep auth status # confirm tenant and OAuth token
```
SafeDep scopes every query to your tenant. You never write a tenant filter
yourself.
## Your first query
Start by seeing what you can query. List the tables, then inspect one to view its
columns, capability flags, and join edges:
```bash theme={null}
safedep query schema list # all tables + one-line descriptions
safedep query schema show projects # one table: columns, flags, joins
```
See [Discovering the schema](#discovering-the-schema) for the full set of schema
commands.
Now run a query. It is mostly SQL. Select some columns, filter with `WHERE`, sort
with `ORDER BY`:
```bash theme={null}
safedep query exec --sql "
SELECT projects.name
FROM projects
WHERE projects.origin_source = 'SOURCE_GITHUB'
ORDER BY projects.name"
```
```
projects.name
safedep/vet
safedep/pmg
safedep/control-tower
...
```
Two things make this different from a generic database:
1. **Write every column as `table.column`** using the real table name. No table
aliases, no bare column names. Result aliases via `AS` work
(`COUNT(...) AS n`, then `ORDER BY n`).
2. **Every query must filter on an indexed column or a bounded time range.** Here
`projects.origin_source` is indexed, so the query is accepted. This rule keeps
queries cheap; the schema marks which columns are indexed.
`safedep query exec` reads the statement from `--sql`, `--sql-file`, or stdin:
```bash theme={null}
safedep query exec --sql "SELECT projects.name FROM projects WHERE projects.origin_source = 'SOURCE_GITHUB'"
safedep query exec --sql-file ./query.sql
echo "SELECT packages.name FROM packages WHERE packages.ecosystem = 'ECOSYSTEM_NPM'" | safedep query exec
```
## How the data is organized
Tables come in two kinds:
* **Tenant tables** hold *your* data: `projects`, `project_versions`, `boms`,
`packages`, `endpoints`, the `component_*` finding tables, and the event tables
(`inventory_events`, `package_guard_events`).
* **Join-only enrichment tables** hold *global* reference data shared across all
tenants: `vulnerabilities`, `epss`, `kev`, `licenses`, `malware_analysis`,
`open_source_packages`, `open_source_projects`, `scorecards`,
`scorecard_checks`, `terraform_providers`.
The most important rule: **every query must reference at least one tenant table.**
You reach an enrichment table only by joining out from a tenant table.
A typical query walks from a first-party project, through its bill of materials,
to a package, to a finding, and out to global enrichment:
```
projects -> project_versions -> boms -> packages -> component_vulnerabilities -> vulnerabilities -> epss
(tenant) (tenant) (tenant) (tenant) (tenant finding) (enrichment) (enrichment)
```
## What SQL we support
| Feature | Notes |
| ---------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `SELECT` | Columns must be qualified `table.column`. |
| `WHERE` | `=`, `!=`, `<`, `<=`, `>`, `>=`, `AND`, `OR`. Enums filtered by name. |
| `LIKE` | Prefix only: `'name%'`. Leading wildcards (`'%name'`) are rejected. |
| `JOIN` | Needs an `ON` clause, but its contents are ignored: the catalog applies a predefined join. Only join tables connected by an edge. |
| `GROUP BY` | Only columns flagged `groupable`. |
| `HAVING` | Filter on aggregates after grouping. |
| `ORDER BY` | Including by an aggregate alias. |
| Aggregates | `count`, `sum`, `avg`, `min`, `max`. |
Not supported: subqueries, CTEs (`WITH`), `UNION` and set operations, window
functions, casts, arbitrary functions, and multiple statements.
## Discovering the schema
You do not need to memorize tables or columns. The schema is self-describing, and
this is the entry point for both humans and AI agents.
```bash theme={null}
safedep query schema list # table names + one-line descriptions
safedep query schema show packages # one table: columns, flags, joins
safedep query schema get # everything: all tables, edges, rules
safedep query schema get -o json # same, machine-readable
```
Each column carries capability flags that tell you what it can do:
| Flag | Meaning |
| -------------- | ------------------------------------------------------ |
| `selectable` | May appear in `SELECT`. |
| `filterable` | May appear in `WHERE`. |
| `groupable` | May appear in `GROUP BY`. |
| `aggregatable` | May be wrapped in `sum`/`avg`/`min`/`max`. |
| `indexed` | Satisfies the "must filter on an indexed column" rule. |
`schema get` also prints the join edges (which tables connect, and their
cardinality) and the usage rules with example queries, so one call gives you
everything needed to write a valid query.
## Letting an AI agent write the queries
Point your AI coding agent at the `safedep` CLI. The schema is self-describing
and server errors come back verbatim, so the agent can discover tables, write a
query, and self-correct on its own.
Prompt:
```text theme={null}
You can query SafeDep Cloud data with the `safedep` CLI (a constrained SQL
dialect). Run `safedep query schema get -o json` to discover the tables,
columns, rules, and join edges. Write a query, run it with
`safedep query exec -o json --sql ""`, and self-correct from the
verbatim server errors. Rules and worked examples:
https://docs.safedep.io/reference/sql-query
```
## Examples
Anchor on the indexed `ecosystem` column (an `OR` across all severities would not
satisfy the index rule):
```bash theme={null}
safedep query exec --sql "
SELECT packages.ecosystem, vulnerabilities.severity_rating,
COUNT(DISTINCT vulnerabilities.vuln_id) AS num_vulns
FROM packages
JOIN component_vulnerabilities ON component_vulnerabilities.component_id = packages.id
JOIN vulnerabilities ON vulnerabilities.vuln_id = component_vulnerabilities.vulnerability_id
WHERE packages.ecosystem = 'ECOSYSTEM_NPM'
GROUP BY packages.ecosystem, vulnerabilities.severity_rating
ORDER BY num_vulns DESC"
```
```bash theme={null}
safedep query exec --sql "
SELECT packages.name, COUNT(DISTINCT vulnerabilities.vuln_id) AS critical_vulns
FROM packages
JOIN component_vulnerabilities ON component_vulnerabilities.component_id = packages.id
JOIN vulnerabilities ON vulnerabilities.vuln_id = component_vulnerabilities.vulnerability_id
WHERE vulnerabilities.severity_rating = 'CRITICAL'
GROUP BY packages.name
ORDER BY critical_vulns DESC" --limit 15
```
EPSS is join-only enrichment, so anchor in the tenant `packages` table and join
out. Sorting by EPSS surfaces the CVEs most likely to be exploited:
```bash theme={null}
safedep query exec -o json --sql "
SELECT packages.name, vulnerabilities.cve_id, epss.epss, epss.percentile
FROM packages
JOIN component_vulnerabilities ON component_vulnerabilities.component_id = packages.id
JOIN vulnerabilities ON vulnerabilities.vuln_id = component_vulnerabilities.vulnerability_id
JOIN epss ON epss.cve = vulnerabilities.cve_id
WHERE vulnerabilities.severity_rating = 'CRITICAL'
ORDER BY epss.epss DESC"
```
```
packages.name cve_id epss percentile
org.springframework:spring-webmvc CVE-2022-22965 0.944 0.99985
org.apache.logging.log4j:log4j-core CVE-2021-44228 0.944 0.99964
next CVE-2025-29927 0.921 0.99724
```
`kev` is the CISA Known Exploited Vulnerabilities catalog. Use a bounded range on
the indexed `kev.date_added` to anchor the query:
```bash theme={null}
safedep query exec --sql "
SELECT packages.name, packages.ecosystem, vulnerabilities.cve_id, vulnerabilities.severity_rating
FROM packages
JOIN component_vulnerabilities ON component_vulnerabilities.component_id = packages.id
JOIN vulnerabilities ON vulnerabilities.vuln_id = component_vulnerabilities.vulnerability_id
JOIN kev ON kev.cve_id = vulnerabilities.cve_id
WHERE kev.date_added >= '2018-01-01T00:00:00Z'
ORDER BY packages.name"
```
`is_malware` is not indexed, so anchor on the indexed `detected_at` timestamp:
```bash theme={null}
safedep query exec --sql "
SELECT packages.name, packages.ecosystem, packages.version,
component_malicious_packages.is_malware,
component_malicious_packages.is_verified
FROM packages
JOIN component_malicious_packages ON component_malicious_packages.analysis_id = packages.id
WHERE component_malicious_packages.detected_at >= '2020-01-01T00:00:00Z'
ORDER BY packages.name"
```
Walk from packages through `component_licenses` to the global `licenses` catalog
for SPDX metadata. Anchor on the indexed `packages.ecosystem`:
```bash theme={null}
safedep query exec --sql "
SELECT packages.name, component_licenses.license_code, licenses.spdx_license_id
FROM packages
JOIN component_licenses ON component_licenses.license_code = packages.id
JOIN licenses ON licenses.license_code = component_licenses.license_code
WHERE packages.ecosystem = 'ECOSYSTEM_NPM'
ORDER BY packages.name"
```
`endpoints` are the machines and CI runners reporting to your tenant. The
timestamp columns are indexed, so a bounded range anchors the query:
```bash theme={null}
safedep query exec --sql "
SELECT endpoints.identifier, endpoints.endpoint_type, endpoints.trust_level, endpoints.last_sync_at
FROM endpoints
WHERE endpoints.last_sync_at >= '2025-01-01T00:00:00Z'
ORDER BY endpoints.last_sync_at DESC"
```
```
endpoints.identifier endpoint_type trust_level last_sync_at
Vignesh 0 1 2026-06-05T07:23:03Z
CICD_GHA_SAFEDEP_CLI 0 1 2026-06-05T06:11:20Z
macbookpro.lan 0 1 2026-06-04T08:54:03Z
```
The `endpoint_type` and `trust_level` enums are not groupable, so use `WHERE`
filters and listings rather than `GROUP BY` for endpoints. Filter them by name
(`endpoint_type = 'CI_RUNNER'`).
For "which endpoints..." questions you want a de-duplicated list, not one row per
event. `endpoints.identifier` is not groupable, so use `SELECT DISTINCT`:
```bash theme={null}
safedep query exec --sql "
SELECT DISTINCT endpoints.identifier
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_action = 'PMG_PACKAGE_ACTION_BLOCKED'"
```
```
endpoints.identifier
macbookpro.lan
pop-os
Sahils-MacBook-Pro.local
Vignesh
```
Join Package Guard events to the endpoint that produced them, newest first, for a
full audit trail:
```bash theme={null}
safedep query exec --sql "
SELECT endpoints.identifier,
package_guard_events.package_name,
package_guard_events.package_ecosystem,
package_guard_events.tool_name,
package_guard_events.timestamp
FROM package_guard_events
JOIN endpoints ON endpoints.id = package_guard_events.invocation_id
WHERE package_guard_events.package_action = 'PMG_PACKAGE_ACTION_BLOCKED'
ORDER BY package_guard_events.timestamp DESC" --limit 50
```
```
endpoints.identifier package_name package_ecosystem tool_name timestamp
pop-os speed5 2 pmg 2026-05-27T10:30:34Z
pop-os lab-helper 2 pmg 2026-05-27T10:28:14Z
Sahils-MacBook-Pro.local martinez-polygon-clipping-tony 2 pmg 2026-05-07T10:20:55Z
Sahils-MacBook-Pro.local prettier 2 pmg 2026-05-07T07:40:20Z
Sahils-MacBook-Pro.local fast-check 2 pmg 2026-05-07T07:39:50Z
```
The `package_ecosystem` enum renders as its stored number in table output
(`2` = `ECOSYSTEM_NPM`, `3` = `ECOSYSTEM_PYPI`); use `-o json` for the enum name.
`inventory_events` captures items discovered by `vet` on each endpoint. Filter on
the indexed `item_kind` to find AI-related items:
```bash theme={null}
safedep query exec --sql "
SELECT inventory_events.item_identity, inventory_events.app, endpoints.identifier
FROM inventory_events
JOIN endpoints ON endpoints.id = inventory_events.app
WHERE inventory_events.item_kind = 'INVENTORY_ITEM_KIND_MCP_SERVER'
ORDER BY inventory_events.item_identity" --limit 50
```
## Output modes
Select with `-o` / `--output`. When omitted, the CLI auto-detects the format: a
rendered table for an interactive terminal, plain text when piped.
| Mode | Use it for |
| ------- | --------------------------------------------------------------------------------------------------------------- |
| `table` | Human reading. Adds a footer: ` \| ~ cost \| ms`, plus a `next page` hint when more rows exist. |
| `plain` | Pipelines. Tab-separated header and rows, no footer. |
| `json` | Scripts and agents. Typed columns, planner stats, and `next_page_token`. |
JSON shape:
```json theme={null}
{
"columns": [ { "name": "packages.name", "type": "STRING" } ],
"rows": [ { "packages.name": "django" } ],
"count": 1,
"next_page_token": "",
"stats": { "estimated_cost": 1243.7, "estimated_rows": 1, "elapsed_ms": 9 }
}
```
## Pagination
Pagination is caller-driven; the CLI does not auto-iterate. Fetch the first page,
then re-run the same query with the returned token:
```bash theme={null}
TOK=$(safedep query exec -o json --sql "SELECT packages.name FROM packages WHERE packages.ecosystem = 'ECOSYSTEM_NPM'" | jq -r .next_page_token)
safedep query exec --sql "SELECT packages.name FROM packages WHERE packages.ecosystem = 'ECOSYSTEM_NPM'" --page-token "$TOK"
```
## Troubleshooting
| Error | Cause | Fix |
| ---------------------------------------------------------------- | --------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `queries must reference at least one tenant table` | Query starts from a join-only enrichment table. | Anchor `FROM` on a tenant table (for example `packages`) and join out. |
| `query must filter on an indexed column or a bounded time range` | No effective indexed filter, or an `OR` across all enum values. | Add an equality filter on an indexed column, or a bounded timestamp range. |
| `column "X" is not groupable` | `GROUP BY` on a non-groupable column. | Group only by `groupable` columns; use `SELECT DISTINCT` for "which" questions. |
| `column "X" is not aggregatable` | `sum`/`avg`/`min`/`max` on a column without the flag. | Use `count(column)`, or aggregate only `aggregatable` columns. |
| `LIKE` pattern rejected | Leading wildcard (`'%name'`). | Use a prefix pattern (`'name%'`). |
| Duplicate rows | Join fan-out from `many_to_one` edges. | Use `COUNT(DISTINCT ...)` or de-duplicate client-side. |
| Unknown table or column | Name not in the schema. | Re-run `safedep query schema get` and use exact names. |
What SafeDep Cloud collects to build the data you query.
Sign in with `safedep auth login` before running queries.
Filter findings locally with Vet's CEL query language.
Programmatic access to SafeDep's APIs.