# How SkillSpector Uses OSV.dev for Vulnerability Lookups in Python Dependencies

> Discover how SkillSpector leverages OSV.dev for efficient Python dependency vulnerability lookups. Learn about batched API requests, caching, and offline fallback.

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

---

**SkillSpector performs batched HTTP POST requests to the OSV.dev API to check every dependency in a manifest against the vulnerability database, caching results for one hour and falling back to a static list when offline.**

SkillSpector, NVIDIA's security analysis framework, automatically identifies vulnerable third-party libraries by integrating with the Open Source Vulnerabilities (OSV) database. The tool queries OSV.dev to scan dependency files such as [`requirements.txt`](https://github.com/NVIDIA/SkillSpector/blob/main/requirements.txt) or [`package.json`](https://github.com/NVIDIA/SkillSpector/blob/main/package.json), mapping detected issues to normalized severity levels. This implementation enables real-time supply chain security analysis while maintaining resilience in air-gapped environments.

## The OSV.dev Integration Architecture

The OSV client implementation lives in [`src/skillspector/nodes/analyzers/osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/src/skillspector/nodes/analyzers/osv_client.py), where SkillSpector constructs batched requests to minimize API overhead. The workflow splits vulnerability discovery into distinct phases: batch querying, detail retrieval, and severity normalization.

### Batch Query Construction

The `_build_query()` function (lines 92–96) assembles a single HTTP POST payload containing all extracted package identifiers. For each dependency file, SkillSpector collects tuples of `(name, version, ecosystem)` and compiles them into a JSON array compatible with the OSV batch API specification.

### Live API Communication

The `query_batch()` function (lines 122–135) transmits the assembled query to `https://api.osv.dev/v1/querybatch` using the **httpx** asynchronous HTTP client. This single request can contain dozens of package entries, reducing network latency compared to individual API calls. The function parses the response to extract vulnerability IDs associated with each submitted package.

### Detailed Vulnerability Retrieval

For every vulnerability ID returned by the batch query, `_fetch_vuln_details()` (lines 86–96) executes a secondary GET request to `https://api.osv.dev/v1/vulns/<ID>`. This endpoint provides comprehensive metadata including the vulnerability summary, severity scores, aliases (such as CVE identifiers), and affected version ranges.

## Caching and Offline Resilience

SkillSpector implements a two-tiered resilience strategy to ensure continuous operation regardless of network availability.

### In-Memory Cache Implementation

The OSV client maintains an in-memory dictionary `_cache` (lines 57–61) keyed by `(name, version, ecosystem)` tuples. Results expire after `_CACHE_TTL_SECS` (set to 3600 seconds), preventing redundant API calls during the analysis of multiple files within the same project. Cache hits bypass both the batch request and detail retrieval phases, returning hydrated vulnerability objects immediately.

### Static Fallback for Air-Gapped Environments

When the OSV API is unreachable or returns empty results, the analyzer consults curated static lists defined in [`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). The dictionaries `_FALLBACK_VULNERABLE_PYPI` and `_FALLBACK_VULNERABLE_NPM` (lines 104–132) contain manually verified high-risk packages. The `_sc4_from_fallback()` method (lines 136–176) processes these entries, ensuring that known critical vulnerabilities are still reported even without internet connectivity.

## Severity Normalization and Mapping

Raw OSV data contains heterogeneous severity formats ranging from GitHub Security Advisory (GHSA) ratings to CVSS vectors. SkillSpector normalizes these through a two-stage transformation.

The `_severity_from_vuln()` function (lines 49–57) extracts the most authoritative available severity rating, prioritizing GHSA data, ecosystem-specific scores, and CVSS calculations in that order. It maps these values to standardized strings: `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW`.

Subsequently, `_osv_severity_to_app()` (lines 60–68) converts these strings into SkillSpector's internal `Severity` enumeration, ensuring consistent severity representation across all analyzer nodes regardless of the original OSV data source.

## Integration with the Supply Chain Analyzer

The `static_patterns_supply_chain` analyzer node orchestrates the OSV integration through the `_sc4_from_osv()` function (lines 88–108). This method:

1. Extracts `(name, version)` pairs from the dependency manifest
2. Invokes `query_batch()` with the appropriate ecosystem constant (e.g., `ECOSYSTEM_PYPI`)
3. Transforms OSV results into `AnalyzerFinding` objects containing the package identifier, advisory count, human-readable summary generated by `_format_vuln_ids()`, and normalized severity

Uncovered packages—those not returned by OSV—are subsequently passed to `_sc4_from_fallback()`, creating a comprehensive security report that combines live threat intelligence with offline safeguards.

## Practical Implementation Examples

You can manually invoke the OSV client to check specific packages outside the standard SkillSpector pipeline:

```python
from skillspector.nodes.analyzers.osv_client import query_batch, ECOSYSTEM_PYPI

# Example packages extracted from a requirements.txt file

packages = [("requests", "2.28.1"), ("django", None)]

# Perform the batch lookup

results = query_batch(packages, ECOSYSTEM_PYPI)

for pkg, vulns in zip(packages, results):
    name, version = pkg
    print(f"{name}{'==' + version if version else ''}:")
    for v in vulns:
        print(f"  - {v.vuln_id} [{v.severity}] {v.summary}")

```

Within the supply chain analyzer, the integration follows this pattern:

```python
def _sc4_from_osv(
    packages: list[tuple[str, str | None, int]],
    ecosystem: str,
    file_path: str,
    tag: list[str],
) -> tuple[list[AnalyzerFinding], set[str]]:
    # Build (name, version) tuples for the OSV client

    pkg_pairs = [(name, version) for name, version, _ in packages]

    # Query OSV.dev (live lookup)

    osv_results = query_batch(pkg_pairs, ecosystem)

    # Convert OSV results → AnalyzerFinding objects

    findings = []
    for (name, version, line), vulns in zip(packages, osv_results):
        if vulns:
            findings.append(
                AnalyzerFinding(
                    file_path=file_path,
                    line_number=line,
                    finding_type="VULNERABLE_DEPENDENCY",
                    severity=_osv_severity_to_app(vulns[0].severity),
                    message=f"{name}@{version} affected by {len(vulns)} advisories",
                    metadata={"vuln_ids": [v.vuln_id for v in vulns]}
                )
            )
    return findings, set()

```

## Summary

- **Batched API queries**: SkillSpector sends single HTTP POST requests to `https://api.osv.dev/v1/querybatch` containing all dependencies, implemented in `query_batch()`.
- **Intelligent caching**: Results are cached for one hour using `(name, version, ecosystem)` keys to avoid redundant network traffic.
- **Graceful degradation**: When OSV.dev is unreachable, the system falls back to static vulnerability lists (`_FALLBACK_VULNERABLE_PYPI` and `_FALLBACK_VULNERABLE_NPM`) defined in [`static_patterns_supply_chain.py`](https://github.com/NVIDIA/SkillSpector/blob/main/static_patterns_supply_chain.py).
- **Severity normalization**: The client extracts GHSA, ecosystem, or CVSS severity and maps them to internal `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW` levels via `_severity_from_vuln()` and `_osv_severity_to_app()`.
- **Supply chain integration**: The `static_patterns_supply_chain` node calls `_sc4_from_osv()` to convert OSV data into structured `AnalyzerFinding` objects for reporting.

## Frequently Asked Questions

### What API endpoints does SkillSpector use for OSV.dev lookups?

SkillSpector communicates with two primary OSV.dev endpoints. It sends batch queries to `https://api.osv.dev/v1/querybatch` to check multiple packages simultaneously, then retrieves detailed metadata for each vulnerability from `https://api.osv.dev/v1/vulns/<ID>`. These endpoints are accessed through the `query_batch()` and `_fetch_vuln_details()` functions in [`osv_client.py`](https://github.com/NVIDIA/SkillSpector/blob/main/osv_client.py).

### How does SkillSpector handle network failures when querying OSV.dev?

The implementation includes a curated static database of known vulnerable packages in [`static_patterns_supply_chain.py`](https://github.com/NVIDIA/SkillSpector/blob/main/static_patterns_supply_chain.py). If the OSV API returns no data or connection errors occur, the analyzer automatically falls back to checking dependencies against `_FALLBACK_VULNERABLE_PYPI` and `_FALLBACK_VULNERABLE_NPM` lists, ensuring supply chain checks continue uninterrupted in air-gapped environments.

### What is the cache duration for OSV.dev queries in SkillSpector?

Results are cached in memory for 3600 seconds (one hour) using the `_CACHE_TTL_SECS` constant. The cache dictionary stores vulnerability data keyed by package name, version, and ecosystem, preventing redundant API calls when analyzing multiple files or re-running scans within the same hour.

### How does SkillSpector normalize severity from OSV.dev responses?

The `_severity_from_vuln()` function extracts the most reliable available severity rating from GHSA data, ecosystem-specific scores, or CVSS vectors. The `_osv_severity_to_app()` helper then maps these raw values to the internal `Severity` enum (`CRITICAL`, `HIGH`, `MEDIUM`, `LOW`), standardizing output across different vulnerability data sources.