-
Notifications
You must be signed in to change notification settings - Fork 48
feat(entity): add exploit intelligence job tracking entities and migration #2435
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Strum355
wants to merge
5
commits into
guacsec:feature/exploit-intelligence-integration
Choose a base branch
from
Strum355:TC-4675
base: feature/exploit-intelligence-integration
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+619
−0
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
36a8b22
feat(entity): add exploit intelligence entities and migrations
Strum355 331c3c9
refactor(migration): combine EI migrations and add missing FK index
Strum355 97c81dc
refactor(entity): rename component_purl to component_ref
Strum355 19376fc
refactor(entity): make sbom_id non-nullable and clarify multi-compone…
Strum355 0060d97
fix(entity): use V7 UUIDs and PostgreSQL enum types for EI jobs
Strum355 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,159 @@ | ||
| /// Entity model for tracking Exploit Intelligence analysis jobs. | ||
| /// | ||
| /// Each row represents a single analysis request submitted to the Exploit Intelligence | ||
| /// service, tracking its lifecycle from submission through completion. The result may | ||
| /// link to an ingested VEX advisory or report an error. | ||
| use sea_orm::entity::prelude::*; | ||
| use time::OffsetDateTime; | ||
|
|
||
| /// Lifecycle status of an Exploit Intelligence analysis job. | ||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, EnumIter, DeriveActiveEnum)] | ||
| #[sea_orm( | ||
| rs_type = "String", | ||
| db_type = "Enum", | ||
| enum_name = "exploit_intelligence_job_status" | ||
| )] | ||
| pub enum ExploitIntelligenceJobStatus { | ||
| /// Job has been created but not yet started. | ||
| #[sea_orm(string_value = "pending")] | ||
| Pending, | ||
| /// Analysis is in progress. | ||
| #[sea_orm(string_value = "running")] | ||
| Running, | ||
| /// Analysis completed successfully. | ||
| #[sea_orm(string_value = "completed")] | ||
| Completed, | ||
| /// Analysis failed with an error. | ||
| #[sea_orm(string_value = "failed")] | ||
| Failed, | ||
| } | ||
|
|
||
| /// Analysis finding from the Exploit Intelligence service. | ||
| /// | ||
| /// Represents the outcome of a vulnerability analysis for a given SBOM/CVE pair. | ||
| #[derive(Copy, Clone, Debug, Eq, PartialEq, EnumIter, DeriveActiveEnum)] | ||
| #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "ei_finding")] | ||
| pub enum Finding { | ||
| /// The analysed component is vulnerable to the CVE. | ||
| #[sea_orm(string_value = "vulnerable")] | ||
| Vulnerable, | ||
| /// The analysed component is not vulnerable to the CVE. | ||
| #[sea_orm(string_value = "not_vulnerable")] | ||
| NotVulnerable, | ||
| /// The analysis was inconclusive. | ||
| #[sea_orm(string_value = "uncertain")] | ||
| Uncertain, | ||
| } | ||
|
|
||
| #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] | ||
| #[sea_orm(table_name = "exploit_intelligence_job")] | ||
| pub struct Model { | ||
| /// The database internal ID. | ||
| #[sea_orm(primary_key)] | ||
| pub id: Uuid, | ||
|
|
||
| /// Correlation ID from the Exploit Intelligence service (unique, nullable). | ||
| /// | ||
| /// Initially `None` when the job is created; populated once the EI service | ||
| /// returns a scan identifier. For multi-component SPDX jobs this stays `None` | ||
| /// because individual scan IDs live on the component records instead. | ||
| #[sea_orm(unique)] | ||
| pub scan_id: Option<String>, | ||
|
|
||
| /// FK to the SBOM being analysed. | ||
| pub sbom_id: Uuid, | ||
|
|
||
| /// CVE identifier (e.g., "CVE-2024-9680"). | ||
| pub vulnerability_id: String, | ||
|
|
||
| /// Current lifecycle status of the job. | ||
| pub status: ExploitIntelligenceJobStatus, | ||
|
|
||
| /// Git repository URL for source code analysis. | ||
| pub source_url: Option<String>, | ||
|
|
||
| /// Link to the EI human-readable justification report. | ||
| /// | ||
| /// Set for single-component (CycloneDX) jobs. For multi-component SPDX jobs | ||
| /// this stays `None` — per-component report URLs live on the component records. | ||
| pub report_url: Option<String>, | ||
|
|
||
| /// FK to advisory — set when a VEX document is ingested from the result. | ||
| /// | ||
| /// Set for single-component (CycloneDX) jobs. For multi-component SPDX jobs | ||
| /// this stays `None` — each component carries its own advisory link. | ||
| pub advisory_id: Option<Uuid>, | ||
|
|
||
| /// Analysis finding for the vulnerability (set on completion). | ||
| /// | ||
| /// For single-component jobs this is the direct EI result. For multi-component | ||
| /// SPDX jobs this is an aggregate: `Vulnerable` if any component is vulnerable, | ||
| /// `NotVulnerable` if all are not, `Uncertain` otherwise. | ||
| pub finding: Option<Finding>, | ||
|
|
||
| /// Error details for failed analyses. | ||
| pub error_message: Option<String>, | ||
|
|
||
| /// EI product ID for SPDX multi-component flow (`None` for single-component). | ||
| /// | ||
| /// When set, per-component results are tracked via the related | ||
| /// [`exploit_intelligence_job_component`](super::exploit_intelligence_job_component) records. | ||
| pub product_id: Option<String>, | ||
|
|
||
| /// Number of components in the product (`None` for single-component). | ||
| pub total_components: Option<i32>, | ||
|
|
||
| /// Timestamp when the job was created. | ||
| pub created: OffsetDateTime, | ||
|
|
||
| /// Timestamp when the job was last updated. | ||
| pub updated: OffsetDateTime, | ||
| } | ||
|
|
||
| /// Foreign-key relationships for the exploit intelligence job entity. | ||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||
| pub enum Relation { | ||
| /// Link to the SBOM that was analysed. | ||
| #[sea_orm( | ||
| belongs_to = "super::sbom::Entity", | ||
| from = "Column::SbomId", | ||
| to = "super::sbom::Column::SbomId" | ||
| )] | ||
| Sbom, | ||
|
|
||
| /// Link to the advisory ingested from the analysis result. | ||
| #[sea_orm( | ||
| belongs_to = "super::advisory::Entity", | ||
| from = "Column::AdvisoryId", | ||
| to = "super::advisory::Column::Id" | ||
| )] | ||
| Advisory, | ||
|
|
||
| /// Link to the per-component analysis results (multi-component SPDX flow). | ||
| #[sea_orm(has_many = "super::exploit_intelligence_job_component::Entity")] | ||
| Components, | ||
| } | ||
|
|
||
| /// Navigate from an exploit intelligence job to its SBOM. | ||
| impl Related<super::sbom::Entity> for Entity { | ||
| fn to() -> RelationDef { | ||
| Relation::Sbom.def() | ||
| } | ||
| } | ||
|
|
||
| /// Navigate from an exploit intelligence job to its advisory. | ||
| impl Related<super::advisory::Entity> for Entity { | ||
| fn to() -> RelationDef { | ||
| Relation::Advisory.def() | ||
| } | ||
| } | ||
|
|
||
| /// Navigate from an exploit intelligence job to its components. | ||
| impl Related<super::exploit_intelligence_job_component::Entity> for Entity { | ||
| fn to() -> RelationDef { | ||
| Relation::Components.def() | ||
| } | ||
| } | ||
|
|
||
| /// Default active-model behaviour (no custom hooks). | ||
| impl ActiveModelBehavior for ActiveModel {} | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,89 @@ | ||
| /// Entity model for tracking per-component analysis results within a multi-component | ||
| /// Exploit Intelligence product job. | ||
| /// | ||
| /// Each row represents one container image component within an SPDX product SBOM, | ||
| /// independently analysed by the Exploit Intelligence service. The parent | ||
| /// `exploit_intelligence_job` holds product-level metadata while this entity holds | ||
| /// the per-component scan results. | ||
| use super::exploit_intelligence_job::{ExploitIntelligenceJobStatus, Finding}; | ||
| use sea_orm::entity::prelude::*; | ||
| use time::OffsetDateTime; | ||
|
|
||
| #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] | ||
| #[sea_orm(table_name = "exploit_intelligence_job_component")] | ||
| pub struct Model { | ||
| /// The database internal ID. | ||
| #[sea_orm(primary_key)] | ||
| pub id: Uuid, | ||
|
|
||
| /// FK to the parent exploit intelligence job. | ||
| pub job_id: Uuid, | ||
|
|
||
| /// Identifier of the component (purl or container image reference). | ||
| pub component_ref: String, | ||
|
|
||
| /// Human-readable component name. | ||
| pub component_name: String, | ||
|
|
||
| /// EI scan ID for this component's report (unique, nullable). | ||
| #[sea_orm(unique)] | ||
| pub scan_id: Option<String>, | ||
|
|
||
| /// Current lifecycle status of this component's analysis. | ||
| pub status: ExploitIntelligenceJobStatus, | ||
|
|
||
| /// Analysis finding for the component (set on completion). | ||
| pub finding: Option<Finding>, | ||
|
|
||
| /// Link to the EI human-readable justification report. | ||
| pub report_url: Option<String>, | ||
|
|
||
| /// FK to advisory — set when a VEX document is ingested from the result. | ||
| pub advisory_id: Option<Uuid>, | ||
|
|
||
| /// Error details for failed analyses. | ||
| pub error_message: Option<String>, | ||
|
|
||
| /// Timestamp when the component record was created. | ||
| pub created: OffsetDateTime, | ||
|
|
||
| /// Timestamp when the component record was last updated. | ||
| pub updated: OffsetDateTime, | ||
| } | ||
|
|
||
| /// Foreign-key relationships for the exploit intelligence job component entity. | ||
| #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] | ||
| pub enum Relation { | ||
| /// Link to the parent exploit intelligence job. | ||
| #[sea_orm( | ||
| belongs_to = "super::exploit_intelligence_job::Entity", | ||
| from = "Column::JobId", | ||
| to = "super::exploit_intelligence_job::Column::Id" | ||
| )] | ||
| Job, | ||
|
|
||
| /// Link to the advisory ingested from the analysis result. | ||
| #[sea_orm( | ||
| belongs_to = "super::advisory::Entity", | ||
| from = "Column::AdvisoryId", | ||
| to = "super::advisory::Column::Id" | ||
| )] | ||
| Advisory, | ||
| } | ||
|
|
||
| /// Navigate from a component to its parent job. | ||
| impl Related<super::exploit_intelligence_job::Entity> for Entity { | ||
| fn to() -> RelationDef { | ||
| Relation::Job.def() | ||
| } | ||
| } | ||
|
|
||
| /// Navigate from a component to its advisory. | ||
| impl Related<super::advisory::Entity> for Entity { | ||
| fn to() -> RelationDef { | ||
| Relation::Advisory.def() | ||
| } | ||
| } | ||
|
|
||
| /// Default active-model behaviour (no custom hooks). | ||
| impl ActiveModelBehavior for ActiveModel {} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use actual enum types:
trustify/entity/src/advisory_vulnerability_score.rs
Lines 65 to 76 in db01ffe
trustify/migration/src/m0002010_add_advisory_scores.rs
Lines 20 to 25 in db01ffe