Skip to content

docs: add a consumer guide for using the reports - #1329

Open
DevamShah wants to merge 2 commits into
ossf:mainfrom
DevamShah:docs/consuming-guide
Open

DevamShah wants to merge 2 commits into
ossf:mainfrom
DevamShah:docs/consuming-guide

Conversation

@DevamShah

Copy link
Copy Markdown

Summary

Adds docs/consuming.md, a quickstart for consuming this dataset — scanning a
project with osv-scanner, querying osv.dev, using the raw reports offline, and
wiring detection into CI and Renovate — and links it from the README's "Get
Involved" section. Closes #110.

Problem / motivation

The repository is an excellent OSV-formatted database of malicious packages, but
the docs only cover how to contribute reports, not how to use them. #110
(open since 2023) asked for exactly this: guidance on consuming the data via
osv-scanner, osv.dev, and Renovate. Without it, a defender who finds this repo
has no documented path from "here is the dataset" to "my dependency tree is
checked against it," which limits the protective value of the data the project
works hard to curate.

Change

  • New docs/consuming.md covering five consumption paths, each with copy-paste
    commands:
    1. osv-scanner (recommended) — install, scan a directory or a lockfile,
      and read a MAL- hit in the output.
    2. osv.dev API/v1/query, /v1/vulns/{id}, and /v1/querybatch
      examples.
    3. Raw dataset, offline — clone / sparse-checkout, build a per-ecosystem
      denylist with jq, and check a single package, with an explicit note to
      exclude withdrawn reports under ./osv/withdrawn/.
    4. CI — a ready-to-use GitHub Actions workflow using the official
      google/osv-scanner-action reusable workflow.
    5. RenovateosvVulnerabilityAlerts / vulnerabilityAlerts config.
  • A "Responding to a match" section that frames a hit as incident response
    (rotate secrets, treat the host as compromised), reusing the project's own
    report language, and points back to the README's False Positives process.
  • README: a new "Consume the Reports" subsection under "Get Involved" linking to
    the guide.

The doc follows the existing docs/ style (single-# section headings, no
license header, as with docs/schema_additions.md and docs/aws_s3_auth.md)
and uses only relative links to in-repo files. No code, schema, or data changes.

Security rationale

Malicious packages map to CWE-506: Embedded Malicious
Code
— the same classification
the reports already carry — and a confirmed dependency on one is, by this
project's own definition, a full host compromise requiring incident response
(MITRE ATT&CK T1195.002, Compromise Software Supply
Chain
). Software supply chain
compromise is OWASP Top 10 2021 A06: Vulnerable and Outdated
Components

and A08: Software and Data Integrity
Failures
.
A curated malware dataset only reduces risk if defenders can apply it; this guide
turns the database into an enforceable control at the two points that matter —
pre-merge in CI and continuously via Renovate — and the "Responding to a match"
section keeps remediation aligned with proper incident response rather than a
routine version bump.

Testing / validation

Documentation-only change; validated locally against a fresh clone:

  • All relative links in the new doc and the README addition resolve to existing
    paths (CONTRIBUTING.md, osv/withdrawn/, osv/malicious/npm/,
    docs/consuming.md); the referenced README anchors (#false-positives,
    #scope) exist.
  • Every JSON snippet in the doc (the /v1/query body, the /v1/querybatch
    body, and the renovate.json) parses cleanly under jq.
  • The offline jq/grep commands the doc teaches were run against the real
    dataset: the per-ecosystem denylist command produces names, and the
    single-package lookup returns MAL-2022-6113 for shubholic-test (positive)
    and nothing for a benign name (negative). That record carries CWE-506.
  • The google/osv-scanner-action tag (v2.3.8, the latest release) and its
    osv-scanner-reusable.yml path were confirmed to exist.
  • Change scope confirmed minimal: README.md (+8 lines) and a new
    docs/consuming.md. The Go test/lint CI is unaffected (it ignores osv/**
    and docs/**).

Fixes #110

Adds docs/consuming.md, a quickstart for consuming this dataset:
scanning a project with osv-scanner, querying osv.dev, using the raw
reports offline, and wiring detection into CI and Renovate. Links it
from the README's Get Involved section.

Closes ossf#110.

Signed-off-by: Devam Shah <devamshah91@gmail.com>
Re-checked every factual claim in docs/consuming.md against the repo and
the upstream tools, and corrected what had drifted:

- The denylist recipe needed `find -type f`. Some malicious packages are
  named `*.json` (npm: packages.json, packages-lock.json, pattern.json,
  ethers.json, ...), so their report directories match `-name '*.json'`
  and jq aborts with "Is a directory" mid-batch. On 6a50fc7 the old
  command yields 204715 names, the fixed one 219834.
- CWE-506 is not on every record. In a 3000-file random sample only 406
  (13.5%) carry affected[].database_specific.cwes, so the guide no longer
  tells consumers to key off it.
- Documented ./osv/unmergable/ (records with an empty id) alongside
  ./osv/withdrawn/, and narrowed the offline advice to osv/malicious/.
- Replaced the osv-scanner sample output with real v2.5.1 output, which
  now has CVSS and FIXED VERSION columns and a summary line.
- Bumped osv-scanner-action v2.3.8 -> v2.5.1 and added `actions: read`,
  which the reusable workflow needs to upload SARIF.
- Added osv-scanner's --offline-vulnerabilities/--local-db-path path as
  the simpler air-gapped option, and fixed the offline-mode doc URL.
- Renovate section now states what actually happens on a malicious match
  (skipReason: malicious-update-proposed), plus the direct-dependencies
  and datasource limits and the experimental flag.
- Added VS Code Marketplace / Open VSX to the ecosystem list.

Signed-off-by: Devam Shah <devamshah91@gmail.com>
@DevamShah
DevamShah force-pushed the docs/consuming-guide branch from 5016eb1 to 5a6ca8d Compare August 28, 2026 08:22
@DevamShah

Copy link
Copy Markdown
Author

@calebbrown — rebased this onto current main (6a50fc7) and re-verified it, since it had been sitting a while.

What it is: docs/consuming.md, a quickstart for the other side of this repo — scanning a project with osv-scanner, querying osv.dev, using the raw reports offline, and wiring detection into CI and Renovate. Answers #110. Plus 8 lines in the README linking it.

I went through every factual claim in it against the repo and the upstream tools rather than just rebasing. Things had drifted, and one of them was a real bug in a command the doc teaches:

The denylist recipe was silently wrong. It used find osv/malicious/npm -name '*.json'. Some malicious packages are themselves named *.json — on npm right now packages.json, packages-lock.json, pattern.json, ethers.json, eth.json, ethe.json, shubholic-test.json — so their report directories match too, jq dies on Is a directory mid-batch, and you get a short list with no obvious error. On 6a50fc7:

$ find osv/malicious/npm -name '*.json' -exec jq -r '.affected[].package.name' {} + | sort -u | wc -l
204715
$ find osv/malicious/npm -type f -name '*.json' -exec jq -r '.affected[].package.name' {} + | sort -u | wc -l
219834

15,119 names missing. Fixed with -type f and a note explaining why it matters.

CWE-506 is not on every record. The guide told consumers each report is tagged with CWE-506 under affected[].database_specific.cwes. In a 3,000-file random sample of osv/malicious/, 406 had it (13.5%). Reworded so nobody builds a gate that keys off it.

osv/unmergable/ wasn't mentioned. Now documented next to osv/withdrawn/, and the offline advice is narrowed to "restrict yourself to osv/malicious/", which excludes both.

osv-scanner sample output was from the old renderer. Replaced with what v2.5.1 actually prints, which I got by installing it and scanning a fixture lockfile pinned to shubholic-test@1.0.0:

Total 1 package affected by 1 known vulnerability (0 Critical, 0 High, 0 Medium, 0 Low, 1 Unknown) from 1 ecosystem.
0 vulnerabilities can be fixed.

+-------------------------------+------+-----------+----------------+---------+---------------+-------------------+
| OSV URL                       | CVSS | ECOSYSTEM | PACKAGE        | VERSION | FIXED VERSION | SOURCE            |
+-------------------------------+------+-----------+----------------+---------+---------------+-------------------+
| https://osv.dev/MAL-2022-6113 |      | npm       | shubholic-test | 1.0.0   | --            | package-lock.json |
+-------------------------------+------+-----------+----------------+---------+---------------+-------------------+

Exit 1 on that, exit 0 on a clean lockfile — so the "gate on the exit code" advice holds. CVSS and FIXED VERSION are empty for MAL- records, which I now call out, because the remediation is removal not an upgrade.

CI example. Bumped osv-scanner-action v2.3.8 → v2.5.1, and added actions: read to permissions:. The v2.5.1 reusable workflow declares actions: read on its own job for the SARIF upload (github/codeql-action#2117), and a called workflow can't hold a scope the caller didn't grant — upstream's own examples all set it, and mine didn't.

Added osv-scanner's own offline mode to Option 3 as the simpler air-gapped path, ahead of the raw-JSON recipes. Verified end to end: --offline-vulnerabilities --download-offline-databases --local-db-path pulled 210MB, then matched MAL-2022-6113 out of the local npm/all.zip. Also fixed that doc URL — it's /usage/offline-mode/, not /offline-mode/.

Renovate section now says what actually happens on a hit (warns and refuses the update, skipReason: malicious-update-proposed) and states the two limits that matter: direct dependencies only, and osvVulnerabilityAlerts is still flagged experimental. It's a second layer behind an osv-scanner gate, not a control on its own.

Also added VS Code Marketplace / Open VSX to the ecosystem list, and a pointer to the stats page.

Verification I ran, so you don't have to take my word for it: all 20 external links resolve (the two 405s are the POST-only osv.dev endpoints); all three osv.dev API calls in the doc return live results including MAL-2022-6113; the YAML and JSON snippets parse; every relative link and in-doc anchor resolves against this tree; the sparse-checkout snippet was run against a fresh clone. There's no markdown lint or link check in .github/workflows/, so this is manual. DCO passes, both commits signed off.

What I did not change: no schema, data, or code, and I left the doc under docs/ following the schema_additions.md / aws_s3_auth.md style — single # section headings, no license header.

Two things I'd rather you decide than guess at:

  1. Length. It's 288 lines. If you'd rather it were a third of that — osv-scanner and osv.dev only, drop the raw-JSON and Renovate sections — say the word and I'll cut it.
  2. Placement. docs/consuming.md linked from "Get Involved" was my guess. If you'd rather it lived in the README directly, or on the GitHub Pages site next to /stats/, I'll move it.

Happy to restructure however you want it. If the answer is that the project doesn't want to carry consumer docs, that's a fine answer too — just tell me and I'll close it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add docs for how to make use of the OSV reports

1 participant