# Dependency advisories

> How inspect.software matches a repository's resolved dependencies against the OSV advisory database — severity, fix versions, coverage, and the limits of the signal. 30% of the Security category.


**Dependency advisories** asks a narrow, checkable question: do the
dependency versions this repository actually resolves to appear in any
published security advisory?

It carries **20% of the [Security](/wiki/security) category**, alongside
[security posture](/wiki/security-posture) at 80%.

The weight is deliberately modest. Calibration against sixteen widely used
packages found fifteen with no known advisories at all: well-maintained
projects genuinely do not ship vulnerable dependencies, so a clean result here
is the norm rather than a distinction. A signal that is nearly always full
marks must not outweigh the one that actually varies.

## What is measured: what a consumer installs

For a repository that **publishes a package**, the assessed set is that
package's **runtime dependency closure** — every package an installation
actually pulls in, direct and transitive — resolved from the open
[deps.dev](https://deps.dev) index. The report names the exact package and
version assessed.

Only where a repository publishes nothing does the inspection fall back to the
repository's **own dependency graph**, computed by the hosting platform from
committed manifests and lockfiles. That graph is a different thing: it also
contains development and test pins, which no installer ever downloads. Reports
in this scope say so.

Runtime closures are currently resolvable for **npm, PyPI, crates.io and
Maven** packages. Repositories publishing to Go, NuGet, RubyGems, Packagist or
Hex are assessed against their repository graph until the index covers them —
a limit of the data source, not a judgement about those ecosystems.

The distinction is not academic. Flask's repository graph contains old
versions of Werkzeug and Jinja that the project deliberately pins to test
against; they carry advisories, and none of them reaches anyone who installs
Flask. The published closure is six packages, none affected. Measuring the
first would report on Flask's test matrix while appearing to report on Flask.

Either set is matched against **[OSV](https://osv.dev)**, the open advisory
database maintained by the Open Source Security Foundation, which aggregates
GitHub Security Advisories, PYSEC, RUSTSEC, Go's vulnerability database and
others behind one schema. OSV is free, public, and versioned — the same
properties that make the Scorecard a defensible backbone for
[security posture](/wiki/security-posture).

The same query also returns packages reported as **malicious**. Those are not
vulnerabilities and are not counted here: they carry no severity and no fixed
version, and are reported and scored separately as
[malicious dependencies](/wiki/malicious-dependencies). A package appearing
there does not also appear in the counts on this page.

## What is reported

For each affected package: the resolved version, whether it is a direct or an
indirect dependency, the worst severity across its advisories, how many
advisories apply, their identifiers, and — where the advisory states one —
**the version the problem was fixed in**.

Severity comes from the advisory database's own label. Records that carry no
severity, common for PYSEC entries, are reported as *unknown* rather than
assigned a guessed value.

## How it scores

Three components:

| Component | Weight |
| --------- | -----: |
| Direct dependencies free of known advisories | 35 |
| Indirect dependencies free of known advisories | 25 |
| No advisories left outstanding | 40 |

Direct dependencies weigh more than transitive ones because they are the
project's own declared choice; transitive ones arrive with them.

**Severity is the published CVSS base score**, not a coarse label. Each
affected package contributes its score on a 0–1 scale. The database's own
`critical`/`high`/`moderate`/`low` wording is used only where an advisory
publishes no CVSS vector.

Within the first two components the **single worst finding dominates**, and
the remaining volume matters progressively less. One critical-severity
dependency costs about three quarters of its component; eight low-severity
ones cost about a third. That ordering is deliberate — a hundred trivial
advisories are not equivalent to one critical — and the volume term never
drives a component to zero, so a project with 300 findings still scores below
one with 30 rather than tying with it.

The third component asks a different question: **how long has the fix been
available?** Being hit by an advisory published last week is bad luck. Still
resolving to a version affected by one published a year ago is a failure to
track dependencies. A package counts here once its oldest advisory passes
90 days. Where no advisory carries a publication date the component is
excluded and its weight renormalized, never assumed clean.

A dependency set with no known advisories scores full marks on all three.

## Coverage is stated, never assumed

Some entries in a dependency graph carry no recorded version. A package
without a version cannot be matched against a version range, so it is skipped
and **counted** — every report states how many dependencies were assessed and
how many were not.

Repositories whose dependency graph is unavailable or switched off are not
penalized. The metric is excluded and the remaining Security weight
renormalized, exactly as with any other unavailable input — see
[the health index](/wiki/health-index).

## What this does not claim

An advisory here means one thing precisely: **the version recorded in the
dependency graph falls inside an advisory's affected range.**

It does not mean the vulnerable code path is reachable from this project, and
it does not mean the project is exploitable. Most advisories in a large
transitive closure are not exploitable in context.

Where a report falls back to the repository's own dependency graph — because
the repository publishes no package — it cannot separate what the project
ships from what it uses to build and test itself. Those reports say which
scope they used, and their findings should be read as being about the
repository's development environment as much as its software.

Read alongside [signals, not warranties](/wiki/signals-not-warranties): a
clean result is not a security guarantee, and a finding is a prompt to look,
not a verdict.

## Why it is separate from security posture

The Scorecard's own vulnerabilities check queries the same advisory database
and already contributes to [security posture](/wiki/security-posture).
Scoring a second signal derived from OSV inside that same metric would count
one body of evidence twice.

Keeping them apart also keeps them honest about their different granularity.
Security posture asks whether a project carries known-vulnerable dependencies
at all, as one check among nineteen. This metric asks which packages, at what
severity, and fixed in which version — the form an answer has to take before
anyone can act on it.
