Skip to content

feat(entity): add exploit intelligence job tracking entities and migration#2435

Open
Strum355 wants to merge 5 commits into
guacsec:feature/exploit-intelligence-integrationfrom
Strum355:TC-4675
Open

feat(entity): add exploit intelligence job tracking entities and migration#2435
Strum355 wants to merge 5 commits into
guacsec:feature/exploit-intelligence-integrationfrom
Strum355:TC-4675

Conversation

@Strum355

@Strum355 Strum355 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Summary

Add database entities and migrations for tracking Exploit Intelligence analysis jobs in trustify. This is the data-layer foundation for integrating the Exploit Intelligence (EI) service, which performs automated vulnerability reachability analysis and produces VEX advisories.

Jira

Implements TC-4675

What changed

New entities:

  • exploit_intelligence_job — tracks the lifecycle of each EI analysis request (submission → completion), including scan ID, status, associated SBOM/vulnerability, and the result (VEX advisory link or error). Supports both single-component (CycloneDX) and multi-component (SPDX product) flows via product_id/total_components.
  • exploit_intelligence_job_component — tracks per-component analysis results within a multi-component SPDX product job. Each row represents one container image component independently analysed by the EI service.

New enums:

  • ExploitIntelligenceJobStatusPending, Running, Completed, Failed
  • FindingVulnerable, NotVulnerable, Uncertain

Migration (m0002200):

  • Creates both tables in a single migration
  • FK constraints to sbom and advisory tables (both nullable)
  • CASCADE delete from job → components
  • UNIQUE constraint on scan_id (both tables)
  • FK indexes on all foreign key columns

Summary by Sourcery

Introduce database support for tracking Exploit Intelligence analysis jobs and their per-component results.

New Features:

  • Add exploit_intelligence_job entity to represent Exploit Intelligence analysis jobs with lifecycle status, findings, and links to SBOMs and advisories.
  • Add exploit_intelligence_job_component entity to capture per-component analysis details for multi-component Exploit Intelligence product jobs.

Enhancements:

  • Register new entities in the entity module and add a migration to create the exploit_intelligence_job and exploit_intelligence_job_component tables with appropriate indexes and foreign keys.

@sourcery-ai

sourcery-ai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds new SeaORM entities and a database migration to track Exploit Intelligence analysis jobs and per-component results, wiring them into the existing entity and migration registries.

Entity relationship diagram for Exploit Intelligence job tracking

erDiagram
    exploit_intelligence_job {
        uuid id
        string scan_id
        uuid sbom_id
        string vulnerability_id
        int status
        int finding
        string product_id
        int total_components
        uuid advisory_id
    }

    exploit_intelligence_job_component {
        uuid id
        uuid job_id
        string component_ref
        string component_name
        string scan_id
        int status
        int finding
        uuid advisory_id
    }

    sbom {
        uuid sbom_id
    }

    advisory {
        uuid id
    }

    sbom ||--o{ exploit_intelligence_job : sbom_id
    advisory ||--o{ exploit_intelligence_job : advisory_id
    exploit_intelligence_job ||--o{ exploit_intelligence_job_component : job_id
    advisory ||--o{ exploit_intelligence_job_component : advisory_id
Loading

File-Level Changes

Change Details Files
Introduce exploit_intelligence_job and exploit_intelligence_job_component entities for tracking EI jobs and per-component analysis results, including enums for job status and findings and their relations to SBOMs, advisories, and each other.
  • Define ExploitIntelligenceJobStatus and Finding as Integer-backed ActiveEnums for job lifecycle and analysis outcome states.
  • Add exploit_intelligence_job entity model with fields for EI correlation IDs, SBOM/vulnerability linkage, status, URLs, advisory/finding info, product metadata, and timestamps.
  • Add exploit_intelligence_job_component entity model with fields for parent job linkage, component identifiers/names, per-component scan IDs, status, findings, advisory links, error messages, and timestamps.
  • Declare SeaORM relations: job belongs_to sbom and advisory, has_many components; components belongs_to job and advisory.
entity/src/exploit_intelligence_job.rs
entity/src/exploit_intelligence_job_component.rs
Register the new entities and migration in the existing library modules so they are part of the ORM and migration pipeline.
  • Expose exploit_intelligence_job and exploit_intelligence_job_component modules from the entity crate root.
  • Register m0002200_create_exploit_intelligence_job migration in the migration crate and include it in MigratorExt::build_migrations().
entity/src/lib.rs
migration/src/lib.rs
Create migration m0002200 to define exploit_intelligence_job and exploit_intelligence_job_component tables, constraints, and indexes, including cascading behavior and foreign-key relationships to sbom and advisory.
  • Create exploit_intelligence_job table with UUID PK, nullable unique scan_id, required sbom_id and vulnerability_id, integer status with default 0, optional URLs/advisory/finding/error/product fields, component count, and created/updated timestamps.
  • Add foreign keys from exploit_intelligence_job.sbom_id to sbom.sbom_id (ON DELETE CASCADE) and from advisory_id to advisory.id (ON DELETE SET NULL), plus indexes on these FKs.
  • Create exploit_intelligence_job_component table with UUID PK, required job_id/component_ref/component_name, nullable unique scan_id, integer status with default 0, optional finding/report_url/advisory_id/error_message, and created/updated timestamps.
  • Add foreign keys from exploit_intelligence_job_component.job_id to exploit_intelligence_job.id (ON DELETE CASCADE) and from advisory_id to advisory.id (ON DELETE SET NULL), plus indexes on job_id and advisory_id, and implement symmetric down migration removing indexes and tables in reverse order.
migration/src/m0002200_create_exploit_intelligence_job.rs

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • If exploit_intelligence_job records will frequently be queried by vulnerability_id (e.g., listing jobs for a CVE), consider adding an index on VulnerabilityId to avoid full table scans as the table grows.
  • Similarly, if per-component lookups are commonly done by component_ref in exploit_intelligence_job_component, adding an index on ComponentRef would improve query performance for larger datasets.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- If `exploit_intelligence_job` records will frequently be queried by `vulnerability_id` (e.g., listing jobs for a CVE), consider adding an index on `VulnerabilityId` to avoid full table scans as the table grows.
- Similarly, if per-component lookups are commonly done by `component_ref` in `exploit_intelligence_job_component`, adding an index on `ComponentRef` would improve query performance for larger datasets.

## Individual Comments

### Comment 1
<location path="migration/src/m0002200_create_exploit_intelligence_job.rs" line_range="42-51" />
<code_context>
+                    .col(ColumnDef::new(ExploitIntelligenceJob::SourceUrl).string())
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Explicitly size URL/text columns to avoid silent truncation or inefficient storage.

These URL fields (e.g., SourceUrl, ReportUrl) use `.string()` with the default length (typically 255), which may be too small for real EI/report/repo URLs and can cause silent truncation or require later schema changes. Please use an explicit length (e.g., `.string_len(1024)`) or `.text()` to better match expected URL sizes and avoid data loss and future migrations.

Suggested implementation:

```rust
                    .col(
                        ColumnDef::new(ExploitIntelligenceJob::SourceUrl)
                            .string_len(1024),
                    )
                    .col(
                        ColumnDef::new(ExploitIntelligenceJob::ReportUrl)
                            .string_len(1024),
                    )

```

Depending on the rest of the schema, you may also want to:
1. Apply explicit lengths or switch to `.text()` for other potentially long text fields (e.g., `ErrorMessage`, any other URL or description fields).
2. Ensure corresponding entity definitions (e.g., in your SeaORM models) use compatible types so ORM-level validations align with the new column lengths.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +42 to +51
.col(ColumnDef::new(ExploitIntelligenceJob::SourceUrl).string())
.col(ColumnDef::new(ExploitIntelligenceJob::ReportUrl).string())
.col(ColumnDef::new(ExploitIntelligenceJob::AdvisoryId).uuid())
.col(ColumnDef::new(ExploitIntelligenceJob::Finding).integer())
.col(ColumnDef::new(ExploitIntelligenceJob::ErrorMessage).string())
.col(ColumnDef::new(ExploitIntelligenceJob::ProductId).string())
.col(
ColumnDef::new(ExploitIntelligenceJob::TotalComponents).integer(),
)
.col(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

suggestion (bug_risk): Explicitly size URL/text columns to avoid silent truncation or inefficient storage.

These URL fields (e.g., SourceUrl, ReportUrl) use .string() with the default length (typically 255), which may be too small for real EI/report/repo URLs and can cause silent truncation or require later schema changes. Please use an explicit length (e.g., .string_len(1024)) or .text() to better match expected URL sizes and avoid data loss and future migrations.

Suggested implementation:

                    .col(
                        ColumnDef::new(ExploitIntelligenceJob::SourceUrl)
                            .string_len(1024),
                    )
                    .col(
                        ColumnDef::new(ExploitIntelligenceJob::ReportUrl)
                            .string_len(1024),
                    )

Depending on the rest of the schema, you may also want to:

  1. Apply explicit lengths or switch to .text() for other potentially long text fields (e.g., ErrorMessage, any other URL or description fields).
  2. Ensure corresponding entity definitions (e.g., in your SeaORM models) use compatible types so ORM-level validations align with the new column lengths.

@Strum355 Strum355 requested a review from ruromero July 2, 2026 12:10
Strum355 and others added 4 commits July 2, 2026 13:13
Merge m0002200 and m0002210 into a single migration that creates both
the exploit_intelligence_job and exploit_intelligence_job_component
tables together, including product_id/total_components columns upfront.
Add the missing advisory_id FK index on the component table per
CONVENTIONS.md migration patterns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The column stores both purls (successful components) and container image
references (failed/excluded components), so component_ref is more accurate.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nt docs

sbom_id is always provided when creating a job, so make it NOT NULL in
both the entity and migration. Change the FK on-delete action from
SetNull to Cascade accordingly.

Add doc comments to scan_id, report_url, advisory_id, finding, and
product_id clarifying which fields stay None in the multi-component
SPDX product flow vs the single-component CycloneDX flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
.uuid()
.not_null()
.primary_key()
.default(Func::cust(UuidV4)),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please use V7 UUIDs. They perform better in PSQL.

/// Lifecycle status of an Exploit Intelligence analysis job.
#[derive(Copy, Clone, Debug, Eq, PartialEq, EnumIter, DeriveActiveEnum)]
#[sea_orm(rs_type = "i32", db_type = "Integer")]
pub enum ExploitIntelligenceJobStatus {

Copy link
Copy Markdown
Contributor

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:

  • #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, EnumIter, DeriveActiveEnum)]
    #[sea_orm(rs_type = "String", db_type = "Enum", enum_name = "score_type")]
    pub enum ScoreType {
    #[sea_orm(string_value = "2.0")]
    V2_0,
    #[sea_orm(string_value = "3.0")]
    V3_0,
    #[sea_orm(string_value = "3.1")]
    V3_1,
    #[sea_orm(string_value = "4.0")]
    V4_0,
    }
  • create_enum_if_not_exists(
    manager,
    Severity::Table,
    Severity::VARIANTS.iter().skip(1).copied(),
    )
    .await?;

@codecov

codecov Bot commented Jul 2, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 25.00000% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.38%. Comparing base (db01ffe) to head (19376fc).

Files with missing lines Patch % Lines
entity/src/exploit_intelligence_job.rs 0.00% 9 Missing ⚠️
entity/src/exploit_intelligence_job_component.rs 0.00% 6 Missing ⚠️
Additional details and impacted files
@@                             Coverage Diff                              @@
##           feature/exploit-intelligence-integration    #2435      +/-   ##
============================================================================
- Coverage                                     71.39%   71.38%   -0.02%     
============================================================================
  Files                                           452      455       +3     
  Lines                                         27249    27269      +20     
  Branches                                      27249    27269      +20     
============================================================================
+ Hits                                          19454    19465      +11     
- Misses                                         6666     6669       +3     
- Partials                                       1129     1135       +6     

☔ View full report in Codecov by Harness.
📢 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.

Address PR review feedback: remove gen_random_uuid() defaults so
application code generates V7 UUIDs, and convert Status/Finding from
integer-backed columns to proper PostgreSQL enum types matching the
existing Severity/ScoreType pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants