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

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 or 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, 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. 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:

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:

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.
  • 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.

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. 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →