Skip to content

Instantly share code, notes, and snippets.

@cgoldberg
Last active September 12, 2025 23:02
Show Gist options
  • Save cgoldberg/caf2a7bf5ce859022bd42cfe9bd462f2 to your computer and use it in GitHub Desktop.
Save cgoldberg/caf2a7bf5ce859022bd42cfe9bd462f2 to your computer and use it in GitHub Desktop.
Python - Generate language specific SBOMs from a multi-language monorepo on GitHub
#!/usr/bin/env python3
#
# Copyright (c) 2025 Corey Goldberg (https://github.com/cgoldberg)
# License: MIT
#
# Generate language specific SBOMs from a multi-language monorepo on GitHub
# - fetches repo-wide SBOM from GitHub
# - generates individual SBOMs for each language specified
#
# requires:
# - lib4sbom
# - requests
import re
from lib4sbom.parser import SBOMParser
from lib4sbom.output import SBOMOutput
import requests
ORGANIZATION = "SeleniumHQ"
REPO = "selenium"
PACKAGE_MANAGERS_LANGUAGES = {
"gem": "Ruby",
"cargo": "Rust",
"npm": "JavaScript",
"nuget": "DotNET",
"pypi": "Python",
}
def get_package_managers(packages):
package_managers = []
for package in packages:
external_reference = package["externalreference"]
package_manager = "UNKNOWN"
if external_reference[0][0] == "PACKAGE-MANAGER":
match = re.search(r"^pkg:([^/]+)", external_reference[0][2])
if match:
package_manager = match.group(1)
if package_manager != "githubactions":
package_managers.append(package_manager)
return list(set(package_managers))
def filter_packages(packages, package_manager):
filtered_packages = []
for package in packages:
match = re.search(r"^pkg:([^/]+)", package["externalreference"][0][2])
if match.group(1) == package_manager:
filtered_packages.append(package)
return filtered_packages
def filter_relationships(packages, relationships):
filtered_relationships = []
for relationship in relationships:
for package in packages:
if package["id"] == relationship["target_id"]:
filtered_relationships.append(relationship)
break
return filtered_relationships
def get_repo_sbom(organization, repo):
headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
url = f"https://api.github.com/repos/{organization}/{repo}/dependency-graph/sbom"
response = requests.get(url, headers=headers)
if response.status_code != 200:
raise Exception(f"Failed to retrieve SBOM")
return response.text
if __name__ == "__main__":
print(f"\nRetrieving SBOM from GitHub for {ORGANIZATION}/{REPO} ...\n")
sbom = get_repo_sbom(ORGANIZATION, REPO)
parser = SBOMParser()
parser.parse_string(sbom)
print(f"SBOM type: {parser.get_type()}")
main_sbom = parser.get_sbom()
all_packages = [p for p in parser.get_packages() if REPO not in p["name"].lower()]
print(f"Found {len(all_packages)} packages\n")
all_relationships = parser.get_relationships()
package_managers = get_package_managers(all_packages)
unknown_package_managers = [
pm for pm in package_managers if pm not in PACKAGE_MANAGERS_LANGUAGES.keys()
]
if unknown_package_managers:
raise Exception(
f"Unknown package manager found in SBOM: {unknown_package_managers}"
)
for package_manager, language in PACKAGE_MANAGERS_LANGUAGES.items():
language_sbom = main_sbom.copy()
packages = filter_packages(all_packages, package_manager)
relationships = filter_relationships(packages, all_relationships)
language_sbom["packages"] = packages
language_sbom["relationships"] = relationships
file_name = f"{ORGANIZATION}_{REPO}_{language}.json"
sbom_output = SBOMOutput(filename=file_name, output_format="json")
sbom_output.generate_output(language_sbom)
print(f"{language}:\n - Found {len(packages)} packages")
print(f" - Generated language-specific SBOM:\n - {file_name}\n")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment