Skip to content

Conversation

@jcrossley3
Copy link
Contributor

@jcrossley3 jcrossley3 commented Dec 3, 2025

Fixes #2162

We're using the CycloneDX types initially, as the SPDX 3 types are still coagulating. Once they're finalized, I expect we'll revisit these.

I don't expect we'll need to migrate/reingest existing data as our service layer will treat null values as the default "application" type.

Summary by Sourcery

Add a typed package kind field to SBOM packages and plumb it through ingestion, backed by a DB migration.

New Features:

  • Introduce a PackageType enum on SBOM packages, aligned with CycloneDX component types, and store it as an optional field in the package model.
  • Populate package_type for CycloneDX-ingested components based on their declared type.

Enhancements:

  • Extend SBOM ingestion node metadata to carry package type alongside existing package attributes.
  • Remove now-redundant ad-hoc PackageType and PackageNamespace query result structs from PURL-related entities.

Build:

  • Add a database migration to add a nullable package_type integer column to the sbom_package table and register it with the migrator.

Tests:

  • Add tests to verify PackageType string/JSON serialization, deserialization, case-insensitive parsing, and error handling.

@sourcery-ai
Copy link
Contributor

sourcery-ai bot commented Dec 3, 2025

Reviewer's Guide

This PR introduces a strongly-typed PackageType enum for SBOM packages, persists it on the sbom_package table, wires it through the SBOM ingestor (CycloneDX and SPDX paths), and removes several now-redundant ad‑hoc PackageType/Namespace query result structs.

Sequence diagram for package_type flow from CycloneDX component to sbom_package row

sequenceDiagram
    actor Ingestor
    participant SbomContext
    participant ComponentCreator
    participant PackageCreator
    participant Database

    Ingestor->>SbomContext: ingest_cyclonedx(sbom)
    SbomContext->>ComponentCreator: new(component, refs)
    ComponentCreator->>ComponentCreator: package_type = PackageType::from_str(component.type_)
    ComponentCreator->>PackageCreator: create(NodeInfoParam{ package_type, ... })
    PackageCreator->>PackageCreator: sbom_package.package_type = NodeInfoParam.package_type
    PackageCreator->>Database: insert sbom_package (package_type column)
    Database-->>PackageCreator: persisted sbom_package
    PackageCreator-->>SbomContext: PackageCreator result
    SbomContext-->>Ingestor: updated graph including package_type
Loading

ER diagram for sbom_package table with new package_type column

erDiagram
    sbom_package {
        int id PK
        varchar node_id
        varchar group
        varchar version
        int package_type "nullable, maps to PackageType enum"
    }
Loading

Class diagram for new PackageType enum and updated SBOM package structures

classDiagram
    class Model {
        +i32 id
        +String node_id
        +Option~String~ group
        +Option~String~ version
        +Option~PackageType~ package_type
    }

    class PackageType {
        <<enumeration>>
        Application
        Framework
        Library
        Container
        Platform
        OperatingSystem
        Device
        DeviceDriver
        Firmware
        File
        MachineLearningModel
        Data
        CryptographicAsset
    }

    class NodeInfoParam {
        +String node_id
        +String name
        +Option~String~ group
        +Option~String~ version
        +Vec~PackageLicensenInfo~ package_license_info
        +Option~PackageType~ package_type
        +new() Default
    }

    class PackageLicensenInfo {
        +String license
        +Option~String~ license_expression
    }

    class PackageCreator {
        +sbom_package ActiveModel
        +sbom_package_purls Vec~sbom_package_purl::ActiveModel~
        +sbom_package_licenses Vec~sbom_package_license::ActiveModel~
    }

    Model --> PackageType : uses
    NodeInfoParam --> PackageType : optional
    PackageCreator --> Model : populates
    NodeInfoParam --> PackageLicensenInfo : contains
Loading

File-Level Changes

Change Details Files
Add a typed PackageType enum field to sbom_package and persist it via a database migration.
  • Extend sbom_package::Model with an optional package_type field using a new PackageType active enum mapped to an integer column.
  • Define the PackageType enum with CycloneDX-inspired variants, SeaORM mappings, serde and strum traits for (de)serialization and string conversion.
  • Add a migration that alters the sbom_package table to add a nullable integer package_type column and supports rollback by dropping the column.
entity/src/sbom_package.rs
migration/src/m0001210_alter_package_add_type.rs
migration/src/lib.rs
Wire PackageType through SBOM ingestion, especially the CycloneDX flow, and update NodeInfoParam construction sites.
  • Extend NodeInfoParam to carry an optional PackageType and derive Default so callers can use struct update syntax for new fields.
  • Populate sbom_package::ActiveModel.package_type from NodeInfoParam in PackageCreator.
  • In CycloneDX ingestion, map component type strings to PackageType via FromStr and pass the result into NodeInfoParam.
  • Update SPDX and generic SBOM paths to construct NodeInfoParam using Default struct update syntax, leaving package_type as None for now.
modules/ingestor/src/graph/sbom/common/package.rs
modules/ingestor/src/graph/sbom/mod.rs
modules/ingestor/src/graph/sbom/spdx.rs
modules/ingestor/src/graph/sbom/cyclonedx.rs
Remove unused query-result-only PackageType and PackageNamespace structs from purl-related entities.
  • Delete FromQueryResult-based PackageType and PackageNamespace structs from base_purl, qualified_purl, and versioned_purl entities as they are superseded or unused.
  • Keep entity ActiveModelBehavior implementations unchanged.
entity/src/base_purl.rs
entity/src/qualified_purl.rs
entity/src/versioned_purl.rs
Add tests to validate PackageType string/JSON conversions and error handling.
  • Introduce a unit test module that checks round-trip conversions between kebab-case strings, PackageType enum variants, Display output, and serde_json representation.
  • Verify case-insensitive parsing and proper error behavior for invalid strings.
entity/src/sbom_package.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#2162 Add a database-backed field on the SBOM package/component model to store a component type (including machine-learning-model) so it can be persisted and queried.
#2162 Populate the new component type field during SBOM ingestion (especially CycloneDX/AIBOM) based on the component type information in the SBOM.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 68.19%. Comparing base (de8702c) to head (422d93f).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2163   +/-   ##
=======================================
  Coverage   68.18%   68.19%           
=======================================
  Files         376      377    +1     
  Lines       21161    21168    +7     
  Branches    21161    21168    +7     
=======================================
+ Hits        14429    14435    +6     
+ Misses       5870     5866    -4     
- Partials      862      867    +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ctron
Copy link
Contributor

ctron commented Dec 4, 2025

I'd prefer having an enum type in the database, preventing the case of having to deal with unknown integer values.

@jcrossley3
Copy link
Contributor Author

I'd prefer having an enum type in the database, preventing the case of having to deal with unknown integer values.

I patterned the PackageType enum after DiscriminatorType in our db. Why are "unknown integer values" ok for that one? Can you point me to an example of an enum type in our db I can follow?

@ctron
Copy link
Contributor

ctron commented Dec 4, 2025

I don't think they are ok. Having proper enums names more sense.

Can you point me to an example of an enum type in our db I can follow?

CREATE TYPE public.cvss3_a AS ENUM (
'n',
'l',
'h'
);

https://github.com/ctron/trustify/blob/feature/adr_rescan_1/migration/src/m0002010_add_advisory_scores.rs

They don't seem to be used, and I want to claim the name, PackageType,
for sbom_package.
Fixes guacsec#2162

We're using the CycloneDX types initially, as the SPDX 3 types are
still coagulating. Once they're finalized, I expect we'll revisit
these.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Introduce a db field to track component *types*

2 participants