# OSV.dev Integration for CVE Lookups in SkillSpector: Implementation Guide

> Learn how to integrate OSV.dev for CVE lookups in NVIDIA SkillSpector. This guide details batch API queries, in-memory caching, and fallback strategies for vulnerability data.

- Repository: [NVIDIA Corporation/SkillSpector](https://github.com/NVIDIA/SkillSpector)
- Tags: how-to-guide
- Published: 2026-06-16

---

**SkillSpector integrates OSV.dev by querying the batch API endpoint with package metadata, implementing an in-memory cache with 1-hour TTL, and falling back to static vulnerability databases when the service is unreachable.**

NVIDIA's SkillSpector performs real-time vulnerability detection through its OSV.dev integration for CVE lookups in Python and JavaScript dependencies. The implementation resides in [`skillspector/nodes/analyzers/osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/skillspector/nodes/analyzers/osv_client.py) and provides the supply-chain analyzer with live security intelligence while maintaining offline resilience.

## How the OSV Client Module Works

The [`osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/osv_client.py) module encapsulates all communication with the OSV.dev API. It handles request construction, response parsing, severity calculation, and network failure recovery.

### Step 1: Package-Level Request Generation

For each dependency, the analyzer builds a JSON query containing the package name, optional version, and ecosystem identifier. The helper function `_build_query` constructs these payloads for `"PyPI"` or `"npm"` ecosystems.

According to the source code, the query structure includes the package name, version string, and ecosystem constant (`ECOSYSTEM_PYPI` or `ECOSYSTEM_NPM`) before being aggregated into a batch request.

### Step 2: In-Memory Caching Strategy

Before issuing network requests, the client checks an in-process cache keyed by the tuple `(name, version, ecosystem)`. Cached entries persist for one hour as defined by `_CACHE_TTL_SECS = 3600`.

The internal methods `_get_cached` and `_put_cache` manage cache look-ups and updates. This minimizes redundant API calls when analyzing multiple files containing overlapping dependencies during the same process run.

### Step 3: Batch API Request

All uncached queries are sent in a single POST request to `https://api.osv.dev/v1/querybatch` (stored in `_OSV_BATCH_URL`). The `query_batch` function orchestrates this request and parses the response, which contains a list of vulnerability IDs for each submitted package.

This batch approach reduces network overhead by consolidating multiple package lookups into one HTTP transaction rather than issuing individual requests per dependency.

### Step 4: Vulnerability Detail Fetching

For each vulnerability ID returned by the batch query, the client issues a GET request to `https://api.osv.dev/v1/vulns/<ID>`. To prevent latency issues, the implementation limits detail fetching to the first 10 IDs per package.

The `_fetch_vuln_details` helper parses the JSON response into `VulnResult` objects. These objects contain:
- `vuln_id`: The OSV vulnerability identifier
- `summary`: Human-readable description
- `severity`: Calculated severity level
- `aliases`: Associated CVE numbers and other identifiers

### Step 5: Severity Estimation Without External Dependencies

OSV vulnerability objects may contain explicit severity fields or raw CVSS vectors. When only a CVSS vector is present, the `_estimate_cvss_severity` function estimates a coarse severity band (`CRITICAL`, `HIGH`, `MEDIUM`, `LOW`) without importing full CVSS calculation libraries.

This lightweight estimation avoids heavy dependencies while still providing actionable risk categorization for the supply-chain analyzer.

### Step 6: Graceful Fallback Handling

If the OSV request fails due to network errors, timeouts, or air-gapped environments, `query_batch` returns empty result lists. The supply-chain analyzer then falls back to built-in static vulnerability databases (`_FALLBACK_VULNERABLE_PYPI` and `_FALLBACK_VULNERABLE_NPM`).

Additionally, the `is_available()` method performs a lightweight POST with a dummy query to verify OSV endpoint reachability before attempting full batch requests, preventing unnecessary timeouts during analysis.

## Integration with the Supply-Chain Analyzer

The [`static_patterns_supply_chain.py`](https://github.com/NVIDIA/SkillSpector/blob/main/static_patterns_supply_chain.py) analyzer orchestrates the OSV integration by:

1. Extracting dependency entries from [`requirements.txt`](https://github.com/NVIDIA/SkillSpector/blob/main/requirements.txt), [`pyproject.toml`](https://github.com/NVIDIA/SkillSpector/blob/main/pyproject.toml), [`setup.py`](https://github.com/NVIDIA/SkillSpector/blob/main/setup.py), `Pipfile`, or [`package.json`](https://github.com/NVIDIA/SkillSpector/blob/main/package.json)
2. Calling `query_batch` with lists of `(name, version)` tuples and the appropriate ecosystem constant
3. Creating SC4 findings for vulnerable packages that include the most severe result and formatted advisory IDs via `_format_vuln_ids`
4. Processing uncovered packages through the static fallback routine (`_sc4_from_fallback`) when OSV is unavailable

## Practical Usage Examples

You can interact with the OSV client directly for custom vulnerability scanning:

```python
from skillspector.nodes.analyzers.osv_client import (
    ECOSYSTEM_PYPI,
    ECOSYSTEM_NPM,
    query_batch,
    is_available,
)

# 1️⃣ Check that the OSV.dev service is reachable

if is_available():
    # 2️⃣ Build a list of (package, version) tuples

    packages = [
        ("requests", "2.28.0"),   # PyPI package

        ("lodash", "4.17.20"),    # npm package

    ]

    # 3️⃣ Query the OSV.dev batch endpoint for PyPI packages

    results = query_batch(packages, ECOSYSTEM_PYPI)

    # 4️⃣ Inspect the returned VulnResult objects

    for pkg, vulns in zip(packages, results):
        name, version = pkg
        if not vulns:
            print(f"{name} ({version}) – no known CVEs")
            continue

        print(f"{name} ({version}) – {len(vulns)} CVE(s) found:")
        for v in vulns:
            print(f"  • {v.vuln_id} – {v.summary[:80]} (severity={v.severity})")
else:
    print("OSV.dev endpoint not reachable – falling back to static data")

```

In the supply-chain analyzer, this logic executes automatically:

```python

# Inside static_patterns_supply_chain.py

pkg_pairs = [(name, version) for name, version, _ in packages]
osv_results = query_batch(pkg_pairs, ecosystem)   # ← live OSV lookup

# Each `osv_results` entry is a list[VulnResult] used to build SC4 findings.

```

## Key Implementation Files

- **[`src/skillspector/nodes/analyzers/osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/osv_client.py)**: Implements the OSV.dev HTTP client, caching layer, severity parsing, and connectivity verification.
- **[`src/skillspector/nodes/analyzers/static_patterns_supply_chain.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/static_patterns_supply_chain.py)**: Calls `query_batch`, formats OSV results into SC4 findings, and coordinates fallback handling when OSV is unreachable.
- **[`tests/unit/test_osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/unit/test_osv_client.py)**: Unit tests validating cache behavior, error paths, and API response parsing.
- **[`tests/unit/test_patterns_new.py`](https://github.com/NVIDIA/SkillSpector/blob/main/tests/unit/test_patterns_new.py)**: Integration tests for the supply-chain analyzer's OSV interaction using mocked `query_batch` responses.

## Summary

- **Batch API Architecture**: SkillSpector consolidates vulnerability lookups into single POST requests to `https://api.osv.dev/v1/querybatch` to minimize network overhead.
- **Intelligent Caching**: The in-memory cache with 1-hour TTL prevents redundant API calls for repeated dependency checks during the same analysis run.
- **Dual Ecosystem Support**: The implementation handles both PyPI and npm packages through ecosystem-specific constants and query builders.
- **Resilient Fallback**: When OSV.dev is unreachable, the system automatically reverts to static vulnerability databases (`_FALLBACK_VULNERABLE_PYPI`/`_FALLBACK_VULNERABLE_NPM`).
- **CVSS Parsing**: Custom severity estimation logic avoids heavy dependencies while categorizing risks into standard severity bands.

## Frequently Asked Questions

### How does SkillSpector handle network failures during OSV lookups?

If the OSV.dev API is unreachable due to network errors, timeouts, or air-gapped environments, the `query_batch` function returns empty result lists. The supply-chain analyzer detects these empty results and automatically falls back to static vulnerability databases containing known vulnerable packages for PyPI and npm.

### What information does SkillSpector cache from OSV.dev responses?

The OSV client caches `VulnResult` objects keyed by the tuple `(package_name, version, ecosystem)` for 3600 seconds (one hour). Each cached entry contains the vulnerability ID, summary, severity level, and any CVE aliases retrieved from the API, eliminating redundant network requests for duplicate dependencies.

### Why does SkillSpector limit vulnerability detail fetching to 10 IDs per package?

The `_fetch_vuln_details` function limits detail retrieval to the first 10 vulnerability IDs to prevent excessive latency when processing packages with numerous historical vulnerabilities. This prioritization ensures rapid analysis while still capturing the most relevant recent security issues.

### Which dependency file formats does the supply-chain analyzer scan for OSV queries?

The supply-chain analyzer extracts dependency information from [`requirements.txt`](https://github.com/NVIDIA/SkillSpector/blob/main/requirements.txt), [`pyproject.toml`](https://github.com/NVIDIA/SkillSpector/blob/main/pyproject.toml), [`setup.py`](https://github.com/NVIDIA/SkillSpector/blob/main/setup.py), `Pipfile` for Python projects, and [`package.json`](https://github.com/NVIDIA/SkillSpector/blob/main/package.json) for Node.js projects. It normalizes these entries into `(name, version)` tuples before submitting them to the OSV batch API with the appropriate ecosystem identifier.