SBOM Play

Presented @ BlackHat Presented @ Defcon Peerlist Launchpad GitHub Sponsors Buy Me A Coffee

About SBOM Play

Learn more about this client-side SBOM analysis tool

Project Overview

SBOM Play is a client-side web application for analyzing Software Bill of Materials (SBOM) data from GitHub repositories, organizations, and users. Built for security professionals to identify dependency vulnerabilities, assess license compliance, and understand software supply chain risks in real-time.

The tool features comprehensive SBOM analysis including dependency tracking, vulnerability detection via OSV.dev integration, license compliance checking, author analysis with funding detection, and SBOM quality assessment.

Key Principle: All analysis happens directly in your browser - no data ever leaves your machine.

Features
  • SBOM Analysis: Analyze SBOM data from GitHub organizations, users, and repositories
  • Dependency Tracking: Track dependency usage across multiple repositories
  • Vulnerability Detection: OSV.dev integration for vulnerability scanning
  • License Compliance: Comprehensive license categorization and risk assessment
  • Author Analysis: Author deduplication and funding opportunity detection
  • SBOM Quality Assessment: Quality scoring based on multiple categories
  • Multi-Organization Storage: Persistent storage using IndexedDB
  • Export/Import: Export and import analysis data with checksum validation
Privacy & Security
Privacy Assured: All analysis happens in your browser. No data is sent to any server.

SBOM Play is designed with privacy and security as top priorities:

  • Client-Side Processing: All SBOM analysis, dependency resolution, and vulnerability checking happens entirely in your browser
  • No Data Transmission: Your SBOM data never leaves your machine - it's processed locally
  • Local Storage: Analysis results are stored in your browser's IndexedDB, giving you full control
  • API Calls: Only public registry APIs are queried (npm, PyPI, crates.io, etc.) - no sensitive data is transmitted
  • GitHub Token: Optional GitHub Personal Access Token is used only for API rate limits and is never stored
Paranoid Self-Host / Airgapped Deployment

For security-conscious environments, airgapped networks, or self-hosted deployments, the following domains must be allowed through your firewall or proxy. Domains are grouped by functionality and criticality.

CDN / Static Assets (Required for UI)
DomainPurpose
cdn.jsdelivr.netBootstrap CSS/JS, marked.js, DOMPurify, js-yaml, Chart.js
cdnjs.cloudflare.comFont Awesome icons
unpkg.comLeaflet maps library
GitHub APIs (Core Functionality)
DomainPurpose
api.github.comREST API and GraphQL API for SBOM fetching
raw.githubusercontent.comRaw file access for FUNDING.yml info
github.comUser-facing links (non-essential for analysis)
Vulnerability Database
DomainPurpose
api.osv.devOSV vulnerability lookups
Package Registries (Dependency Resolution)
DomainPurpose
registry.npmjs.orgnpm package metadata (authors, funding, repository URL)
pypi.orgPyPI package metadata (authors, project URLs)
crates.ioCargo/Rust package metadata
rubygems.orgRubyGems package metadata
packages.ecosyste.msCross-ecosystem package metadata (Maven, NuGet, Go, etc.) — used for ecosystems where the native registry blocks browser CORS (Maven Central) or has no JSON API
api.deps.devGoogle deps.dev API — licenses, version graphs, and labeled SOURCE_REPO / HOMEPAGE / ISSUE_TRACKER links for every supported ecosystem
Map Services (Optional - Authors Page)
DomainPurpose
tile.openstreetmap.orgMap tiles for author locations
nominatim.openstreetmap.orgGeocoding service
Analytics (Optional - Can Be Blocked)
DomainPurpose
plausible.ioPrivacy-focused analytics (optional)
Fully Airgapped Environments: For complete network isolation, you can self-host the CDN assets (Bootstrap, Font Awesome, Leaflet) and modify the HTML files to reference your local copies. The tool will function with reduced features if package registries are unavailable.
How It Works
  1. Input: Enter a GitHub organization name, username, repository, or GitHub URL
  2. SBOM Fetching: The tool queries GitHub's Dependency Graph API to retrieve SBOM data for public repositories
  3. Dependency Resolution: Full dependency trees are resolved by querying package registries (npm, PyPI, crates.io, etc.)
  4. Analysis: Dependencies are analyzed for vulnerabilities (OSV.dev), licenses, authors, and quality metrics
  5. Storage: Results are stored locally in your browser's IndexedDB for future reference
  6. Visualization: View results across multiple pages: Overview, Licenses, Vulnerabilities, Quality, Dependencies, and Authors
Direct vs Transitive Classification & Dependency Depth

Every dependency in a portfolio is classified per consuming repository as either direct (the repo's own code declares it) or transitive (the repo only inherits it through the dependency tree of something else it declared). The same package can be direct in one repo and transitive in another. SBOM Play computes this attribution centrally with a deterministic two-pass algorithm so every page that splits Direct vs Transitive — Licenses, Vulnerabilities, Dependencies, Findings — reads from the same source of truth.

Definitions:
Direct A dependency the repository declares itself (e.g., listed in package.json, pom.xml, requirements.txt, or .github/workflows/*.yml) — the team consciously brought it in.
Transitive A dependency pulled in only because a direct dependency required it — the team didn't choose it, but they ship it.
Depth Distance from the repo's own package in the resolved dependency tree (depth 1 = direct, depth 2+ = transitive).
Pass 1 — SBOM Truth

For every repository in the portfolio, walk that repo's SBOM-declared dependencies and consult its directDependencies set (built from the SBOM's DEPENDS_ON relationships from the root package). Each (dep, repo) pair is added as direct in that repo if the SBOM declared it as a top-level edge from the repository, and as transitive in that repo otherwise.

Pass 2 — Per-Repo BFS Through the Tree

Some transitives never appear in the SBOM at all — they're discovered later when SBOM Play walks each direct dep's full version graph through the registry resolver (deps.dev, packages.ecosyste.ms). For every repo we BFS from its direct seed set through each dep's child registry edges and mark every reached (dep, repo) pair transitive in that repo. Pass 1 always wins on direct classification — Pass 2 never overwrites a direct mark with a transitive one.

Why per-repo BFS, not per-ecosystem flood? Earlier versions of the attribution pass mutated directIn / transitiveIn inside each ecosystem's resolver loop. When npm and Maven resolved concurrently, a Maven dep with no SBOM-listed repos got attributed to repos that only had npm direct deps (cross-ecosystem leak). The per-repo BFS scope is naturally per-ecosystem because dep.children only contains same-ecosystem children, so this class of leak is impossible by construction.
Why we don't trust GitHub-flat SBOMs alone

GitHub's dependency-graph SBOMs flatten the tree — every DEPENDS_ON relationship goes from the repo's main package, so the SBOM marks every listed dep as a direct dep of the repo, including transitives. If we only trusted the SBOM (Pass 1), the Direct/Transitive split for GitHub-fetched portfolios would be meaningless. Pass 2's BFS through resolver-discovered edges is what recovers the real shape — anything reached only via dep.children is transitive, regardless of what the flat SBOM said.

Imputed Direct (cross-portfolio inference)

For organisation-wide aggregations, a dep that's transitive in every repo it touches can still be considered "imputed-directly used" by the org if the package is also directly declared in any other repo in the portfolio (or a sister portfolio sharing the same lockfile family). This is a forward-looking concept that the forthcoming Insights page surfaces — it gives M&A and CTO audiences a more honest picture of "what the org actually owns" beyond SBOM-listed direct edges.

References
Tech-Debt Composite Scoring

The Insights page computes a single Tech-Debt composite score (0-100, higher is healthier) from six weighted sub-components. Direct dependencies carry weight compared to transitive ones, reflecting the higher risk and remediation urgency of code you directly control.

Component Weights
ComponentWeightWhat it measures
Version Drift30%Major/minor updates behind, 3× weight for direct deps
Vulnerabilities30%Severity-weighted CVE count (C=30, H=12, M=3 for direct; C=10, H=4, M=1 for transitive)
Package Age15%Fraction of deps with last release > 2 years ago
License Risk10%Fraction of deps with copyleft/high-risk licenses
EOL Risk10%Fraction of deps flagged as probable EOL (staleness heuristic or endoflife.date)
Repo Hygiene5%Fraction of repos with D/F SBOM grade or missing SBOM
SBOM Quality excluded: the composite deliberately omits SBOM quality because users cannot control the NTIA-completeness fields that GitHub auto-generates.
Grading Scale
GradeHealth Score Range
A90 – 100
B75 – 89
C55 – 74
D35 – 54
F0 – 34
Visualization

Charts on the Insights page are rendered with Chart.js 4.4, loaded from the jsdelivr CDN. Theme-aware colors are driven by --chart-text-color and --chart-grid-color CSS custom properties.

References
End-of-Life (EOX) Detection Methodology

SBOM Play uses multiple data sources and heuristics to identify end-of-life (EOL) and end-of-support (EOS) dependencies. Understanding which software components are no longer maintained is critical for supply chain security.

1. Confirmed EOL/EOS (Primary Source)

We query the endoflife.date API for official end-of-life information. This database tracks EOL dates for major software products including:

  • Programming languages: Python, Node.js, Ruby, PHP, Go, Java, .NET, Rust
  • Frameworks: Django, Rails, Laravel, Angular, React, Vue, Spring
  • Databases: PostgreSQL, MySQL, MongoDB, Redis, Elasticsearch
  • Infrastructure: Kubernetes, Docker, Terraform, Nginx, Apache
Definitions:
EOL End-of-Life: No updates of any kind (features, bug fixes, or security patches)
EOS End-of-Support: No security updates, but may still receive critical bug fixes
2. Probable EOL (Staleness Heuristic)

When official EOL data is unavailable, we use staleness analysis to infer probable abandonment. Research shows that packages without updates for extended periods are likely unmaintained.

ThresholdClassificationRationale
24+ months (2 years) Probable EOL Most active packages release at least once per year. 2 years of inactivity suggests abandonment.
36+ months (3 years) Highly Likely EOL Very high confidence of abandonment. Security vulnerabilities likely unpatched.

Note: This heuristic has limitations. Some stable packages (e.g., small utilities) may be "complete" and not require updates. We recommend manual review for probable EOL findings.

3. Archived Repositories

When a GitHub repository is archived, it's a clear signal from the maintainer that the project is no longer active. We detect dependencies whose source repositories have been archived and flag them accordingly.

Archived = Definitive Signal: Unlike staleness (which is probabilistic), an archived repository is an explicit declaration by the maintainer that development has ceased.
References
Dependency Confusion Detection Methodology

Dependency confusion (also known as "namespace confusion" or "substitution attacks") is a supply chain attack where an attacker publishes a malicious package to a public registry with the same name as an internal/private package. When developers or CI/CD systems resolve dependencies, they may inadvertently download the malicious public package instead of the intended private one.

Real-World Impact: This attack vector has been used successfully against major organizations including Apple, Microsoft, PayPal, Shopify, Netflix, and many others, as disclosed by security researcher Alex Birsan in 2021.
How SBOM Play Detects Dependency Confusion

SBOM Play uses a multi-layered approach to detect potential dependency confusion vulnerabilities in your SBOMs:

Detection TypeSeverityDescription
Namespace Not Found HIGH The namespace/organization (e.g., @mycompany/ in npm or com.mycompany in Maven) doesn't exist in the public registry. An attacker could register the entire namespace and publish malicious packages. This is the highest-confidence indicator.
Package Not Found MEDIUM The specific package doesn't exist in the public registry. Could be a private/internal package that an attacker could claim by publishing a package with that name.
GitHub Action Missing HIGH For GitHub Actions, the owner/organization doesn't exist on GitHub. An attacker could create the organization and publish a malicious action.
Supported Registries (36+)

SBOM Play checks packages against 36+ public registries via the ecosyste.ms API, plus GitHub for Actions:

  • npm - JavaScript/Node.js
  • PyPI - Python
  • Crates.io - Rust
  • RubyGems - Ruby
  • Maven Central - Java
  • NuGet - .NET
  • Go Proxy - Go modules
  • Packagist - PHP/Composer
  • CocoaPods - iOS/macOS
  • Swift Package Index - Swift
  • Hex.pm - Elixir/Erlang
  • Pub.dev - Dart/Flutter
  • CPAN - Perl
  • CRAN - R
  • Hackage - Haskell
  • Clojars - Clojure
  • Julia Registry - Julia
  • Elm Packages - Elm
  • Deno - Deno/TypeScript
  • Racket - Racket
  • Bower - Frontend JS
  • Homebrew - macOS packages
  • Conda - Python/R/C
  • vcpkg - C/C++
  • Bioconductor - R Bioinformatics
  • Puppet Forge - Puppet modules
  • Terraform Registry - Terraform
  • Ansible Galaxy - Ansible
  • Chef Supermarket - Chef
  • WordPress Plugins - WordPress
  • Drupal - Drupal modules
  • Alpine APK - Alpine Linux
  • GitHub Actions - CI/CD workflows
  • ...and more
Detection Process
  1. PURL Parsing: Extract ecosystem and package name from Package URLs (PURLs) in the SBOM
  2. Namespace Extraction: Parse scoped packages (e.g., @scope/package) to identify namespaces
  3. Registry Query: Check if namespace/package exists via ecosyste.ms or direct registry APIs
  4. GitHub Verification: For GitHub Actions, verify organization and repository exist via GitHub API
  5. Evidence Collection: Store API response URLs as proof for findings
Limitations:
  • Detection is based on public registry lookups - private registries are not checked
  • Some packages may be intentionally unpublished (deprecated) rather than private
  • Rate limits on registry APIs may affect detection completeness
  • False positives may occur for system packages (e.g., Python built-ins)
Mitigation Strategies
  • Namespace Squatting: Register your organization's namespace on public registries (even if unused)
  • Scoped Packages: Use organization scopes (e.g., @mycompany/) for all internal packages
  • Private Registry Priority: Configure package managers to check private registries first
  • Package Lock Files: Use lock files with integrity hashes to prevent substitution
  • Registry Allowlisting: Configure CI/CD to only allow packages from approved registries
References
Author Enrichment Methodology

For every package in the SBOM, SBOM Play attempts to attribute a list of authors — the humans (and bots) whose accounts can ship code into the package. This list is what powers the Authors page, the geographic risk map, the sanctioned-country detector, and the contributor correlation in the Findings report. Because no single registry exposes all the information we need, the pipeline draws from up to three sources per package, in a fixed precedence order, and every API call is made once per package and reused.

1. Source Precedence
OrderSourceWhat it gives usApplies to
1 Native registry (npm, PyPI, crates.io, RubyGems) Authoritative author / maintainers field, declared by the publisher Ecosystems with a public per-package authors API
2 packages.ecosyste.ms Aggregated maintainers / owners + repository_url + homepage Maven, NuGet, Go, Composer (no native authors API)
3 GitHub /repos/{owner}/{repo}/contributors (top 10) Tentative author list derived from commit-history contributors of the source repository Any package whose source URL points to github.com

Sources 1 and 2 are mutually exclusive (we use whichever is available for the ecosystem). Source 3 runs in addition to whichever of 1 or 2 returned data — registries publish only the maintainer-of-record, but real-world risk lives in the broader contributor population that can land code through pull requests.

2. Why GitHub contributors?

For a Maven artifact like org.springframework.boot:spring-boot-starter-web, the registry publishes a single "Pivotal" maintainer entry. The actual humans who can merge code into that package are the contributors of spring-projects/spring-boot on GitHub — currently around 1,000 people, of whom we capture the top 10 by contribution count. These are tentative correlations (we have no email/identity authority for them), so they're flagged with a ⚠️ badge in the Authors page so you can distinguish them from registry-attested maintainers.

3. Cost model

Per-run we issue exactly one /repos/{owner}/{repo}/contributors REST call per unique GitHub repository, deduped by an in-memory cache keyed on ${owner}/${repo}. Profile fields (location, company, GitHub user type) are filled in afterwards by a single batched GraphQL query — never per-user REST calls. So a 159-package Spring Boot SBOM where every package points at spring-projects/spring-boot issues 1 contributors call and a handful of GraphQL profile calls, not 159 + 1,590.

4. Shared-repository attribution
Definition: When several packages in the SBOM ship from the same source repository (a monorepo: spring-projects/spring-boot, babel/babel, angular/angular, …), all of those packages will list the same set of contributors as authors. This is intentional, not a bug.

From a threat-model perspective, those packages share a single supply-chain blast radius. A contributor with merge rights to the shared repository can land malicious code into every package that ships from it, in a single commit. Listing the contributors once per package (rather than once per repository) makes that fan-out explicit on the Authors page: an author shown as having "23 packages" across "1 repo" is actually a single point of failure for 23 packages, and that's exactly what the Repository Usage column on the Authors page surfaces with the High Risk / Moderate Risk badges.

The downside is visual repetition — a Spring-heavy SBOM will show the same ~10 Pivotal employees on every Spring artifact. The upside is risk fidelity: the count of packages those employees appear on directly reflects the number of packages an account compromise would compromise.

5. Bots and automated accounts

GitHub contributors include automated accounts: dependabot[bot], github-actions[bot], renovate[bot], pre-commit-ci[bot], and similar. We do not filter them out at the enrichment stage — instead the Authors page detects bot accounts (by the canonical [bot] suffix, GitHub's type: 'Bot' profile field, and a denylist of common automation accounts) and routes them to a dedicated Active Bots in the Environments section. The human authors table stays clean, while the bot inventory remains visible — bots that can ship code through PRs are part of the threat model and worth knowing about.

References
SBOM Compliance Standards

SBOM Play assesses SBOMs against multiple international compliance standards. Understanding these standards helps organizations ensure their SBOMs meet regulatory requirements.

CISA 2025 Minimum Elements (US Federal)

The Cybersecurity and Infrastructure Security Agency (CISA) updated SBOM requirements in August 2025, building upon the 2021 NTIA guidelines. Key additions marked with NEW.

ElementDescriptionStatus
Software ProducerEntity that creates components (was "Supplier Name")Renamed
Component NameName of each software componentRequired
Component VersionVersion identifierRequired
Software IdentifierPURL, CPE, OmniBOR, or SWHIDRequired
Component HashCryptographic fingerprint (SHA-256+)NEW
License InformationLegal terms for each componentNEW
Dependency RelationshipUpstream component relationshipsRequired
SBOM AuthorEntity that created the SBOM (was "Author of SBOM Data")Renamed
TimestampDate and time SBOM was assembledRequired
Tool NameTool used to generate SBOMNEW
Generation ContextPre-build, build, or post-build stageNEW
BSI TR-03183-2 v2.0 (German/EU)

The German Federal Office for Information Security (BSI) Technical Guideline provides requirements for SBOMs in the European context, with stricter requirements around cryptographic hashes.

RequirementDescription
SBOM SpecificationMust use CycloneDX 1.5+ or SPDX 2.2.1+
Creator ContactEmail or URL required for SBOM creator
Component HashSHA-256 minimum (stricter than CISA)
License ExpressionValid SPDX license expression required
SBOM URIUnique namespace or serial number
Source Code URILink to component source (where available)
References
SBOM Quality Assessment Methodology

Our quality scoring is aligned with sbomqs v2.0, an industry-leading SBOM quality assessment tool. The overall score is calculated from 7 weighted categories:

CategoryWeightWhat It Measures
Identification12%Component names, versions, unique identifiers (PURLs, CPEs)
Provenance15%SBOM author, timestamp, tool info, supplier data
Integrity18%Cryptographic hashes (SHA-256+), digital signatures
Completeness15%Dependencies, source URIs, component purpose
Licensing18%Valid SPDX expressions, concluded/declared licenses
Vulnerability12%Security references, vulnerability tracking readiness
Structural10%Spec version, format validity, schema compliance
Grading Scale:
A 90-100% | B 80-89% | C 70-79% | D 60-69% | F Below 60%
Showcased

SBOM Play has been featured, launched, and presented at the following venues:

Black Hat Europe 2025 Arsenal

December 2025 · London, UK

Arsenal Page
Black Hat Asia 2026 Arsenal

April 2026 · Singapore

Arsenal Page
DEF CON Singapore 2026 Demo Labs

April 28–30, 2026 · Marina Bay Sands, Singapore

Demo Labs Page
Peerlist Launchpad

Product Launch

Peerlist Project
Presentation Slides

Black Hat EU 2025

View Slides
Version Information

Current Version: 0.0.8

View Changelog

Credits & Technologies
Technologies Used
  • Bootstrap 5: UI framework
  • Font Awesome: Icons
  • IndexedDB: Local storage
  • GitHub API: SBOM data source
  • OSV.dev API: Vulnerability data
  • Package Registries: npm, PyPI, crates.io, RubyGems, Maven, etc.
Inspirations

The following open-source projects provided insights and inspiration for key features:

  • sbomqs - Quality scoring logic and SBOM assessment methodology
  • DepConfuse - Dependency confusion detection logic
Acknowledgments

This project was developed with the assistance of AI tools, most notably Cursor IDE and Claude Code. These tools helped accelerate development and improve velocity. All AI-generated code has been carefully reviewed and validated through human inspection to ensure it aligns with the project's intended functionality and quality standards.

Links & Resources