Skip to content

feat: exploit intelligence integration - #2534

Merged
Strum355 merged 13 commits into
mainfrom
feature/exploit-intelligence-integration
Jul 29, 2026
Merged

feat: exploit intelligence integration#2534
Strum355 merged 13 commits into
mainfrom
feature/exploit-intelligence-integration

Conversation

@Strum355

@Strum355 Strum355 commented Jul 22, 2026

Copy link
Copy Markdown
Member

@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.

Sorry @Strum355, your pull request is larger than the review limit of 150000 diff characters

@Strum355
Strum355 requested a review from ruromero July 22, 2026 16:41
@rh-jfuller
rh-jfuller requested a review from a team July 22, 2026 18:42
@Strum355
Strum355 force-pushed the feature/exploit-intelligence-integration branch from 068214e to c74a322 Compare July 23, 2026 08:50
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.80936% with 257 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.75%. Comparing base (93e2f8e) to head (f715b7f).
⚠️ Report is 8 commits behind head on main.

Files with missing lines Patch % Lines
modules/exploit-intelligence/src/service/mod.rs 74.60% 43 Missing and 5 partials ⚠️
modules/exploit-intelligence/src/runner/worker.rs 76.74% 30 Missing and 10 partials ⚠️
modules/exploit-intelligence/src/runner/polling.rs 82.67% 15 Missing and 20 partials ⚠️
...odules/exploit-intelligence/src/runner/analysis.rs 80.45% 18 Missing and 16 partials ⚠️
modules/exploit-intelligence/src/runner/mod.rs 50.00% 30 Missing ⚠️
modules/exploit-intelligence/src/error.rs 39.28% 17 Missing ⚠️
modules/exploit-intelligence/src/endpoints/mod.rs 82.27% 4 Missing and 10 partials ⚠️
server/src/profile/api.rs 93.10% 9 Missing and 3 partials ⚠️
entity/src/exploit_intelligence_job.rs 0.00% 6 Missing ⚠️
entity/src/exploit_intelligence_job_component.rs 0.00% 6 Missing ⚠️
... and 5 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2534      +/-   ##
==========================================
+ Coverage   72.13%   72.75%   +0.61%     
==========================================
  Files         466      483      +17     
  Lines       28634    30166    +1532     
  Branches    28634    30166    +1532     
==========================================
+ Hits        20654    21946    +1292     
- Misses       6810     6974     +164     
- Partials     1170     1246      +76     

☔ 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.

Comment thread migration/src/m0002290_create_exploit_intelligence_job.rs
Comment thread migration/src/m0002290_create_exploit_intelligence_job.rs
Comment thread modules/fundamental/src/lib.rs Outdated
Comment thread server/src/profile/api.rs
Comment thread server/src/profile/api.rs
Comment thread server/src/profile/api.rs Outdated
Comment thread server/src/profile/api.rs Outdated

@ctron ctron 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.

I think that, unless impossible, this should go into it's own module. It doesn't seem to be "fundemental".

Also, how can the UI evaluate if this feature is configured or not?

@ruromero ruromero 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.

Review: Exploit Intelligence Integration

Solid feature with well-designed job queue, comprehensive tests, and good resilience patterns. The convention and code quality items below should be addressed before merge.

See inline comments for details.

Comment thread modules/fundamental/Cargo.toml Outdated
Comment thread modules/fundamental/src/endpoints.rs
Comment thread modules/fundamental/src/endpoints.rs Outdated
Comment thread modules/exploit-intelligence/src/runner/polling.rs Outdated
Comment thread modules/exploit-intelligence/src/runner/polling.rs Outdated
Comment thread modules/fundamental/src/exploit_intelligence/runner/polling.rs Outdated
Comment thread modules/exploit-intelligence/src/service/mod.rs
Comment thread server/src/profile/api.rs Outdated
Comment thread server/src/profile/api.rs Outdated
Comment thread modules/exploit-intelligence/src/model/mod.rs
@Strum355

Copy link
Copy Markdown
Member Author

Also, how can the UI evaluate if this feature is configured or not?

Thanks for pointing this out, seems my changes disappeared into a git stash. Im taking the approach of adding a flag to /.well-known/trustify, any concerns with that? Seems reasonably straightforward and clean

{"version":"0.5.0-rc.1","readOnly":false,"exploitIntelligence":false,"build":{...}}

I think that, unless impossible, this should go into it's own module. It doesn't seem to be "fundemental".

Ill have a look into this 👍

@Strum355
Strum355 requested review from ctron and ruromero July 23, 2026 16:38
@Strum355
Strum355 force-pushed the feature/exploit-intelligence-integration branch from 4e515b5 to fe36624 Compare July 23, 2026 21:10
Comment thread modules/exploit-intelligence/src/runner/worker.rs Outdated
@Strum355
Strum355 force-pushed the feature/exploit-intelligence-integration branch from db0c4ef to de5108a Compare July 24, 2026 11:55
@Strum355
Strum355 requested a review from rh-jfuller July 24, 2026 13:08
@ctron

ctron commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

The command line arguments of EI mix with others and are inconsistently named:

| Exploit Intelligence:
|       --exploit-intelligence-url <EXPLOIT_INTELLIGENCE_URL>
|           Base URL of the Exploit Intelligence client service
|           
|           [env: EXPLOIT_INTELLIGENCE_URL=]
| 
|       --exploit-intelligence-ui-url <EXPLOIT_INTELLIGENCE_UI_URL>
|           Base URL of the Exploit Intelligence web UI for deep-linking to reports
|           
|           [env: EXPLOIT_INTELLIGENCE_UI_URL=]
| 
|       --exploit-intelligence-poll-interval <EXPLOIT_INTELLIGENCE_POLL_INTERVAL>
|           Polling interval for EI analysis completion (humantime, e.g. "30s")
|           
|           [env: EXPLOIT_INTELLIGENCE_POLL_INTERVAL=]
|           [default: 30s]
| 
|       --exploit-intelligence-max-poll-duration <EXPLOIT_INTELLIGENCE_MAX_POLL_DURATION>
|           Maximum duration before EI polling is considered timed out (humantime, e.g. "30m")
|           
|           [env: EXPLOIT_INTELLIGENCE_MAX_POLL_DURATION=]
|           [default: 30m]
| 
|       --exploit-intelligence-upload-max-retries <EXPLOIT_INTELLIGENCE_UPLOAD_MAX_RETRIES>
|           Maximum number of upload retry attempts for EI analysis
|           
|           [env: EXPLOIT_INTELLIGENCE_UPLOAD_MAX_RETRIES=]
|           [default: 3]
| 
|       --exploit-intelligence-upload-retry-delay <EXPLOIT_INTELLIGENCE_UPLOAD_RETRY_DELAY>
|           Initial delay between upload retries (humantime, exponential backoff)
|           
|           [env: EXPLOIT_INTELLIGENCE_UPLOAD_RETRY_DELAY=]
|           [default: 1s]
| 
|       --exploit-intelligence-max-consecutive-poll-failures <EXPLOIT_INTELLIGENCE_MAX_CONSECUTIVE_POLL_FAILURES>
|           Maximum consecutive poll failures before giving up
|           
|           [env: EXPLOIT_INTELLIGENCE_MAX_CONSECUTIVE_POLL_FAILURES=]
|           [default: 5]
| 
|       --worker-poll-interval <WORKER_POLL_INTERVAL>
|           How often the background worker checks for new EI jobs
|           
|           [env: EXPLOIT_INTELLIGENCE_WORKER_POLL_INTERVAL=]
|           [default: 5s]
| 
|       --exploit-intelligence-auth-token <EXPLOIT_INTELLIGENCE_AUTH_TOKEN>
|           Authentication token for the Exploit Intelligence service (static token, for backward compatibility)
|           
|           [env: EXPLOIT_INTELLIGENCE_AUTH_TOKEN=]
| 
| Exploit Intelligence OIDC:
|       --ei-oidc-client-id <ei_oidc_client_id>
|           [env: EXPLOIT_INTELLIGENCE_OIDC_CLIENT_ID=]
| 
|       --ei-oidc-client-secret <ei_oidc_client_secret>
|           [env: EXPLOIT_INTELLIGENCE_OIDC_CLIENT_SECRET=]
| 
|       --ei-oidc-issuer-url <ei_oidc_issuer_url>
|           [env: EXPLOIT_INTELLIGENCE_OIDC_ISSUER_URL=]
| 
|       --ei-oidc-refresh-before <ei_oidc_refresh_before>
|           [env: EXPLOIT_INTELLIGENCE_OIDC_REFRESH_BEFORE=]
|           [default: 30s]
| 
|       --ei-oidc-tls-insecure
|           [env: EXPLOIT_INTELLIGENCE_OIDC_TLS_INSECURE=]
| 
|       --max-cache-size <max-cache-size>
|           Maximum size of the graph cache.
|           
|           [env: TRUSTD_MAX_CACHE_SIZE=8GiB]
|           [default: "200 MiB"]
| 
|       --concurrency <CONCURRENCY>
|           The number of concurrent tasks for analysis (must be > 0).
|           
|           [env: TRUSTIFY_ANALYSIS_CONCURRENCY=]
|           [default: 10]
| 
|       --sbom-published-cutoff-days <SBOM_PUBLISHED_CUTOFF_DAYS>
|           Only consider SBOMs published within this many days. Disabled by default.
|           
|           [env: TRUSTIFY_LATEST_SBOM_PUBLISHED_CUTOFF_DAYS=]

@ctron

ctron commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

When setting the EI URL, but not the EI UI URL, what's expected to happen?

@ctron

ctron commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Are --exploit-intelligence-auth-token <EXPLOIT_INTELLIGENCE_AUTH_TOKEN> and the OIDC configuration mutually exclusive? Then we need to enforce this using clap.

Comment thread modules/exploit-intelligence/src/runner/analysis.rs Outdated
Comment thread modules/exploit-intelligence/src/runner/analysis.rs Outdated
Comment thread modules/exploit-intelligence/src/runner/worker.rs Outdated
Comment thread modules/exploit-intelligence/src/model/mod.rs
Comment thread modules/exploit-intelligence/src/runner/worker.rs Outdated
Comment thread modules/exploit-intelligence/src/runner/worker.rs Outdated
Comment thread Cargo.toml Outdated
@ctron

ctron commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Looks like a great improvement. Less complex. Lot of minor things to clean up. Error handling, types, imports and common stuff.

Also I noticed that the arguments are still mixed:

Exploit Intelligence OIDC:
      --ei-oidc-client-id <ei_oidc_client_id>
          [env: EXPLOIT_INTELLIGENCE_OIDC_CLIENT_ID=]

      --ei-oidc-client-secret <ei_oidc_client_secret>
          [env: EXPLOIT_INTELLIGENCE_OIDC_CLIENT_SECRET=]

      --ei-oidc-issuer-url <ei_oidc_issuer_url>
          [env: EXPLOIT_INTELLIGENCE_OIDC_ISSUER_URL=]

      --ei-oidc-refresh-before <ei_oidc_refresh_before>
          [env: EXPLOIT_INTELLIGENCE_OIDC_REFRESH_BEFORE=]
          [default: 30s]

      --ei-oidc-tls-insecure
          [env: EXPLOIT_INTELLIGENCE_OIDC_TLS_INSECURE=]

      --max-cache-size <max-cache-size>
          Maximum size of the graph cache.
          
          [env: TRUSTD_MAX_CACHE_SIZE=]
          [default: "200 MiB"]

      --concurrency <CONCURRENCY>
          The number of concurrent tasks for analysis (must be > 0).
          
          [env: TRUSTIFY_ANALYSIS_CONCURRENCY=]
          [default: 10]

      --sbom-published-cutoff-days <SBOM_PUBLISHED_CUTOFF_DAYS>
          Only consider SBOMs published within this many days. Disabled by default.
          
          [env: TRUSTIFY_LATEST_SBOM_PUBLISHED_CUTOFF_DAYS=]

EI is claiming other settings.

ruromero pushed a commit to ruromero/trustify that referenced this pull request Jul 29, 2026
- Renumber migration m0002270 → m0002280 to avoid collision with PR guacsec#2534
- Add comment explaining no-op down() in data-fix migration
- Document CVSS score preference order in cvss.rs
- Add generation provenance to SQL test dump files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Assisted-by: Claude Code
ruromero pushed a commit to ruromero/trustify that referenced this pull request Jul 29, 2026
Remove the m0002270 → m0002280 rename — the collision with PR guacsec#2534
does not apply after rebase. Retain the doc comments and test dump
provenance headers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Strum355
Strum355 requested a review from ctron July 29, 2026 13:05
Strum355 and others added 13 commits July 29, 2026 14:18
…ion (#2435)

Create database entities and migration for tracking Exploit Intelligence
analysis jobs. Every job has at least one component row — CycloneDX
single-component jobs get exactly one, SPDX product jobs get N.

Job table tracks lifecycle (sbom_id, vulnerability_id, status) while all
per-component results (scan_id, finding, advisory_id) live exclusively on
the component table. Report URLs are derived at query time from config +
scan_id, not stored.

Includes composite index on (sbom_id, vulnerability_id) for the
find_active_job deduplication query, and FK indexes per CONVENTIONS.md.

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Renumber EI migration from m0002250 to m0002270 to resolve collision
  with m0002250_create_cpe_status
- Move create.exploitIntelligence permission from read:document to
  create:document scope (write operations should not be gated by
  read-only access)
- Remove duplicate doc comment on compute_job_report_url

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 17 service-level tests covering job creation, dedup, dequeue
mechanics (visibility timeout, retry exhaustion, batch limit),
status guard enforcement, failure cascade to components, visibility
extension, component lifecycle, and excluded component semantics.

Add 12 runner-level tests using wiremock to mock the EI service API,
covering both CycloneDX and SPDX flows: successful analysis with
findings, VEX advisory ingestion, EI-side analysis failure, upload
rejection, transient retry, all-excluded products, mixed components,
product-level failure, and error classification.

Fix advisory_id not being linked to components after VEX ingestion
(strip_prefix("urn:uuid:") failed when the ingestor returned a
plain UUID without the prefix).

Fix dequeue_jobs returning stale pre-update models by reflecting
the UPDATE in memory before returning.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…-intelligence

  - New crate modules/exploit-intelligence/ (trustify-module-exploit-intelligence) with its own Error type (3 variants: Unavailable, Database, Any) and ResponseError impl
  - Moved all 13 EI source files, updated crate::exploit_intelligence:: → crate:: paths, replaced SourceDocument round-trip with direct StorageKey::from_sha256()
  - Cleaned fundamental: removed pub mod exploit_intelligence, removed ei_service parameter from configure(), removed backon/reqwest/bytes from deps, removed EI imports from 5 integration test files and test
  helpers
  - Rewired server: imports now come from trustify_module_exploit_intelligence, EI endpoints mounted directly in /api scope alongside importer/ingestor/fundamental
- Use find_also_related join instead of two sequential queries
- Use try_collect::<BytesMut> for stream collection
- Split run_analysis: extract upload_sbom helper
- Match on Format enum instead of if/else
- Avoid unnecessary clone with as_deref
- Import reqwest::multipart types and clean up inline paths
- Replace Cell<u32> with AtomicU32 for retry counters
- Add EiRequestError::Pending variant replacing string sentinel
- Add EiRequestError::Rejected for 4xx with user_message() sanitization
- Use TrustifyQuery + Filtering for generic job list query/filtering
- Handle duplicate key on concurrent create_job gracefully (return 200)
- Log UUID parse failures in VEX ingestion
- Log mark_failed DB errors in worker
- Replace log::warn with tracing::warn, remove log dep
- Rename ei_client.rs to client.rs
- Nest service imports, clean up inline paths across model/polling/worker
- Use strum::IntoStaticStr for status_expr instead of manual match
- Remove dead From<JobStatus> and ProductComponent
- Return Result<_, Error> from endpoints, remove map_err boilerplate
- Add NotFound, Query, From<DbError> to error type
- Accept String path param for job ID, return 404 on invalid UUID
- Add ProductState enum for product_state field
- Fall back to EI URL when UI URL not set
- Rename --worker-poll-interval to --exploit-intelligence-worker-poll-interval
- Enforce mutual exclusion between auth token and OIDC via conflicts_with
- Add server tests for well-known EI enabled and analyze 503 when disabled

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… SKIP LOCKED model

Rewrite the EI worker from a visibility-timeout + backon retry model to
a simpler per-tick held-lock model as requested by ctron.

Each worker tick: BEGIN TX → SELECT FOR UPDATE SKIP LOCKED LIMIT 1 →
process (upload or poll) → COMMIT. On error: ROLLBACK → increment
retry_count outside TX. Permanent errors (4xx, parse failures, missing
data) fail the job immediately without burning retries.

Key changes:
- Replace dequeue_jobs/extend_visibility/fail_exhausted_jobs with
  claim_one_job returning an open transaction
- Remove backon dependency — retry spacing comes from worker_poll_interval
- Split run_analysis into process_pending (upload) and process_running
  (single poll per tick)
- Add AnalysisError enum (Retryable/Permanent) with typed From impls
  for granular error classification
- Add poll cooldown: Running jobs not re-claimed until worker_poll_interval
  has elapsed since last update
- Simplify config: remove 5 polling/retry knobs, add max_retries and
  concurrency (N independent worker loops via LocalSet)
- Remove visible_at column from entity and migration (never deployed)
- Remove AllowNotUpdated status guards (SKIP LOCKED prevents races)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Internal error details (HTTP status codes, EI response bodies) should
not be exposed to API consumers. The raw error remains in the database
column and server logs for operators. The UI handles user-facing
messaging based on the job status.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… dedup

- Remove copied JobStatus/JobFinding enums; use entity enums directly
  with Serialize/Deserialize/ToSchema derives
- Rename Finding → ExploitIntelligenceFinding for OpenAPI consistency
- Replace multi-pass ComponentCounts with single-pass fold
- Make PollResponse.status and ProductReportEntry.state enums (ReportStatus)
- Extract ei_get<T> helper to deduplicate HTTP request/classify/parse
- Extract parse_justification_status to deduplicate finding mapping
- Add PollResponse::vex() accessor
- Replace anyhow wrappers with typed From impls; thiserror #[from]
- Remove redundant find_active_job pre-check from analyze endpoint
- Use db_ro for duplicate-key fallback read
- Remove backon from workspace Cargo.toml
- Move EI args to end of Run struct to fix CLI help grouping
- Add shutdown signal via oneshot channel (RAII drop on server exit)
- Move all inline qualified paths to file-level imports

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Strum355
Strum355 force-pushed the feature/exploit-intelligence-integration branch from 05ecbd4 to f715b7f Compare July 29, 2026 13:18
@Strum355
Strum355 enabled auto-merge July 29, 2026 13:57
@Strum355
Strum355 added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit 9d15ed2 Jul 29, 2026
12 checks passed
@Strum355
Strum355 deleted the feature/exploit-intelligence-integration branch July 29, 2026 18:14
@github-project-automation github-project-automation Bot moved this to Done in Trustify Jul 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

4 participants