ossprey is the command-line scanner for the Ossprey
supply-chain malware platform. It catalogues your project's dependencies into
an OSSBOM, submits it to the Ossprey API, and fails the build if any of those
packages are known to contain malware.
You need an Ossprey account to run scans. Sign up for a free account at ossprey.com, then either run
ossprey loginto sign in via your browser, or provide an API key viaOSSPREY_API_KEY(see Authentication). The--localand--dry-run-*modes work without credentials.
Today the CLI covers Python and JavaScript projects via static parsing of the manifests and lockfiles already in your repo — no package installs, no sandbox, no virtualenv.
- Install — Linux / macOS · Windows · manual download · from source · updating
- Quick start
- Usage
- Authentication
check— scan named packages- Package-manager forwarder — check before install for
npm/yarn/pip/poetry/uv - How to make Ossprey scan on all package manager commands — shell aliases so you don't type
osspreyfirst - Supported ecosystems
- CI usage
- Output
- Status
- Support
curl -fsSL https://github.com/ossprey/ossprey-cli/releases/latest/download/install.sh | sudo shThe script detects your OS/arch, downloads the matching binary, verifies its
sha256, and installs it to /usr/local/bin/ossprey.
Override the defaults with env vars:
# Pin a specific version
curl -fsSL https://github.com/ossprey/ossprey-cli/releases/latest/download/install.sh \
| OSSPREY_VERSION=v0.1.0 sudo -E sh
# Install to a user-writable dir (no sudo)
curl -fsSL https://github.com/ossprey/ossprey-cli/releases/latest/download/install.sh \
| OSSPREY_INSTALL_DIR=$HOME/.local/bin shRuns natively on Windows — no WSL, no admin rights needed. From any PowerShell prompt:
irm https://github.com/ossprey/ossprey-cli/releases/latest/download/install.ps1 | iexOr from cmd.exe:
powershell -ExecutionPolicy Bypass -Command "irm https://github.com/ossprey/ossprey-cli/releases/latest/download/install.ps1 | iex"The script detects your architecture, downloads the matching ossprey.exe,
verifies its sha256, installs it to %LOCALAPPDATA%\Programs\ossprey, and
adds that directory to your user PATH (open a new terminal to pick it up).
Override the defaults with env vars:
# Pin a specific version
$env:OSSPREY_VERSION = 'v0.1.0'
irm https://github.com/ossprey/ossprey-cli/releases/latest/download/install.ps1 | iex
# Custom install location
$env:OSSPREY_INSTALL_DIR = 'C:\tools\ossprey'
irm https://github.com/ossprey/ossprey-cli/releases/latest/download/install.ps1 | iexGrab the binary direct from the releases page:
| Asset | Platform |
|---|---|
ossprey-linux-amd64 |
Linux x86_64 |
ossprey-linux-arm64 |
Linux arm64 |
ossprey-darwin-amd64 |
macOS Intel |
ossprey-darwin-arm64 |
macOS Apple Silicon |
ossprey-windows-amd64.exe |
Windows x86_64 |
ossprey-windows-arm64.exe |
Windows arm64 |
chmod +x and drop it on your PATH. Each asset ships with a .sha256
sidecar for verification. Pin a specific tag by replacing latest/download
with download/<tag> in the URL.
git clone https://github.com/ossprey/ossprey-cli.git
cd ossprey-cli
make tidy # first time
make build # produces bin/osspreyRequires Go 1.25+.
The release build (make build) ships with -trimpath -ldflags="-s -w" for a
~16 MB binary. Use make build-debug for an unstripped ~21 MB build with
symbols.
Once installed, the CLI can update itself:
ossprey update # update in place to the latest release
ossprey update --check # just report whether an update is available
ossprey update --version v0.2.0 # install a specific version (up- or downgrade)
ossprey update --force # reinstall even if already on the target versionupdate downloads the release binary matching your OS/architecture, verifies
its sha256, and atomically replaces the running executable. If the binary
lives in a root-owned directory (the /usr/local/bin default on Linux/macOS),
run sudo ossprey update; the Windows default (%LOCALAPPDATA%\Programs\ossprey)
is user-writable, so no elevation is needed.
# Interactive: log in once via your browser...
ossprey login
ossprey scan .
# ...or non-interactive (CI): use an API key
export OSSPREY_API_KEY=ospy_...
ossprey scan .Exit codes:
0— no malware found,--localdump, or scan skipped by the API (e.g. quota exhausted)1— malware found, or the scan itself failed (bad path, catalog error, API/network error, missing key)
If you need to distinguish "clean" from "errored" in CI, check stderr or parse the OSSBOM emitted via -o.
Get an API key at dashboard.ossprey.com.
| Command | What it does |
|---|---|
ossprey scan [path] |
Catalogue a directory, submit the OSSBOM, fail on malware. path defaults to .. |
ossprey check -e <pypi|npm> <pkg>... |
Check packages by name, no project needed. |
ossprey npm|yarn|pip|poetry|uv ... |
Check, then run the real package manager. Blocks the install on malware. |
ossprey login |
Browser login via Auth0. Stores tokens locally. |
ossprey whoami |
Show who the stored login belongs to. |
ossprey logout |
Remove the stored login. |
ossprey update |
Replace the binary with a newer release. |
ossprey completion <shell> |
Print a completion script for bash, zsh, fish or PowerShell. |
ossprey --version |
Print the CLI version. |
To have the forwarders run without typing ossprey every time, see
how to make Ossprey scan on all package manager commands.
ossprey scan [path] [flags]
path defaults to the current directory.
| Flag | Description |
|---|---|
-o, --output <file> |
Write the OSSBOM JSON to <file> (in addition to running the scan). |
-v, --verbose |
Verbose logging. |
--local |
Catalogue only. Dump the OSSBOM to stdout and exit — no API submission, no malware verdict. |
--no-version-lookup |
Don't query the registry to resolve unpinned dependencies; leave them versionless. |
--url <url> |
Override the Ossprey API URL (default https://api.ossprey.com). |
--api-key <key> |
Provide the API key on the command line instead of an env var. |
--dry-run-safe |
Skip the API; report an empty vulnerability list. |
--dry-run-malicious |
Skip the API; inject a test finding against the first component. |
Two ways to authenticate:
Browser login (interactive use). Run ossprey login once — it opens your
browser, you confirm a one-time code, and the CLI stores the resulting Auth0
tokens locally (~/.config/ossprey/credentials.json on Linux, or the
platform's user config dir; override with OSSPREY_CONFIG_DIR). Scans then
authenticate automatically and tokens refresh silently. ossprey whoami
shows the current login; ossprey logout removes it.
API key (CI / non-interactive use). Get a key at dashboard.ossprey.com and provide it via flag or env var.
Credentials are resolved in order:
--api-keyflag (an explicit per-invocation choice)- the stored
ossprey loginsession (JWT) OSSPREY_API_KEYenv varAPI_KEYenv var
A logged-in session therefore wins over API keys exported in the shell; drop
the login with ossprey logout (or pass --api-key) to force key auth. The
credential also picks the API surface: JWTs call the /dashboard/v1 routes,
API keys the /public/v1 routes — same endpoints, same behaviour.
--local, --dry-run-safe and --dry-run-malicious don't talk to the API
and don't need credentials.
ossprey login targets the production Ossprey tenant by default; point it at
another environment with --auth0-domain, --client-id and --audience
flags or the matching OSSPREY_AUTH0_DOMAIN / OSSPREY_AUTH0_CLIENT_ID /
OSSPREY_AUTH0_AUDIENCE env vars. For the QA environment:
ossprey login \
--auth0-domain auth.qa.ossprey.com \
--client-id oT9sXzeqPTyZnRDzpgQ3YjUfd11Xj0Mh \
--audience https://api.qa.ossprey.com
ossprey scan . --url https://api.qa.ossprey.comThe Auth0 application behind the client ID must be a Native app with the Device Code and Refresh Token grants enabled, and the API must have Allow Offline Access on.
Scan one or more packages by name without a project on disk:
ossprey check --eco-system <pypi|npm> <name[@version]>...
ossprey check -e pypi requests@2.31.0
ossprey check -e npm lodash@4.17.21 react@18.2.0When a version is omitted, the latest published version is resolved from the
registry (PyPI / npm) and checked. Both name@version and pip's
name==version forms are accepted.
| Flag | Description |
|---|---|
-e, --eco-system <pypi|npm> |
Package ecosystem (required). |
--url <url> |
Override the Ossprey API URL. |
--api-key <key> |
API key (or env var). |
--dry-run-safe |
Skip the API; report an empty vulnerability list. |
--dry-run-malicious |
Skip the API; inject a test finding against the first package. |
Exit codes match scan: 1 on a malware verdict or error, 0 otherwise.
Wrap an install so packages are checked before they hit your machine. If
any are flagged, the install is blocked (exit 1) and the real package manager
is never invoked; otherwise the command is forwarded unchanged.
ossprey npm install foo@1.2.3 bar@2.0.0 # checks each named package
ossprey yarn add foo@1.2.3
ossprey pip install foo==1.2.3
ossprey poetry add foo
ossprey uv pip install foo==1.2.3Supported managers: npm, yarn, pip, poetry, uv. Non-install
subcommands (npm run, pip list, …) are forwarded straight through with no
check.
Two modes, picked automatically:
- Named packages (
ossprey npm install foo bar,ossprey pip install foo==1 bar): every package named on the command line is checked. Multiple packages, flags, flag-values, local paths, archives and VCS/URL targets are all handled — only the real registry packages are checked, the rest are noted and forwarded. Transitive dependencies are not resolved here; runossprey scanafter install for full-tree coverage. - Manifest install (bare
ossprey npm install,npm ci,yarn install,poetry install,uv sync, orpip install -r requirements.txt): no packages are named, so the manager installs from the project's manifest/lockfile. The forwarder scans the current directory and checks every declared dependency before forwarding — it does not fall through unchecked.
If the registry can't be reached to resolve an unpinned named version, that package is skipped (fail-open) so a registry outage never blocks development. An install whose only targets are local paths or URLs (nothing checkable and no manifest to scan) is forwarded with a warning.
Flag parsing is disabled so every argument reaches the real manager, which
means the forwarder has no --api-key or --url of its own. It reads:
OSSPREY_API_KEY— API keyOSSPREY_API_URL— override the API URL (defaulthttps://api.ossprey.com)
A session from ossprey login also counts, and takes precedence over
OSSPREY_API_KEY, so on your own machine the forwarder usually needs no
environment at all.
The forwarder only runs when somebody remembers to type ossprey first. One
alias per manager removes that step in your own terminal.
Bash or Zsh, in ~/.bashrc / ~/.zshrc:
for mgr in npm yarn pip poetry uv; do alias "$mgr=ossprey $mgr"; doneFish, in ~/.config/fish/config.fish:
for mgr in npm yarn pip poetry uv
alias $mgr "ossprey $mgr"
endPowerShell, in $PROFILE (Set-Alias can't carry an argument, so these are
functions):
function npm { ossprey npm @args }
function yarn { ossprey yarn @args }
function pip { ossprey pip @args }
function poetry { ossprey poetry @args }
function uv { ossprey uv @args }Open a new shell to pick them up. There's no recursion to worry about:
ossprey npm resolves the real npm through PATH rather than through your
shell, so the alias doesn't apply a second time. Wrap only the five managers
above, since ossprey <anything else> isn't a command.
An intercepted install needs credentials exactly like ossprey scan does, so
run ossprey login once (or export OSSPREY_API_KEY) before relying on the
aliases. With neither, the install stops on a credentials error rather than
being quietly forwarded.
To check they took, run type npm. Non-install commands (npm run build, pip list, poetry run pytest) go straight through untouched, so an
ordinary-looking npm --version means the handoff works. An install prints to
stderr before it forwards:
$ npm install left-pad
ossprey: no malware found, forwarding to npm
added 1 package in 525msIf a check comes back dirty you get the finding, a blocked line naming the
command, and an exit code of 1. The real manager never starts.
An alias inherits the forwarder's scope: an install that names packages checks
those packages, not their dependencies. Run ossprey scan afterwards for the
full tree.
Skip the check for one command by calling the manager directly:
command npm install ./local-tarball.tgz # or \npm install ...In PowerShell, & (Get-Command npm -CommandType Application) install ... steps
around the profile function. To undo the whole thing, delete the alias lines
and open a new shell.
The limit of aliases is that they exist only in interactive shells. make setup, a package.json script, a CI job, and whatever your editor spawns in
the background all miss them. Covering those takes a real executable earlier on
PATH, not a shell feature.
Python and JavaScript, via syft's static catalogers.
| Ecosystem | Files parsed |
|---|---|
| Python | requirements.txt, Pipfile.lock, poetry.lock, uv.lock, pdm.lock, setup.py, pyproject.toml, wheel / egg metadata |
| JavaScript | package.json, package-lock.json, yarn.lock, pnpm-lock.yaml |
The CLI never executes your package manager. If your repo has only a manifest and no lockfile, expect direct deps only — supply a lockfile for full transitive coverage.
When a dependency's version can't be determined — an unpinned range in a
manifest (click = "^8") with no lockfile or resolver to pin it against — the
scan defaults that component to the latest published version from its
registry (PyPI / npm): the version a fresh install would pull today. Registry
lookups fail open, so a component whose version can't be resolved (offline,
private, or removed package) is left unversioned rather than dropped or failing
the scan.
To skip these lookups for a fully offline catalog, pass --no-version-lookup
(or set OSSPREY_RESOLVE_LATEST=0, which also covers the package-manager
forwarders, whose args are passed through untouched). Unpinned components are
then left versionless.
Typical GitHub Actions step:
- name: Ossprey scan
env:
OSSPREY_API_KEY: ${{ secrets.OSSPREY_API_KEY }}
run: ossprey scan .The CLI exits non-zero on a malware verdict, which fails the workflow.
ossprey scan prints No malware found on success or one Error: WARNING: <pkg>:<ver> contains malware. Remediate this immediately line per finding on
failure.
Pass -o sbom.json to also write the full OSSBOM JSON (components +
vulnerabilities) to disk, or --local to emit it to stdout instead of
calling the API.
Pre-1.0. The CLI surface, OSSBOM schema, and API contract are stable enough for production use; expect additive changes only.
- Docs: docs.ossprey.com
- Issues: github.com/ossprey/ossprey-cli/issues
- Email: support@ossprey.com