What Risk Guard is
Risk Guard examines the third-party packages a project depends on and scores how risky they are to rely on. It checks your own source and your direct dependencies, and gathers everything into a single report — run it on your machine, or automatically on every pull request.
Ecosystems: npm, PyPI, RubyGems.
Open source on GitHub github.com/Risk-Guard/oss-risk-guard · MITHow it works
-
1
Inventory. Risk Guard reads your repository and builds an SBOM (SPDX or CycloneDX) of your project's packages. You can export it on its own, too.
-
2
Score. Each check runs against your source and each direct dependency.
-
3
Report. Results merge into one SARIF report — the standard format GitHub and GitLab render inline, so findings appear where you review code.
Optional — to enforce it on every change
-
4
Define policy. One
.risk-guard.ymlin git sets which findings block, warn, or are acknowledged. A sensible default ships, so this is optional. -
5
Protect PRs. Run it in CI in
activemode and risky dependencies fail the check before they merge.
Not a vulnerability scanner
Risk Guard doesn't scan for CVEs and doesn't replace the tool that does — run it alongside your vulnerability scanner, such as Snyk, Dependabot, Trivy, Grype, or OSV-Scanner, which catch known vulnerabilities in the versions you already ship. Risk Guard scores something they don't: supply-chain and provenance risk — who maintains a package, whether its source can be verified, and what it does at install time.
What it scores
Four areas of supply-chain risk:
- Security
-
Missing security practices and unsafe configuration, such as a project with
no security policy or a package that points to an insecure
(
http://) source. - License compliance
- Packages with missing or undeclared licenses.
- Maintenance & continuity
- Abandoned or stale projects, single-maintainer packages, or source code that has drifted far ahead of its last published release.
- Provenance
- Whether a package is what it claims to be: mismatches between a package and its stated source, install-time scripts, and downloaded artifacts that fail hash verification.
Much of this risk exists before any CVE is filed — and sometimes a CVE is never filed at all. A package taken over by a new maintainer, or quietly abandoned, is a real risk that vulnerability databases won't describe.
Why it works this way
It's preventative
Risk Guard runs at the pull request and can fail the build before a risky dependency is merged. The decision happens at adoption time — you keep the dependency out, rather than discovering it after it's already in production. A dependency you never merge is one you never have to triage, patch, or respond to.
Because its checks score intrinsic risk signals — abandonment, provenance, install scripts, licensing — it can flag a problem that no vulnerability has been published for yet.
It doesn't overload your developers
Each finding belongs to a risk category, and the default policy gives each
category a severity. Out of the box, only the critical
category (for example, a tampered artifact) and continuity problems in your
direct dependencies block the build — everything else is a warning. It's a short,
readable file you can see and change, kept in git: owners set it once, developers
don't each re-triage, and a finding you acknowledge stops blocking so it doesn't
resurface on every pull request. Findings appear inline on the diff, at review
time.
Reading the results
Each finding names a subject (a package, or your own repository), describes the issue, and references the rule that produced it and where it applies. A local run summarizes them:
| Severity | Subject | Finding | Rule |
|---|---|---|---|
| ⚠️ warning | nokogiri (rubygems) | native extension runs at install time | PACKAGE_INSTALL_SCRIPTS |
| ⚠️ warning | your repository | a dependency's registry URL doesn't match its source repo | PACKAGE_SOURCE_URL_MISMATCH |
| ⬜ info | is-odd@3.0.1 (npm) | last repository commit was 2919 days ago | SOURCE_REPO_ABANDONED |
Every finding has a severity —
blocking,
warning, or
ignore — assigned by a
default policy per risk category. Your .risk-guard.yml
overrides it. The run's mode is
the master switch:
active— fail the build on blocking findings (the default).no-fail— run and report everything, but never fail the build.silent— run, skip the inline annotations, never fail.disabled— skip the scan entirely.
Tuning it
Everything lives in one file at the repository root. Override a severity, or acknowledge a finding you've accepted — it stays in the report but no longer blocks:
# .risk-guard.yml
version: 2
workflow:
mode: active
severity:
check/PACKAGE_INSTALL_SCRIPTS: warning # downgrade one check
expected_failures: # accept a finding; still reported
package/npm/lodash:
checks: [PACKAGE_STALE_RELEASE]
reason: "Pinned deliberately; upgrade tracked in #482"
expires: 2026-09-01T00:00:00Z
The configuration reference covers the full selector grammar, per-environment rules, performance, and private registries.
Privacy
Risk Guard reads public information from the package registries (npm, PyPI, RubyGems) and from projects' source repositories, so it needs network access to them. It runs entirely on your machine or CI runner — your code and your dependency list are never uploaded to any Risk Guard server.
Using it
Install
Linux and macOS — verifies a checksum before installing:
curl -fsSL https://risk-guard.github.io/oss-risk-guard/get.sh | sh
With a Go toolchain installed:
go install github.com/Risk-Guard/oss-risk-guard/cmd/risk-guard@latest
Download an archive for your platform from the
releases page
and put risk-guard on your PATH.
Then scan your repository — init
runs a first scan and writes a starter .risk-guard.yml
from the findings:
risk-guard init
In CI
The Risk Guard Action — findings appear in the Security tab and inline on pull requests:
- uses: actions/checkout@v6
- uses: Risk-Guard/action@v1
The Risk Guard component — findings appear in the merge request widget and the Security tab:
include:
- component: $CI_SERVER_FQDN/risk-guard/components/scan@1.0.3