Skip to content

Instantly share code, notes, and snippets.

@renepardon
Created May 12, 2026 17:42
Show Gist options
  • Select an option

  • Save renepardon/49d5deacfd9b9dd7a87edf8dd2fd47f8 to your computer and use it in GitHub Desktop.

Select an option

Save renepardon/49d5deacfd9b9dd7a87edf8dd2fd47f8 to your computer and use it in GitHub Desktop.
Read SBOM JSON and check osv.dev for known vulnerabilities
import json
import sys
import requests
class OSVClient:
"""Client for the OSV.dev API."""
BASE_URL = "https://api.osv.dev/v1/query"
def check_vulnerability(self, package_name, version):
"""Queries OSV.dev for vulnerabilities for a specific package and version."""
payload = {
"version": version,
"package": {
"name": package_name,
"ecosystem": "PyPI"
}
}
try:
response = requests.post(self.BASE_URL, json=payload, timeout=10)
response.raise_for_status()
data = response.json()
return data.get("vulns", [])
except requests.exceptions.RequestException as e:
# For simplicity in this exercise, we treat API errors as no vulnerabilities found
# or we could raise it. The requirement says "Errors in file handling",
# but doesn't specify API error handling details beyond "Query an external API".
print(f"Warning: Failed to query OSV for {package_name}@{version}: {e}")
return []
def display_results(vulnerable_components):
"""Displays the scan results to the user."""
if not vulnerable_components:
print("\nNo vulnerabilities found.")
return
print("\nVulnerable components found:")
for comp in vulnerable_components:
print(f"- {comp['name']} ({comp['version']}): {len(comp['vulns'])} vulnerabilities")
for vuln in comp['vulns']:
vuln_id = vuln.get('id', 'N/A')
summary = vuln.get('summary', 'No summary provided')
print(f" * {vuln_id}: {summary}")
# Exit with a clear error if vulnerabilities are found
sys.exit(1)
class SBOMScanner:
"""Scanner for SBOM files to detect vulnerabilities."""
def __init__(self, sbom_path):
self.sbom_path = sbom_path
self.osv_client = OSVClient()
def load_sbom(self):
"""Loads and parses the SBOM JSON file."""
try:
with open(self.sbom_path, 'r') as f:
return json.load(f)
except FileNotFoundError:
print(f"Error: The file '{self.sbom_path}' was not found.")
sys.exit(1)
except json.JSONDecodeError:
print(f"Error: Failed to decode JSON from '{self.sbom_path}'.")
sys.exit(1)
except Exception as e:
print(f"Error reading file '{self.sbom_path}': {e}")
sys.exit(1)
def scan(self):
"""Scans all components in the SBOM for vulnerabilities."""
sbom_data = self.load_sbom()
components = sbom_data.get("components", [])
vulnerable_components = []
for component in components:
name = component.get("name")
version = component.get("version")
if not name or not version:
continue
print(f"Checking {name}@{version}...")
vulns = self.osv_client.check_vulnerability(name, version)
if vulns:
vulnerable_components.append({
"name": name,
"version": version,
"vulns": vulns
})
return vulnerable_components
def main():
if len(sys.argv) < 2:
print("Usage: python3 sbom_scanner.py <path_to_sbom_json>")
sys.exit(1)
sbom_path = sys.argv[1]
scanner = SBOMScanner(sbom_path)
vulnerable_components = scanner.scan()
display_results(vulnerable_components)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment