# Package Vulnerabilities Scanner - OSV (npm, PyPI, Maven, Go) (`far003/package-vulnerabilities-osv`) Actor

Check open source packages or a lockfile against the OSV vulnerability database. One result per package: known vulnerabilities with CVE/GHSA ids, severity, fixed versions and a recommended upgrade. Free of API keys.

- **URL**: https://apify.com/far003/package-vulnerabilities-osv.md
- **Developed by:** [Francesco Antonio Russo](https://apify.com/far003) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.50 / 1,000 package checkeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Package Vulnerabilities Scanner for npm, PyPI, Maven, Go and more (OSV)

Check open source packages, or a whole lockfile, against the **OSV** vulnerability database and get **one clean result per package**: the known vulnerabilities (CVE and GHSA ids, severity with a real CVSS score, affected and fixed versions), a **recommended upgrade version**, or an explicit **"no known vulnerabilities"**. No API key, no account, no scraping.

**For:** developers and security teams who want a dependency audit as an API call or a scheduled run, agencies checking client projects, and anyone who needs the vulnerability list of a `package-lock.json`, `requirements.txt`, `go.mod`, `Cargo.lock`, `Gemfile.lock`, `composer.lock`... without installing anything.

### What you get

- **12 ecosystems:** npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist (PHP), Pub (Dart), Hex (Elixir), Hackage (Haskell), CRAN (R).
- **11 file formats read for you:** `package-lock.json` (and `npm-shrinkwrap.json`), `package.json`, `yarn.lock` (v1 and Berry), `pnpm-lock.yaml`, `requirements.txt`, `poetry.lock`, `Pipfile.lock`, `go.mod`, `Cargo.lock`, `Gemfile.lock`, `composer.lock`. Give a public URL or paste the text.
- **Distinct vulnerabilities, not duplicates.** The same vulnerability is often published three times (GitHub, PyPI, NVD). They are merged into one entry; the other records are listed in `alsoPublishedAs`. Django 3.2.0 has 38 vulnerabilities, not the 66 advisory records.
- **A severity you can sort by.** The CVSS score is computed from the advisory's vector (v3.0, v3.1, v4.0 and v2), so `cvssScore` is a number, not a label copied from somewhere. No vector and no label from the database means `null`, never a guess.
- **`fixedVersion`:** the lowest version that is outside every affected range of the advisories shown (when it can be calculated exactly, and it says why when it cannot).
- **Honest errors, free.** A range like `^1.2`, a missing version, an unknown ecosystem or an unreadable file gives a record that tells you what to fix. You are never charged for those.

### Input

| Field | What it does |
|---|---|
| `packages` | Packages with the **exact** version: `{"ecosystem":"npm","name":"lodash","version":"4.17.15"}` or a line such as `npm lodash 4.17.15`, `PyPI django==3.2.0`, `Maven org.apache.logging.log4j:log4j-core 2.14.1`, `pkg:cargo/time@0.1.43`. |
| `manifestUrls` | Public URLs of dependency files. GitHub and GitLab "blob" links are turned into raw files. |
| `manifestText` | The content of a dependency file pasted as text (up to 8 MB). |
| `manifestFormat` | Only needed if the format cannot be detected. |
| `ecosystems` | Check only these ecosystems (for a repo with several languages). |
| `minSeverity` | `low`, `medium`, `high` or `critical`. Advisories with no severity at all are kept (they cannot be shown to be low). |
| `onlyVulnerable` | Leave out packages with nothing to show: not delivered, not charged. |
| `includeDevDependencies` | Turn off to check only what ships to production. |
| `includeDetails` | Add the long advisory text (first 4,000 characters). |
| `includeWithdrawn` | OSV already omits withdrawn advisories; this keeps one that was withdrawn between the lookup and the read. |
| `dedupePackages` | On by default: a package found many times is checked and charged once, and lists where it was found. |
| `maxPackages` | 1 to 20,000 different packages per run (default 1,000). Packages above the limit are not checked and not charged. |

Example input:

```json
{
  "packages": [
    { "ecosystem": "npm", "name": "lodash", "version": "4.17.15" },
    "PyPI django==3.2.0",
    "Maven org.apache.logging.log4j:log4j-core 2.14.1"
  ],
  "manifestUrls": ["https://github.com/mochajs/mocha/blob/v8.0.0/package-lock.json"],
  "minSeverity": "high",
  "includeDevDependencies": false
}
```

**Use exact versions.** A lockfile has them; `package.json` and `requirements.txt` only do for exact pins. For a range such as `^4.17.0` the Actor cannot know which version you run, so it does not guess: you get a free `invalid-version` record that names the lockfile to use. (This is deliberate: OSV itself answers a version it cannot read with every vulnerability ever reported for the package.)

### Output

One dataset record per package. Real output of a run (shortened to one of the six vulnerabilities):

```json
{
  "ecosystem": "npm",
  "name": "lodash",
  "version": "4.17.15",
  "purl": "pkg:npm/lodash@4.17.15",
  "status": "vulnerable",
  "error": null,
  "vulnerabilityCount": 6,
  "maxSeverity": "high",
  "maxCvssScore": 8.1,
  "severityCounts": { "critical": 0, "high": 3, "medium": 3, "low": 0, "unknown": 0 },
  "fixedVersion": "4.18.0",
  "fixedVersionStatus": "calculated",
  "vulnerabilities": [
    {
      "id": "GHSA-p6mc-m468-83gw",
      "aliases": ["CVE-2020-8203"],
      "cveIds": ["CVE-2020-8203"],
      "summary": "Prototype Pollution in lodash",
      "severity": "high",
      "cvssScore": 7.4,
      "cvssVector": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:H",
      "severitySource": "cvss-3.1",
      "published": "2020-07-15T19:15:48Z",
      "modified": "2025-08-12T21:56:17.174859Z",
      "fixedVersions": ["4.17.19"],
      "affectedRanges": [{ "type": "SEMVER", "introduced": "3.7.0", "fixed": "4.17.19", "lastAffected": null }],
      "weaknesses": ["CWE-1321", "CWE-770"],
      "advisoryUrl": "https://nvd.nist.gov/vuln/detail/CVE-2020-8203",
      "osvUrl": "https://osv.dev/vulnerability/GHSA-p6mc-m468-83gw",
      "database": { "name": "GitHub Advisory Database", "license": "CC-BY-4.0", "attribution": "GitHub Advisory Database (CC-BY-4.0), via OSV.dev" },
      "alsoPublishedAs": []
    }
  ],
  "dependencyScope": null,
  "foundIn": ["package list"],
  "checkedAt": "2026-10-19T17:23:06.074Z",
  "dataSource": "OSV.dev"
}
```

A package that got an answer has `status` **`vulnerable`**, **`no-known-vulnerabilities`** or **`only-below-minimum-severity`** (it has advisories, all under the `minSeverity` you set; it is not "clean"). These are the charged results. Everything else is a free record that says what went wrong:

```json
{ "ecosystem": "PyPI", "name": "requests", "version": null, "versionInput": null, "status": "invalid-version",
  "error": "requirements.txt has \">=2.0\" instead of an exact pin (==): pin the version, or use a lock file", "foundIn": ["pasted manifest"] }
```

Other free statuses: `invalid-name`, `unsupported-ecosystem`, `lookup-failed` (OSV did not answer for that package), `manifest-error` (file not found, private, not a lockfile), `not-checked` (a limit was reached). Every field is always present, `null` when the source does not say.

The run also writes a **`SUMMARY`** record (key-value store): counts per status and severity, per-file results (format, packages read, entries skipped with the reason), request counts, warnings and why a run stopped early.

| Main fields | Meaning |
|---|---|
| `version`, `purl` | The exact version checked and its Package URL (join with an SBOM). |
| `vulnerabilityCount`, `maxSeverity`, `maxCvssScore`, `severityCounts` | Totals for sorting and filtering. |
| `fixedVersion`, `fixedVersionStatus`, `fixedVersionNote` | Recommended upgrade; status is `calculated`, `not-needed`, `no-fix-published` (an advisory lists no fix for your release line) or `not-calculable`. |
| `vulnerabilities[]` | `id`, `aliases`, `cveIds`, `summary`, `severity`, `cvssScore`, `cvssVector`, `cvssVersion`, `severitySource`, `published`, `modified`, `fixedVersions`, `affectedRanges`, `weaknesses` (CWE), links, `database` (name, license, attribution), `alsoPublishedAs`. |
| `dependencyScope` | `prod` or `dev` when the lockfile says so. |
| `foundIn`, `notes` | Where the package came from; things worth knowing about this result. |

### Price

**$0.0005 per package checked** ($0.50 per 1,000 packages), one event, `package-checked`. You pay only for packages that got an answer (vulnerable, clean, or only below your severity filter). Errors, invalid input, unreadable files and packages left out by a limit are free. Set a maximum cost per run in Apify and the Actor stops cleanly when it is reached.

Examples (measured on real runs): a 3-package list costs $0.0015. The 2,800 packages of the `yarn.lock` of Next.js 12 were checked in 22 seconds: $1.40 if every package is delivered, $0.10 with `onlyVulnerable` and `minSeverity: critical` (195 packages had something to show). With `onlyVulnerable` you pay only for the packages that have something to show.

### Where the data comes from, and licenses

Data comes from [OSV.dev](https://google.github.io/osv.dev/), the open vulnerability database run by Google, which aggregates advisory databases. This Actor is not affiliated with OSV or Google. OSV documents its API as not rate limited and publishes the licenses of each source on its [data sources page](https://google.github.io/osv.dev/data/), read on 2026-10-19: GitHub Advisory Database, PyPI Advisory Database, Go Vulnerability Database, OSS-Fuzz and the Erlang Ecosystem Foundation CNA are CC-BY 4.0; RustSec, the Global Security Database and Haskell Security Advisories are CC0; OpenSSF Malicious Packages, R Consortium and Bitnami are Apache 2.0; Drupal is MIT. None of them restricts commercial use.

**Every vulnerability carries its `database` with name, license and an attribution line**, and merged duplicates are listed in `alsoPublishedAs` with their own license. If you reuse CC-BY advisories, keep that attribution. The severity scores and `fixedVersion` are computed by this Actor from the advisory data and are not part of the original records. A record from a database that OSV does not list with a license (for example OSV's own conversion of NVD entries) has `license: null` and a note saying so; the Actor does not invent one.

### FAQ

**Does it tell me whether my code actually uses the vulnerable function?** No. It lists the known vulnerabilities of the exact versions you have. Reachability analysis needs your source code.

**Why is `fixedVersion` sometimes `null`?** Because it would be a guess otherwise. `no-fix-published`: an advisory has no fixed version for your release line (common for end-of-life versions). `not-calculable`: the versions use qualifiers (rc, beta, SNAPSHOT) whose order depends on the ecosystem; the fixed versions of each advisory are still listed. When the only fix is in a newer major version, the note says so.

**Why does the version have to be exact?** See the Input section. Resolving a range to an installed version needs a lockfile or a registry, and a guess would give a false "clean".

**Are names case-sensitive?** PyPI and Packagist names are normalised for you. npm names are matched as written. OSV matches NuGet names with their exact capitalisation (`Newtonsoft.Json`, not `newtonsoft.json`), so an all-lowercase NuGet name that finds nothing is looked up once in the public NuGet registry (api.nuget.org) for its exact spelling and checked again; the record says the name was corrected. If the registry does not answer or does not know the name, the result carries a note instead.

**Which vulnerabilities are included?** Those OSV matches to the exact package and version, from every database it aggregates, withdrawn ones excluded. A "no known vulnerabilities" result means OSV has no advisory for that version today; it is not a guarantee of safety.

**Private files?** Only public URLs are fetched (ports 80 and 443, no addresses inside private networks, no URLs with a password, files up to 8 MB). For private repos paste the text in `manifestText`. Only the one URL you give is downloaded; no links are followed. `file:`, `link:`, `workspace:` and git/URL dependencies inside a lockfile are skipped and counted in the SUMMARY.

**What is not supported?** `pom.xml`, `build.gradle`, container images, SBOM files and OS packages (Debian, Alpine, Ubuntu). Git and local-path dependencies are skipped and listed in the SUMMARY with the reason.

**How big can it be?** Up to 20,000 different packages and 8 MB per file per run. Very long lists of problems are summarised in one free record instead of thousands.

### Integration

JavaScript:

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('far003/package-vulnerabilities-osv').call({
    manifestUrls: ['https://github.com/mochajs/mocha/blob/v8.0.0/package-lock.json'],
    minSeverity: 'high',
    onlyVulnerable: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const pkg of items) console.log(pkg.name, pkg.version, pkg.maxSeverity, '->', pkg.fixedVersion);
```

Python:

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("far003/package-vulnerabilities-osv").call(run_input={
    "packages": ["PyPI django==3.2.0", "npm lodash 4.17.15"],
})
for pkg in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(pkg["name"], pkg["version"], pkg["status"], pkg["maxSeverity"], pkg["fixedVersion"])
```

Run it on a schedule against the lockfile URL of each project and use `onlyVulnerable` with `minSeverity: "high"` to get a record only when something needs attention.

# Actor input Schema

## `packages` (type: `array`):

Packages to check, each with the EXACT version installed. Either an object {"ecosystem", "name", "version"} or a text line such as "npm lodash 4.17.15", "PyPI django==3.2.0", "Maven org.apache.logging.log4j:log4j-core 2.14.1" or a package URL "pkg:npm/lodash@4.17.15". Ecosystems: npm, PyPI, Go, Maven, crates.io, RubyGems, NuGet, Packagist, Pub, Hex, Hackage, CRAN. A range (^1.2, >=2) or "latest" cannot be checked: you get a free error record telling you so. Names are normalised (PyPI case and underscores, Packagist case, spaces).

## `manifestUrls` (type: `array`):

Public URLs of dependency files: package-lock.json, package.json, yarn.lock, pnpm-lock.yaml, requirements.txt, poetry.lock, Pipfile.lock, go.mod, Cargo.lock, Gemfile.lock, composer.lock. Use the raw file (GitHub and GitLab "blob" page links are converted to raw automatically). Lockfiles give exact versions and are the best input; package.json and requirements.txt only help for exact pins. Files must be public: a URL that asks for a login returns a free error record.

## `manifestText` (type: `string`):

Paste the content of a dependency file here (up to 8 MB). The format is detected from the content; set "Manifest format" if it cannot be told.

## `manifestFormat` (type: `string`):

Format of the pasted text (and of URLs whose file name does not tell it). Auto-detect works for almost every file.

## `ecosystems` (type: `array`):

Check only packages of these ecosystems and skip the others (useful for a repository with several languages). Empty = all.

## `minSeverity` (type: `string`):

Keep only vulnerabilities of at least this severity (low, medium, high, critical). Severity is the CVSS rating computed from the advisory's vector (or the database's own label when there is no vector). Advisories with NO severity at all are kept, because they cannot be shown to be low.

## `onlyVulnerable` (type: `boolean`):

Leave out packages that have no vulnerability to show (clean ones, and ones whose advisories are all under the minimum severity). They are not delivered and not charged. Off by default, because "checked and clean" is a useful answer.

## `includeDevDependencies` (type: `boolean`):

Lockfiles that mark development-only packages (package-lock.json, poetry.lock, Pipfile.lock, composer.lock, package.json). Turn off to check only what ships to production.

## `includeWithdrawn` (type: `boolean`):

OSV already leaves withdrawn advisories out of its answers. If one is withdrawn between the lookup and the read of its details it is dropped here unless this is on.

## `includeDetails` (type: `boolean`):

Add the long description (first 4,000 characters) of each advisory. Off by default to keep results small; every advisory always has its summary and links.

## `dedupePackages` (type: `boolean`):

A package that appears several times (in different files or at different places of a lockfile) with the same version is checked and charged once, and lists where it was found. Turn off to get one record per occurrence.

## `maxPackages` (type: `integer`):

Upper limit of different packages checked in one run (1 to 20,000). Packages above the limit are not checked and not charged; a free record tells you how many were left out.

## Actor input object example

```json
{
  "packages": [
    {
      "ecosystem": "npm",
      "name": "lodash",
      "version": "4.17.15"
    },
    {
      "ecosystem": "PyPI",
      "name": "django",
      "version": "3.2.0"
    },
    {
      "ecosystem": "Maven",
      "name": "org.apache.logging.log4j:log4j-core",
      "version": "2.14.1"
    }
  ],
  "manifestUrls": [
    "https://github.com/expressjs/express/blob/4.16.0/package.json"
  ],
  "manifestFormat": "auto",
  "minSeverity": "any",
  "onlyVulnerable": false,
  "includeDevDependencies": true,
  "includeWithdrawn": false,
  "includeDetails": false,
  "dedupePackages": true,
  "maxPackages": 1000
}
```

# Actor output Schema

## `packages` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "packages": [
        {
            "ecosystem": "npm",
            "name": "lodash",
            "version": "4.17.15"
        },
        {
            "ecosystem": "PyPI",
            "name": "django",
            "version": "3.2.0"
        },
        {
            "ecosystem": "Maven",
            "name": "org.apache.logging.log4j:log4j-core",
            "version": "2.14.1"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("far003/package-vulnerabilities-osv").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "packages": [
        {
            "ecosystem": "npm",
            "name": "lodash",
            "version": "4.17.15",
        },
        {
            "ecosystem": "PyPI",
            "name": "django",
            "version": "3.2.0",
        },
        {
            "ecosystem": "Maven",
            "name": "org.apache.logging.log4j:log4j-core",
            "version": "2.14.1",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("far003/package-vulnerabilities-osv").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "packages": [
    {
      "ecosystem": "npm",
      "name": "lodash",
      "version": "4.17.15"
    },
    {
      "ecosystem": "PyPI",
      "name": "django",
      "version": "3.2.0"
    },
    {
      "ecosystem": "Maven",
      "name": "org.apache.logging.log4j:log4j-core",
      "version": "2.14.1"
    }
  ]
}' |
apify call far003/package-vulnerabilities-osv --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,far003/package-vulnerabilities-osv"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/GjJB8bJRGaNQ8d6Aw/builds/jza2iOOTKuXmEqu9s/openapi.json
