How OSV.dev Vulnerability Lookup Works in SkillSpector: Live API with Offline Fallback Support
SkillSpector's supply-chain analyzer (SC4) performs a live OSV.dev vulnerability lookup for project dependencies, then automatically falls back to a curated static vulnerability database when the API is unreachable or returns incomplete coverage.
The NVIDIA SkillSpector repository implements a robust two-stage vulnerability detection system in its supply-chain security analyzer. This architecture ensures that Python and JavaScript dependencies are checked against the latest OSV.dev advisory database while maintaining reliable offline detection through embedded fallback data. The implementation spans osv_client.py for API communication and static_patterns_supply_chain.py for orchestration and offline coverage.
How OSV.dev Vulnerability Lookup Works
The live vulnerability assessment operates through batched API queries with intelligent caching and strict timeout handling.
Live OSV.dev API Query
For each dependency discovered in requirements.txt or package.json files, SkillSpector constructs a query batch and invokes query_batch() from osv_client.py. The client generates a cache key using _cache_key() based on the package name, version, and ecosystem (ECOSYSTEM_PYPI for PyPI or ECOSYSTEM_NPM for npm).
If the package is not cached, the client sends an HTTP POST request to https://api.osv.dev/v1/querybatch with a 10-second timeout. Upon receiving the response, query_batch() extracts vulnerability IDs and retrieves detailed information for up to ten IDs using _fetch_vuln_details(). Each entry is parsed into a VulnResult object and stored in the in-memory cache via _put_cache().
In-Memory Caching Strategy
The OSV client implements a time-bound cache to avoid redundant API calls during analysis runs. The _cache dictionary stores tuples of (timestamp, results) per package, with a TTL (time-to-live) of one hour defined by _CACHE_TTL_SECS. When query_batch() processes a package, it first attempts _get_cached() using the computed cache key; if valid cached data exists, the function returns immediately without network overhead.
Graceful Error Handling
If the network request fails due to HTTP errors, timeouts, or malformed JSON responses, query_batch() returns an empty list for all packages in the batch. This signals to the orchestration layer in static_patterns_supply_chain.py that the live lookup is unavailable, triggering immediate activation of the offline fallback mechanism.
Offline Fallback for Supply Chain Security
When OSV.dev lookups fail or leave certain packages without coverage, SkillSpector seamlessly transitions to a static vulnerability database embedded in the source code.
Static Vulnerability Database
The fallback data resides in static_patterns_supply_chain.py as two constant tables: _FALLBACK_VULNERABLE_PYPI and _FALLBACK_VULNERABLE_NPM. Each entry follows the tuple structure (package_name, max_safe_version, description, confidence), containing known vulnerable versions curated by the SkillSpector maintainers.
Coverage Gap Analysis
After the live lookup completes, the _sc4_from_osv() function returns both the vulnerability findings and a set of covered package names (osv_covered). The analyzer then computes coverage gaps:
uncovered_packages = [
p for p in packages
if p[0].lower().replace("_", "-") not in osv_covered
]
The _sc4_from_fallback() function receives this list of uncovered packages and matches them against the static tables, ensuring no dependency escapes analysis due to API limitations.
Severity and Version Matching
Fallback findings are automatically assigned Severity.HIGH regardless of the original vulnerability severity. When a static entry specifies a max_safe version, the code compares it against the detected version using _version_lt(). If the installed version is lower (and therefore vulnerable), the finding includes a remediation suggestion formatted as >={max_safe}.
Implementation Details and Code Examples
The following examples demonstrate how to interact with SkillSpector's vulnerability detection components directly.
Querying the OSV Client Directly
To check specific packages without running the full analysis pipeline:
from skillspector.nodes.analyzers.osv_client import query_batch, ECOSYSTEM_PYPI
# Look up two packages simultaneously
pkgs = [("requests", "2.28.0"), ("pyyaml", None)]
results = query_batch(pkgs, ECOSYSTEM_PYPI)
for pkg, vulns in zip(pkgs, results):
print(f"{pkg[0]}:{pkg[1] or ''} → {len(vulns)} vulnerabilities")
for v in vulns:
print(f" - {v.vuln_id} ({v.severity}) – {v.summary}")
Running the Full SC4 Analysis
Execute the complete supply-chain analyzer including both live and fallback stages:
from skillspector.state import SkillspectorState
from skillspector.nodes.analyzers.static_patterns_supply_chain import node
# Minimal state mimicking SkillSpector's runtime
state = SkillspectorState(
components=["requirements.txt"],
file_cache={"requirements.txt": open("requirements.txt").read()},
manifest={},
)
response = node(state)
for finding in response["findings"]:
if finding.rule_id == "SC4":
print(finding.message)
Detecting Offline Mode
Check API availability before triggering analysis:
from skillspector.nodes.analyzers.osv_client import is_available
if not is_available():
print("OSV.dev is unreachable – static fallback will be used automatically.")
Key Design Benefits
SkillSpector's dual-layer approach provides several architectural advantages:
- Hybrid coverage combines the freshness of live OSV.dev data with the reliability of static vulnerability tables
- Graceful degradation ensures security scanning continues uninterrupted during network outages
- Performance optimization through 1-hour TTL caching reduces redundant API calls for repeated package versions
- Ecosystem flexibility supports both PyPI and npm dependencies through unified
ECOSYSTEMconstants - Severity consistency maps OSV severity levels via
_osv_severity_to_app()while maintaining high-severity defaults for offline findings
Summary
- SkillSpector checks dependencies via
query_batch()inosv_client.py, which querieshttps://api.osv.dev/v1/querybatchwith 10-second timeouts - In-memory caching stores results for one hour (
_CACHE_TTL_SECS) to optimize repeated lookups - Network failures trigger automatic fallback to
_FALLBACK_VULNERABLE_PYPIand_FALLBACK_VULNERABLE_NPMinstatic_patterns_supply_chain.py - Uncovered packages are identified by comparing against
osv_coveredand processed by_sc4_from_fallback() - Fallback findings always use
Severity.HIGHand include safe version recommendations whenmax_safe_versionis defined
Frequently Asked Questions
What happens when the OSV.dev API is unreachable?
When the API returns HTTP errors, times out after 10 seconds, or returns malformed JSON, query_batch() returns an empty list and logs a warning via logger.warning. The SC4 analyzer in static_patterns_supply_chain.py detects this condition and immediately processes all discovered dependencies through the offline fallback tables, ensuring continuous vulnerability detection without manual intervention.
How does SkillSpector handle cached vulnerability data?
The OSV client maintains an in-memory cache (_cache) that stores query results with timestamps. Before making network requests, _get_cached() checks if valid data exists using a cache key derived from package name, version, and ecosystem. Cached entries expire after one hour (_CACHE_TTL_SECS), after which _put_cache() updates the storage with fresh API results.
Which package ecosystems does the OSV.dev lookup support?
SkillSpector currently supports PyPI and npm ecosystems through the constants ECOSYSTEM_PYPI = "PyPI" and ECOSYSTEM_NPM = "npm" defined in osv_client.py. The analyzer automatically detects the appropriate ecosystem based on file type—requirements.txt triggers PyPI analysis while package.json triggers npm analysis via _extract_packages_from_requirements() and _extract_packages_from_package_json() respectively.
How are severity levels determined for fallback findings?
All fallback vulnerabilities are assigned Severity.HIGH regardless of their actual CVSS score or severity in the OSV database. This conservative approach ensures that offline findings receive immediate attention. In contrast, live OSV results are normalized through _osv_severity_to_app() to map OSV severity strings to SkillSpector's internal severity enumeration.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →