SBOM Play
About SBOM Play
Methodology, compliance standards, and how every metric is computed.
Contents
click to collapse
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
Security Findings the Tool Detects
Issues surfaced on the Findings page fall into the categories below. Each category groups one or more rule ids; severity is set per rule based on the underlying risk and how recoverable it is.
Malicious Packages critical
KNOWN_MALICIOUS_PACKAGE— a dependency matches an OSVMAL-*advisory or the OpenSSF Malicious Packages dataset. Remove or replace immediately and rotate any secrets that may have been exposed.
GitHub Actions — workflow-level (fixable in your repo)
MUTABLE_TAG_REFERENCE— action referenced by a mutable tag instead of a 40-char commit SHA. high for branch/latest-style tags (main,master,latest); medium for version tags (v2,v2.3.4) — both can be re-pointed by the publisher.PULL_REQUEST_TARGET_CHECKOUT— workflow usespull_request_targetand checks out PR code, which executes untrusted code with the elevated permissions of the target branch.EXCESSIVE_WORKFLOW_PERMISSIONS/EXCESSIVE_JOB_PERMISSIONS— workflow or job grants broadwrite-allinstead of least-privilege scoping.POTENTIAL_HARDCODED_SECRET— environment variable value looks like an API key or token. Should be a GitHub Secret reference, not a literal.
Unpinnable Actions — not fixable by SHA-pinning alone
Patterns from Palo Alto's "Unpinnable Actions" research. Even when your workflow pins the action by commit SHA, the action's runtime still pulls mutable resources from elsewhere, so the SHA pin alone does not protect you. Remediation usually means picking a different action or asking the maintainer to pin their own runtime dependencies.
DOCKER_IMPLICIT_LATEST/DOCKER_FLOATING_TAG— the action'saction.ymlreferences a Docker image by a floating tag (no@sha256:digest), so an attacker who pushes a new image to the same tag is automatically picked up by the next run.DOCKERFILE_FLOATING_BASE_IMAGE— the action ships a Dockerfile whoseFROMis a floating tag (e.g.ubuntu:22.04with no digest). Findings link directly to the offendingFROMline at the commit your workflow pins.DOCKER_UNPINNED_DEPENDENCIES/DOCKER_REMOTE_CODE_NO_INTEGRITY— the action's Dockerfileapt/pip/npm installs unpinned versions, orcurl | bashes remote code without checksum verification.COMPOSITE_NESTED_UNPINNED_ACTION— a composite action's internaluses:references a child action by tag instead of SHA, breaking the pinning chain.COMPOSITE_UNPINNED_DEPENDENCIES/COMPOSITE_REMOTE_CODE_NO_INTEGRITY— composite action'srun:steps install unpinned packages or fetch remote scripts without integrity checks.UNPINNED_PACKAGE_INSTALL/REMOTE_CODE_NO_INTEGRITY— a JavaScript action installs unpinned npm packages or downloads scripts at runtime without checksum verification.INDIRECT_UNPINNABLE_ACTION— an action you pinned transitively pulls in one of the patterns above through a nested action.
Dependency Confusion / Hijacking
NAMESPACE_NOT_IN_REGISTRYhigh — the package's namespace/scope/group is not registered on the public registry. An attacker could claim the whole namespace and any package under it.PACKAGE_NOT_IN_REGISTRY— the package name itself is not present in the public registry; likely a private/internal package that is vulnerable to confusion attacks if an attacker registers the same name publicly.
See the Dependency Confusion methodology section below for how each ecosystem is checked and which registries are queried.
End-of-Life / End-of-Support
EOLhigh — confirmed end-of-life via endoflife.date. No security updates of any kind.EOSmedium — end of regular support (often security-only patches remain).HIGHLY_LIKELY_EOLhigh — staleness heuristic: no new release in 3+ years.PROBABLE_EOLmedium — staleness heuristic: no new release in 2–3 years.
See the EOX Detection Methodology section below for thresholds, data sources, and the ecosystem-aware product-mapping logic.
Abandoned / Dead Source Repos
REPO_NOT_FOUNDmedium — the source repository URL recorded in the SBOM 404s. The repo was deleted, renamed, or never published. If the slot is later re-registered by someone else, the package can be hijacked through release infrastructure tied to that repo.
Privacy & Security
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
Self-hosted & offline 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)
| Domain | Purpose |
|---|---|
cdn.jsdelivr.net | Bootstrap CSS/JS, marked.js, DOMPurify, js-yaml, Chart.js |
cdnjs.cloudflare.com | Font Awesome icons |
unpkg.com | Leaflet maps library |
GitHub APIs (Core Functionality)
| Domain | Purpose |
|---|---|
api.github.com | REST API and GraphQL API for SBOM fetching |
raw.githubusercontent.com | Raw file access for FUNDING.yml info |
github.com | User-facing links (non-essential for analysis) |
Vulnerability Database
| Domain | Purpose |
|---|---|
api.osv.dev | OSV vulnerability lookups |
Package Registries (Dependency Resolution)
| Domain | Purpose |
|---|---|
registry.npmjs.org | npm package metadata (authors, funding, repository URL) |
pypi.org | PyPI package metadata (authors, project URLs) |
crates.io | Cargo/Rust package metadata |
rubygems.org | RubyGems package metadata |
packages.ecosyste.ms | Cross-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.dev | Google deps.dev API — licenses, version graphs, and labeled SOURCE_REPO / HOMEPAGE / ISSUE_TRACKER links for every supported ecosystem |
Map Services (Optional - Authors Page)
| Domain | Purpose |
|---|---|
tile.openstreetmap.org | Map tiles for author locations |
geocoding-api.open-meteo.com | Primary geocoding API (fallback after static country/city tables) |
photon.komoot.io | Secondary geocoding API (failover when Open-Meteo is rate-limited or unavailable) |
Analytics (Optional - Can Be Blocked)
| Domain | Purpose |
|---|---|
plausible.io | Privacy-focused analytics (optional) |
How It Works
- Input: Enter a GitHub organization name, username, repository, or GitHub URL
- SBOM Fetching: The tool queries GitHub's Dependency Graph API to retrieve SBOM data for public repositories
- Dependency Resolution: Full dependency trees are resolved by querying package registries (npm, PyPI, crates.io, etc.)
- Analysis: Dependencies are analyzed for vulnerabilities (OSV.dev), licenses, authors, and quality metrics
- Storage: Results are stored locally in your browser's IndexedDB for future reference
- 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.
• 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.
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 3× weight compared to transitive ones, reflecting the higher risk and remediation urgency of code you directly control.
Component Weights
| Component | Weight | What it measures |
|---|---|---|
| Version Drift | 30% | Major/minor updates behind, 3× weight for direct deps |
| Vulnerabilities | 30% | Severity-weighted CVE count (C=30, H=12, M=3 for direct; C=10, H=4, M=1 for transitive) |
| Package Age | 15% | Fraction of deps with last release > 2 years ago |
| License Risk | 10% | Fraction of deps with copyleft/high-risk licenses |
| EOL Risk | 10% | Fraction of deps flagged as probable EOL (staleness heuristic or endoflife.date) |
| Repo Hygiene | 5% | Fraction of repos with D/F SBOM grade or missing SBOM |
Grading Scale
| Grade | Health Score Range |
|---|---|
| A | 90 – 100 |
| B | 75 – 89 |
| C | 55 – 74 |
| D | 35 – 54 |
| F | 0 – 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
Risk Portfolio Scoring
At the top of the Insights page, the same underlying metrics that feed the Tech-Debt composite are re-projected into three risk domains aimed at due-diligence and M&A audiences. Each domain gets an independent 0–100 score (higher is healthier) and a traffic-light verdict, while the overall Tech-Health Score shown in the header is the Tech-Debt composite score and grade (see Tech-debt scoring).
Domain Scores
| Domain | Formula (starts at 100, penalties subtracted) |
|---|---|
| Security Posture | −20 per critical vuln, −8 per high vuln, −25 per malware advisory, and −15 / −8 when the median direct-dep CVE dwell time exceeds 90 / 30 days. |
| Operational Health | −3 per EOL component, −5 per repo inactive > 1 year, −3 per repo missing an SBOM (major-drift components are folded in as a partial offset). |
| License & Compliance | −8 per high-risk license, −2 per unpinned GitHub Action. |
Verdict Thresholds
| Traffic light | Penalty (100 − score) | Overall verdict (Tech-Health) |
|---|---|---|
| Good | ≤ 30 | ≥ 75: low risk — standard monitoring |
| Moderate | 31 – 55 | 55 – 74: moderate risk — plan remediation |
| Needs Attention | > 55 | < 55: elevated risk — active remediation |
On-Device AI (Gemini Nano)
The Agent page (linked from Insights) uses Chrome’s built-in Prompt API with Gemini Nano. All prompts, context, and model output stay on your machine — SBOM Play does not upload your analysis to a cloud LLM for this feature.
How it works
The Agent runs an app-driven tool-use loop: JavaScript executes typed
query_sbom() handlers (vulnerabilities, licenses, drift, EOL, supply chain, etc.)
against in-memory stored analysis, then asks Gemini Nano to summarize those JSON results.
Answers deep-link packages, repositories, authors, and advisory IDs into the matching pages.
Availability
Requires a Chromium browser with the Prompt API and Gemini Nano enabled (see Chrome Prompt API documentation). When the model is missing or unsupported, the page shows a non-blocking “AI not available” state; the rest of SBOM Play continues to work offline from IndexedDB.
js/ai-nano.js) targets the modern LanguageModel API and
falls back to the legacy window.ai.languageModel where needed.
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
• 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.
| Threshold | Classification | Rationale |
|---|---|---|
| 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.
References
- endoflife.date API Documentation
- OpenEOX Standard - Emerging standard for EOL data exchange
- GitHub: Archiving Repositories
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.
How SBOM Play Detects Dependency Confusion
SBOM Play uses a multi-layered approach to detect potential dependency confusion vulnerabilities in your SBOMs:
| Detection Type | Severity | Description |
|---|---|---|
| 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
- PURL Parsing: Extract ecosystem and package name from Package URLs (PURLs) in the SBOM
- Namespace Extraction: Parse scoped packages (e.g.,
@scope/package) to identify namespaces - Registry Query: Check if namespace/package exists via ecosyste.ms or direct registry APIs
- GitHub Verification: For GitHub Actions, verify organization and repository exist via GitHub API
- Evidence Collection: Store API response URLs as proof for findings
- 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
- Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies - Alex Birsan's original disclosure
- DepConfuse - Original dependency confusion detection tool that inspired our implementation
- ecosyste.ms API Documentation - Registry aggregation API
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
| Order | Source | What it gives us | Applies 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
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. Location geocoding
GitHub profile location strings are free-form text — "San Francisco, CA", "Germany",
"Bengaluru", or joke entries like "Milky Way". SBOM Play resolves them to an ISO country code for
the Authors map and sanctioned-country checks using a four-step cascade that avoids hammering any
single public geocoder:
| Step | Resolver | When it applies |
|---|---|---|
| 1 | Static country table (country-data.js) |
Trailing segment is a country name, alpha-2/3 code, or US state (e.g. "Berlin, Germany") |
| 2 | Static city table (city-data.js, GeoNames cities ≥50k pop.) |
Bare major-city names with no cross-country ambiguity (e.g. "Tokyo", "Bengaluru") |
| 3 | Open-Meteo Geocoding API | Remaining strings; primary network fallback, no API key required |
| 4 | Photon (komoot) | Failover when Open-Meteo returns no result or is rate-limited |
All successful and failed resolutions are cached in IndexedDB for 7 days so each unique string is fetched at most once per session. Network requests are serialized through a shared queue with per-provider spacing and cooldown on HTTP 429/503. The Authors map itself never triggers geocoding — it reads only from cache and static tables. Nominatim is no longer used.
6. 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
- GitHub REST API: List repository contributors
- ecosyste.ms API Documentation — aggregated package metadata for ecosystems without a native authors endpoint
- npm package.json:
authorandcontributorsfields - PyPI core metadata:
Author/Maintainerfields - GeoNames — static major-city table (
cities15000, CC BY 4.0) - Open-Meteo Geocoding API — primary network geocoding fallback
- Photon (komoot) — secondary geocoding failover
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.
| Element | Description | Status |
|---|---|---|
| Software Producer | Entity that creates components (was "Supplier Name") | Renamed |
| Component Name | Name of each software component | Required |
| Component Version | Version identifier | Required |
| Software Identifier | PURL, CPE, OmniBOR, or SWHID | Required |
| Component Hash | Cryptographic fingerprint (SHA-256+) | NEW |
| License Information | Legal terms for each component | NEW |
| Dependency Relationship | Upstream component relationships | Required |
| SBOM Author | Entity that created the SBOM (was "Author of SBOM Data") | Renamed |
| Timestamp | Date and time SBOM was assembled | Required |
| Tool Name | Tool used to generate SBOM | NEW |
| Generation Context | Pre-build, build, or post-build stage | NEW |
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.
| Requirement | Description |
|---|---|
| SBOM Specification | Must use CycloneDX 1.5+ or SPDX 2.2.1+ |
| Creator Contact | Email or URL required for SBOM creator |
| Component Hash | SHA-256 minimum (stricter than CISA) |
| License Expression | Valid SPDX license expression required |
| SBOM URI | Unique namespace or serial number |
| Source Code URI | Link 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:
| Category | Weight | What It Measures |
|---|---|---|
| Identification | 12% | Component names, versions, unique identifiers (PURLs, CPEs) |
| Provenance | 15% | SBOM author, timestamp, tool info, supplier data |
| Integrity | 18% | Cryptographic hashes (SHA-256+), digital signatures |
| Completeness | 15% | Dependencies, source URIs, component purpose |
| Licensing | 18% | Valid SPDX expressions, concluded/declared licenses |
| Vulnerability | 12% | Security references, vulnerability tracking readiness |
| Structural | 10% | Spec version, format validity, schema compliance |
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:
Version Information
Current Version: 0.2.1
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.