Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1042,13 +1042,15 @@ sbomify uses a plugin architecture for SBOM generation, automatically selecting

Generators are tried in priority order. Native tools (optimized for specific ecosystems) are preferred over generic scanners. Each tool supports different ecosystems:

| Priority | Generator | Supported Ecosystems | Output Formats |
| -------- | ------------------- | -------------------------------------------------------------------------------------------------------------- | ------------------------------- |
| 10 | **cyclonedx-py** | Python only | CycloneDX 1.0–1.7 |
| 10 | **cargo-cyclonedx** | Rust only | CycloneDX 1.4–1.6 |
| 20 | **cdxgen** | Python, JavaScript, **Java/Gradle**, Go, Rust, Ruby, Dart, C++, PHP, .NET, Swift, Elixir, Scala, Docker images | CycloneDX 1.4–1.7 |
| ~~30~~ | ~~**Trivy**~~ | ~~Temporarily disabled due to security vulnerabilities~~ | ~~CycloneDX 1.6, SPDX 2.3~~ |
| 35 | **Syft** | Python, JavaScript, Go, Rust, Ruby, Dart, C++, PHP, .NET, Swift, Elixir, Terraform, Docker images | CycloneDX 1.2–1.6, SPDX 2.2–2.3 |
| Priority | Generator | Supported Ecosystems | Output Formats |
| -------- | --------------------- | ------------------------------------------------------------------------------------------------- | ------------------------------- |
| 10 | **cyclonedx-py** | Python only | CycloneDX 1.0–1.7 |
| 10 | **cargo-cyclonedx** | Rust only | CycloneDX 1.4–1.6 |
| 20 | **cdxgen** (lockfiles) | Python, JavaScript, **Java/Gradle**, Go, Rust, Ruby, Dart, C++, PHP, .NET, Swift, Elixir, Scala | CycloneDX 1.4–1.7 |
| 25 | **Syft** (images) | Docker images | CycloneDX 1.2–1.6, SPDX 2.2–2.3 |
| ~~30~~ | ~~**Trivy**~~ | ~~Temporarily disabled due to security vulnerabilities~~ | ~~CycloneDX 1.6, SPDX 2.3~~ |
| 35 | **Syft** (lockfiles) | Python, JavaScript, Go, Rust, Ruby, Dart, C++, PHP, .NET, Swift, Elixir, Terraform | CycloneDX 1.2–1.6, SPDX 2.2–2.3 |
| 40 | **cdxgen** (images) | Docker images (fallback: lacks the OS metadata vulnerability scanners need) | CycloneDX 1.4–1.7 |

#### How It Works

Expand All @@ -1057,7 +1059,7 @@ Generators are tried in priority order. Native tools (optimized for specific eco
3. **Java lockfiles** (pom.xml, build.gradle, gradle.lockfile) → cdxgen (best Java support)
4. **Dart lockfiles** (pubspec.lock) → cdxgen or Syft
5. **Other lockfiles** (package-lock.json, go.mod, etc.) → cdxgen (then Syft as fallback)
6. **Docker images** → cdxgen (then Syft as fallback)
6. **Docker images** → Syft (then cdxgen as fallback). Syft SBOMs include the `operating-system` component and distro PURL qualifiers that vulnerability scanners (Trivy, Grype, Dependency-Track) need to detect OS packages.

If the primary generator fails or doesn't support the input, the next one in priority order is tried automatically.

Expand Down
36 changes: 26 additions & 10 deletions sbomify_action/_generation/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,19 @@ def create_default_registry() -> GeneratorRegistry:
- Input: Rust lock files only (Cargo.lock)
- Output: CycloneDX 1.4-1.6

Priority 20 - Comprehensive Multi-Ecosystem (cdxgen):
Priority 20 - Comprehensive Multi-Ecosystem (cdxgen, lock files):
- CdxgenFsGenerator: Filesystem/lock file scanning
- Input: Python, JavaScript, Java/Gradle, Go, Rust, Ruby, Dart, C++,
PHP, .NET, Swift, Elixir, Scala
- Output: CycloneDX 1.4-1.7 (no SPDX support)
Comment on lines +34 to 38

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in e6b6f87 — updated the generators/init.py docstring to the new priorities (syft-image 25, cdxgen-image 40) and reordered the list by priority. Also swept the generation module for any other stale priority references; none remain.

- CdxgenImageGenerator: Docker image scanning

Priority 25 - Preferred Image Scanner (Syft):
- SyftImageGenerator: Docker image scanning
- Input: Container images
- Output: CycloneDX 1.4-1.7 (no SPDX support)
- Output: CycloneDX 1.2-1.6, SPDX 2.2-2.3
- Emits the operating-system component and distro PURL qualifiers
that vulnerability scanners (Trivy, Dependency-Track, Grype)
need to detect OS packages (see issue #264)

Priority 30 - Generic Multi-Ecosystem (Trivy) [TEMPORARILY DISABLED]:
- TrivyFsGenerator: Filesystem/lock file scanning
Expand All @@ -50,14 +55,18 @@ def create_default_registry() -> GeneratorRegistry:
- Output: CycloneDX 1.6, SPDX 2.3
NOTE: Trivy is temporarily disabled due to recurring security vulnerabilities.

Priority 35 - Generic Multi-Ecosystem (Syft):
Priority 35 - Generic Multi-Ecosystem (Syft, lock files):
- SyftFsGenerator: Filesystem/lock file scanning
- Input: Python, JavaScript, Go, Rust, Ruby, Dart, C++, PHP, .NET,
Swift, Elixir, Terraform (NOT Java/Gradle lock files)
- Output: CycloneDX 1.2-1.6, SPDX 2.2-2.3
- SyftImageGenerator: Docker image scanning

Priority 40 - Fallback Image Scanner (cdxgen):
- CdxgenImageGenerator: Docker image scanning
- Input: Container images
- Output: CycloneDX 1.2-1.6, SPDX 2.2-2.3
- Output: CycloneDX 1.4-1.7 (no SPDX support)
- Fallback only: its image SBOMs lack the operating-system
component, so Trivy/Dependency-Track can't scan them

Generators are queried sequentially in priority order. The first
generator that supports the input and requested format/version is used.
Expand All @@ -71,9 +80,13 @@ def create_default_registry() -> GeneratorRegistry:
registry.register(CycloneDXPyGenerator())
registry.register(CycloneDXCargoGenerator())

# Priority 20: cdxgen generators (comprehensive multi-ecosystem)
# Priority 20: cdxgen lock file generator (comprehensive multi-ecosystem)
registry.register(CdxgenFsGenerator())
registry.register(CdxgenImageGenerator())

# Priority 25: Syft image generator — preferred for Docker images because
# its SBOMs carry the operating-system component that downstream
# vulnerability scanners (Trivy, Dependency-Track) require (issue #264)
registry.register(SyftImageGenerator())

# Priority 30: Trivy generators - TEMPORARILY DISABLED due to recurring
# security vulnerabilities in Trivy. To re-enable, reintroduce the
Expand All @@ -83,9 +96,12 @@ def create_default_registry() -> GeneratorRegistry:
# registry.register(TrivyFsGenerator())
# registry.register(TrivyImageGenerator())

# Priority 35: Syft generators (version selection, wide ecosystem support)
# Priority 35: Syft lock file generator (version selection, wide ecosystem support)
registry.register(SyftFsGenerator())
registry.register(SyftImageGenerator())

# Priority 40: cdxgen image generator — fallback behind Syft; its image
# SBOMs lack the operating-system component (issue #264)
registry.register(CdxgenImageGenerator())

return registry

Expand Down
4 changes: 2 additions & 2 deletions sbomify_action/_generation/generators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
- CycloneDXPyGenerator: Native Python CycloneDX generator (priority 10)
- CycloneDXCargoGenerator: Native Rust/Cargo CycloneDX generator (priority 10)
- CdxgenFsGenerator: cdxgen filesystem scanner (priority 20)
- CdxgenImageGenerator: cdxgen Docker image scanner (priority 20)
- SyftImageGenerator: Syft Docker image scanner (priority 25)
- TrivyFsGenerator: Trivy filesystem scanner (priority 30)
- TrivyImageGenerator: Trivy Docker image scanner (priority 30)
- SyftFsGenerator: Syft filesystem scanner (priority 35)
- SyftImageGenerator: Syft Docker image scanner (priority 35)
- CdxgenImageGenerator: cdxgen Docker image scanner (priority 40)
"""

from .cdxgen import CdxgenFsGenerator, CdxgenImageGenerator
Expand Down
22 changes: 15 additions & 7 deletions sbomify_action/_generation/generators/cdxgen.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""cdxgen generator plugins for filesystem and Docker image scanning.

Priority: 20 (comprehensive multi-ecosystem)
Priority: 20 for lock files (comprehensive multi-ecosystem),
40 for Docker images (fallback behind Syft — see CdxgenImageGenerator).

cdxgen is a comprehensive SBOM generator that supports many ecosystems
and programming languages with excellent coverage.
Expand Down Expand Up @@ -228,6 +229,11 @@ class CdxgenImageGenerator:

Uses cdxgen to scan Docker images and generate SBOMs.

Fallback behind Syft for Docker images: cdxgen image SBOMs lack the
`operating-system` component, so vulnerability scanners like Trivy
(including Dependency-Track's Trivy analyzer) can't detect OS
packages and scan nothing (see issue #264).

Verified capabilities (cdxgen 12.0.0):
- CycloneDX versions: 1.4, 1.5, 1.6, 1.7 (default: 1.6)
- SPDX: Not supported
Expand All @@ -245,8 +251,9 @@ def command(self) -> str:

@property
def priority(self) -> int:
# Comprehensive multi-ecosystem, higher priority than Trivy/Syft
return 20
# Fallback behind Syft (25): cdxgen image SBOMs omit the
# operating-system component that Trivy/Dependency-Track need
return 40

@property
def supported_formats(self) -> list[FormatVersion]:
Expand Down Expand Up @@ -306,10 +313,11 @@ def generate(self, input: GenerationInput) -> GenerationResult:

try:
# run_command raises SBOMGenerationError on failure (uses check=True).
# log_errors=False: cdxgen image scanning is a priority-20 fallback
# ahead of syft; a benign failure here shouldn't spam ERROR when syft
# can still succeed. (Docker-image-not-found is handled separately and
# still logs at WARNING.)
# log_errors=False: cdxgen image scanning is a fallback behind syft;
# a benign failure here shouldn't spam ERROR — if every generator
# fails, the pipeline raises SBOMGenerationError and the run fails.
# (Docker-image-not-found is handled separately and still logs at
# WARNING.)
Comment on lines 315 to +320

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 2b97439 — reworded to say the pipeline raises SBOMGenerationError when every generator fails, rather than claiming the orchestrator logs an ERROR (the registry logs failures at INFO, as you noted).

run_command(cmd, "cdxgen", timeout=DEFAULT_TIMEOUT, docker_image=input.docker_image, log_errors=False)

# Verify output file was created
Expand Down
20 changes: 16 additions & 4 deletions sbomify_action/_generation/generators/syft.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
"""Syft generator plugins for filesystem and Docker image scanning.

Priority: 35 (generic multi-ecosystem, lower than Trivy)
Priority: 35 for lock files (generic multi-ecosystem, lower than Trivy),
25 for Docker images (preferred over cdxgen — see SyftImageGenerator).

Syft is a comprehensive SBOM generator that supports version selection.
It supports more versions than Trivy but is slightly lower priority.
For lock files it supports more spec versions than Trivy but ranks
slightly lower; for Docker images it ranks higher than both Trivy and
cdxgen because its image SBOMs carry the OS metadata downstream
vulnerability scanners need.

Verified capabilities (Syft 1.38.2):
- CycloneDX versions: 1.2, 1.3, 1.4, 1.5, 1.6 (default: 1.6)
Expand Down Expand Up @@ -176,6 +180,13 @@ class SyftImageGenerator:

Uses Syft to scan Docker images and generate SBOMs.

Preferred over cdxgen for Docker images: Syft emits the
`operating-system` component and distro PURL qualifiers that
vulnerability scanners (Trivy, Dependency-Track's Trivy analyzer,
Grype) need to detect OS packages. cdxgen image SBOMs lack the OS
component, so Trivy reports "Unsupported os" and scans nothing
(see issue #264).

Verified capabilities (Syft 1.38.2):
- CycloneDX versions: 1.2, 1.3, 1.4, 1.5, 1.6 (default: 1.6)
- SPDX versions: 2.2, 2.3 (default: 2.3)
Expand All @@ -192,8 +203,9 @@ def command(self) -> str:

@property
def priority(self) -> int:
# Generic multi-ecosystem, slightly lower priority than Trivy
return 35
# Preferred image scanner: unlike cdxgen, Syft's image SBOMs are
# consumable by downstream vulnerability scanners (Trivy/Grype)
return 25

@property
def supported_formats(self) -> list[FormatVersion]:
Expand Down
12 changes: 7 additions & 5 deletions tests/test_generation_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,11 +235,13 @@ def test_get_generators_for_docker_image(self):
input = GenerationInput(docker_image="alpine:3.18", output_format="cyclonedx")
generators = self.registry.get_generators_for(input)

# cyclonedx-py doesn't support Docker images
# cyclonedx-py doesn't support Docker images.
# Syft is preferred for images: its SBOMs carry the operating-system
# component that Trivy/Dependency-Track need (issue #264).
self.assertEqual(len(generators), 3)
self.assertEqual(generators[0].name, "cdxgen-image") # Priority 20
self.assertEqual(generators[0].name, "syft-image") # Priority 25
self.assertEqual(generators[1].name, "trivy-image") # Priority 30
self.assertEqual(generators[2].name, "syft-image") # Priority 35
self.assertEqual(generators[2].name, "cdxgen-image") # Priority 40


@patch("sbomify_action._generation.generators.cyclonedx_py._CYCLONEDX_PY_AVAILABLE", True)
Expand Down Expand Up @@ -642,7 +644,7 @@ def setUp(self):
def test_name_and_priority(self):
"""Test generator name and priority."""
self.assertEqual(self.generator.name, "cdxgen-image")
self.assertEqual(self.generator.priority, 20)
self.assertEqual(self.generator.priority, 40)

def test_supports_docker_images(self):
"""Test support for Docker images."""
Expand Down Expand Up @@ -862,7 +864,7 @@ def setUp(self):
def test_name_and_priority(self):
"""Test generator name and priority."""
self.assertEqual(self.generator.name, "syft-image")
self.assertEqual(self.generator.priority, 35)
self.assertEqual(self.generator.priority, 25)

def test_supports_docker_images(self):
"""Test support for Docker images."""
Expand Down