Skip to main content

Multi-ecosystem CycloneDX 1.6 SBOM scanner with HTML report and CVE scanning

Project description

SBOM Scanner

A generic SBOM and HTML report generator for software projects. Produces a CycloneDX 1.6 SBOM of all dependencies (including transitive) and an interactive HTML report with CVE scanning and version analysis.

Features

  • Multi-ecosystem — npm, PyPI, Dart/Flutter, Maven/Gradle, Rust/Cargo, yarn
  • Multiroot / Monorepo — multiple lockfiles per ecosystem, with labels and tags
  • Gradle subprojects — automatic detection and scanning of all submodules
  • Auto-detect — discovers ecosystems automatically from lockfiles
  • Auto-configurator — recursively scans projects and generates config interactively (fancy TUI or simple text mode)
  • CVE scanning — pluggable scanner architecture with grype and osv-scanner built in
  • Latest-version check — parallel lookups against npm, PyPI, crates.io, Maven Central, Google Maven
  • HTML report — tabs, search, filters (dependency type, module, status), dark mode, print styles
  • Dependency tree — expandable, animated, outdated descendants bubble up, sub-tabs per ecosystem/module
  • PDF export — via weasyprint, Chrome headless, or wkhtmltopdf
  • Two report modes — interactive (tabs/JS) with --simple fallback (flat, no JS)
  • Fully modular — new ecosystem or CVE scanner = one file + registry entry, no changes to the report generator
  • Library API — directly importable (from sbom_scanner import generate_sbom, generate_report)
  • i18n — English by default, German translation included (--lang de)
  • Installablepip install with three CLI commands

Quickstart

# Install
pip install sbom-scanner[all]

# 1. Generate config (interactive — scans the project and asks)
sbom configure --project-dir /path/to/project

# 2. Generate SBOM
sbom scan --project-dir /path/to/project

# 3. Generate HTML report
sbom report --project-dir /path/to/project

# Open report
open /path/to/project/sbom-report.html

Step 1 is optional — without a config file, ecosystems are auto-detected with default paths. Config is recommended for monorepos or subdirectory layouts.

Installation

# From PyPI (once published)
pip install sbom-scanner[all]

# From the repository
pip install .[all]

# For development
pip install -e .[all]

Optional extras:

Extra Installs When needed
yaml PyYAML YAML config and Dart/Flutter
python pipdeptree Transitive Python deps and dependency graph
pdf weasyprint PDF export (alternative: Chrome headless)
tui rich, InquirerPy Fancy interactive configurator
all PyYAML + pipdeptree + rich + InquirerPy Recommended

External tools (optional):

Tool When needed
grype CVE scanning
osv-scanner CVE scanning
Google Chrome / Chromium PDF export (alternative to weasyprint)
dart CLI Dart/Flutter outdated check

Configuration

Create an sbom.config.yaml in the project directory (or let sbom configure generate it):

Minimal config

project:
  name: my-app
  version: "1.0.0"

Full config

project:
  name: my-app
  version: "2.0.0"
  description: "My application"

# Only specify when paths differ from defaults.
# Omit = auto-detect with default paths.
sources:
  npm:
    package_json: package.json        # default
    lockfile: package-lock.json       # default (also supports yarn.lock)
  pypi:
    requirements: requirements.txt    # default (pip-compile or plain)
  pub:
    pubspec_yaml: pubspec.yaml        # default
    pubspec_lock: pubspec.lock        # default
  maven:
    gradle_dir: .                     # default (root with build.gradle)
    configuration: runtimeClasspath   # default
  cargo:
    cargo_toml: Cargo.toml            # default
    lockfile: Cargo.lock              # default

output:
  sbom: sbom.cyclonedx.json
  report: sbom-report.html

options:
  skip_cve: false
  pdf: false
  simple: false
  workers: 20

Subdirectory paths

When lockfiles are not in the project root:

project:
  name: my-fullstack-app
  version: "1.0.0"

sources:
  pypi:
    requirements: backend/requirements.txt
  npm:
    package_json: frontend/package.json
    lockfile: frontend/package-lock.json

Multiroot / Monorepo

Multiple instances of the same ecosystem as a list:

project:
  name: my-monorepo
  version: "3.0.0"

sources:
  npm:
    - label: frontend
      package_json: apps/frontend/package.json
      lockfile: apps/frontend/package-lock.json
      tags: [web, react]
    - label: admin
      package_json: apps/admin/package.json
      lockfile: apps/admin/package-lock.json
      tags: [internal]
  cargo:
    cargo_toml: services/api/Cargo.toml
    lockfile: services/api/Cargo.lock
  • label — Display name in the report. Fallback: derived from the directory path when multiple entries exist.
  • tags — Applied as CycloneDX tags to each component. Shown as badges in the report and available in the SBOM JSON for automation.

Gradle subprojects

Gradle projects with submodules are detected automatically. All subprojects (:client, :server, :common etc.) are scanned individually. Module filters and dependency tree sub-tabs appear in the report.

sources:
  maven:
    gradle_dir: .
    configuration: runtimeClasspath   # optional, default: runtimeClasspath

Auto-Configurator

Instead of writing the config manually:

sbom configure --project-dir /path/to/project

With rich + InquirerPy installed, you get a fancy TUI with cursor navigation, status table, and options submenu. Without these dependencies, a simple text menu is used as fallback (--simple forces it).

Features:

  • Recursively scans, skips node_modules, target, .venv etc.
  • Suggests labels from directory names
  • Reads project name/version from manifest files
  • Loads existing config for editing
  • Options submenu: CVE scan, PDF, simple report, workers
  • --non-interactive accepts all defaults

CLI Reference

sbom configure

sbom configure [--project-dir DIR] [--output FILE] [--non-interactive] [--simple] [--lang LANG]
Flag Default Description
--project-dir . Project directory to scan
--output sbom.config.yaml Output path (relative to project-dir)
--non-interactive false Accept all defaults
--simple false Force simple text menu (no TUI)
--lang en Language (en, de)

sbom scan

sbom scan [--project-dir DIR] [--config FILE] [--output FILE] [--lang LANG]
Flag Default Description
--project-dir . Project directory
--config sbom.config.yaml Configuration file (in project-dir)
--output from config or sbom.cyclonedx.json Output path
--lang en Language (en, de)

sbom report

sbom report [--project-dir DIR] [--config FILE] [--sbom FILE] [--output FILE] [--skip-cve] [--pdf] [--simple] [--lang LANG]
Flag Default Description
--project-dir . Project directory
--config sbom.config.yaml Configuration file
--sbom from config or sbom.cyclonedx.json Input SBOM
--output from config or sbom-report.html Output path
--skip-cve false Skip CVE scanning
--pdf false Additionally generate PDF (uses simple report)
--simple false Simple report without tabs, search, and filters
--lang en Language (en, de)

Library API

Directly importable in Python:

from pathlib import Path
from sbom_scanner import generate_sbom, generate_report, load_config

project = Path("../my-project")
config = load_config(project / "sbom.config.yaml")
sbom_path = project / "sbom.cyclonedx.json"

# Generate SBOM
generate_sbom(project, config, sbom_path)

# Generate HTML report
generate_report(sbom_path, project / "sbom-report.html", skip_cve=True)

Supported Ecosystems

Ecosystem Lockfile Registry Detection
npm package-lock.json (v1/v2/v3), yarn.lock (v1) npmjs.org package.json + lockfile
PyPI requirements.txt (pip-compile or plain) pypi.org requirements.txt
Dart/Flutter pubspec.lock pub.dev pubspec.yaml + pubspec.lock
Maven/Gradle build.gradle / build.gradle.kts Maven Central + Google Maven build.gradle + gradlew
Rust/Cargo Cargo.lock crates.io Cargo.toml + Cargo.lock

PyPI notes:

  • pip-compile format (with # via comments) — automatically detects direct vs. transitive
  • Plain requirements.txt — all packages as direct, transitive via pipdeptree (if installed)

Maven/Gradle notes:

  • Subprojects are automatically detected and scanned individually
  • Module membership (:client, :server etc.) shown as filter in the report
  • Also supports pre-computed gradle-dependencies.json (custom Gradle task)

Adding a New Ecosystem

Just one file + registry entry. No changes to the report generator needed.

  1. Create a new file, e.g. src/sbom_scanner/ecosystems/composer.py:
from .base import Ecosystem

class ComposerEcosystem(Ecosystem):
    # Identification
    name = "composer"
    display_name = "PHP/Composer"
    cdx_prefix = "cdx:composer"
    purl_type = "composer"

    # Report configuration
    package_url_template = "https://packagist.org/packages/{name}"
    dep_property = "cdx:composer:dependency"
    latest_property = "cdx:composer:latestVersion"
    dep_labels = {"direct main": "direct", "transitive": "transitive"}
    has_group_column = False

    # Auto-configurator pattern
    def scan_pattern(self):
        return {
            "detect_files": ["composer.lock"],
            "companion_files": ["composer.json"],
            "config_keys": {"composer.lock": "lockfile", "composer.json": "composer_json"},
            "icon": "🐘",
        }

    # Required methods
    def detect(self, project_dir, config): ...
    def parse(self, project_dir, config): ...
    def fetch_latest_versions(self, packages, workers=20): ...
    def build_component(self, pkg, latest): ...
    def get_direct_purls(self, packages): ...

    # Optional
    def parse_dependency_graph(self, project_dir, config, packages):
        return []
  1. Register in src/sbom_scanner/ecosystems/__init__.py:
from .composer import ComposerEcosystem

REGISTRY: list = [
    ...
    ComposerEcosystem(),
]

That's it. The report generator reads all properties from the class — tabs, filters, badges, tree work automatically. The auto-configurator discovers it via scan_pattern().

Adding a New CVE Scanner

Same architecture — one file + registry entry.

  1. Create src/sbom_scanner/scanners/trivy.py:
from .base import Scanner

class TrivyScanner(Scanner):
    name = "trivy"
    targets = ["*"]  # or ["npm", "pypi"] for specific ecosystems

    def is_available(self): ...
    def scan(self, sbom_path, lockfiles, project_dir): ...
  1. Register in src/sbom_scanner/scanners/__init__.py.

Built-in scanners:

Scanner Type Targets Description
grype SBOM-based * Scans the CycloneDX SBOM
osv Lockfile-based * Scans lockfiles against the OSV database

Project Structure

sbom-scanner/
├── pyproject.toml
├── README.md
├── LICENSE                             # MIT
├── CONTRIBUTING.md
├── sbom.config.yaml                    # Example config
└── src/
    └── sbom_scanner/
        ├── __init__.py                 # Version + library API
        ├── i18n.py                     # Internationalization (gettext)
        ├── configure.py                # sbom configure (TUI + simple fallback)
        ├── generate_sbom.py            # sbom scan (ecosystem-agnostic)
        ├── generate_sbom_report.py     # sbom report (ecosystem-agnostic)
        ├── ecosystems/
        │   ├── __init__.py             # Registry
        │   ├── base.py                 # Base class (Ecosystem)
        │   ├── npm.py                  # npm + yarn
        │   ├── pypi.py                 # PyPI + pipdeptree
        │   ├── pub.py                  # Dart/Flutter
        │   ├── maven.py                # Maven/Gradle + subprojects
        │   └── cargo.py                # Rust/Cargo
        ├── scanners/
        │   ├── __init__.py             # Registry
        │   ├── base.py                 # Base class (Scanner)
        │   ├── grype.py                # grype (SBOM-based)
        │   └── osv.py                  # osv-scanner (lockfile-based)
        └── locales/
            └── de/LC_MESSAGES/         # German translation (.po/.mo)

CI/CD Integration

GitLab CI example:

sbom:
  stage: test
  script:
    - pip install sbom-scanner[all]
    - sbom scan --project-dir .
    - sbom report --project-dir . --skip-cve
  artifacts:
    paths:
      - sbom.cyclonedx.json
      - sbom-report.html

With CVE scanning:

sbom:
  stage: test
  image: python:3.13
  before_script:
    - pip install sbom-scanner[all]
    - curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh -s -- -b /usr/local/bin
  script:
    - sbom scan --project-dir .
    - sbom report --project-dir .
  artifacts:
    paths:
      - sbom.cyclonedx.json
      - sbom-report.html

License

MIT — © 2026 Frmwrk GmbH

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sbom_scanner-0.0.1.tar.gz (50.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

sbom_scanner-0.0.1-py3-none-any.whl (62.9 kB view details)

Uploaded Python 3

File details

Details for the file sbom_scanner-0.0.1.tar.gz.

File metadata

  • Download URL: sbom_scanner-0.0.1.tar.gz
  • Upload date:
  • Size: 50.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sbom_scanner-0.0.1.tar.gz
Algorithm Hash digest
SHA256 5fa5c3ee7d1e2c8f5bdce4085ff43fc65a1f427fe9b0b59202be2b846e5c966f
MD5 065c7b44a9a88cae8841e65ca7a7157c
BLAKE2b-256 50145d1e2561a9964e9dd8c0777d8853f8c6525c23d52b10c31d14a7c6c2151a

See more details on using hashes here.

Provenance

The following attestation bundles were made for sbom_scanner-0.0.1.tar.gz:

Publisher: pypi.yml on Frmwrk-GmbH/sbom-scanner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sbom_scanner-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: sbom_scanner-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 62.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for sbom_scanner-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 7de29fa2d42ee8866b7ae4c30ceb4994c8f59fff3611d5f0b9182b1a7bf5be6b
MD5 31414e9ed0bac92c1129783ce8235324
BLAKE2b-256 5e7180176f330f733c2ea05aeea0219f70bee79b2f79e6ca20bb07339a0a8d85

See more details on using hashes here.

Provenance

The following attestation bundles were made for sbom_scanner-0.0.1-py3-none-any.whl:

Publisher: pypi.yml on Frmwrk-GmbH/sbom-scanner

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page