Skip to content

feat(enrichment): ingest KEV,EPSS,OSV feeds with vulnerability rescoring#35

Merged
Arzu-N merged 12 commits into
mainfrom
feature/enrichment-and-fix-first-scoring
Jul 13, 2026
Merged

feat(enrichment): ingest KEV,EPSS,OSV feeds with vulnerability rescoring#35
Arzu-N merged 12 commits into
mainfrom
feature/enrichment-and-fix-first-scoring

Conversation

@Arzu-N

@Arzu-N Arzu-N commented Jul 12, 2026

Copy link
Copy Markdown
Member

What does this change?

This PR adds vulnerability feed enrichment and auto re-scoring

Related issue

Closes #9

Area

  • Frontend (apps/web)
  • Backend
  • Docs
  • Other

Screenshots

If this touches the UI, drop before and after screenshots here.

Checklist

  • I ran bun run typecheck && bun run build in apps/web (for frontend changes)
  • I bumped the version if this is a meaningful change
  • Commits are small and focused, with no AI attribution lines
  • I updated docs or AGENTS.md if behavior or structure changed

Summary by CodeRabbit

  • New Features

    • Added vulnerability enrichment using CVE, EPSS, KEV, and OSV data.
    • Vulnerability records now display and retain CVE identifiers.
    • Added scheduled feed synchronization to refresh vulnerability data automatically.
    • Added reachability-aware prioritization for more accurate vulnerability severity.
    • Improved enrichment resilience so individual synchronization errors do not stop the full process.
  • Bug Fixes

    • Improved handling of missing enrichment data with safe defaults.
    • Added clearer validation when required vulnerability information is unavailable.

@Arzu-N Arzu-N self-assigned this Jul 12, 2026
@Arzu-N Arzu-N added the area: backend Backend service or library label Jul 12, 2026
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds CVE persistence, external KEV/EPSS/OSV clients, revised priority scoring, and scheduled feed synchronization that updates stored vulnerabilities.

Changes

Enrichment and rescoring

Layer / File(s) Summary
Vulnerability contracts and persistence
backend/libs/common/..., backend/libs/domain/..., backend/libs/persistence/...
Vulnerability DTOs, entities, mappings, migration, and domain records now carry CVE, EPSS, and reachability data.
Feed clients and enrichment service
backend/libs/enrichment/...
Adds KEV, EPSS, and OSV DTOs and clients, RestTemplate configuration, KEV caching, and combined feed retrieval with error translation.
Priority scoring and scanner integration
backend/libs/domain/..., backend/libs/scanning/VulnerabilityScanner.java, backend/libs/scanning/service/VulnerabilityUpdateService.java, backend/libs/scanning/src/test/...
Priority rules use KEV, CVSS, EPSS, severity, and reachability; scanner validation and feed-based entity updates are added.
Scheduled feed synchronization
backend/apps/..., backend/libs/scanning/service/FeedSyncService.java, backend/libs/scanning/src/test/...
Spring scheduling, enrichment component scanning, synchronization configuration, pagination, per-vulnerability feed retrieval, and delegated persistence are wired together.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Scheduler
  participant FeedSyncService
  participant VulnerabilityRepository
  participant FeedService
  participant VulnerabilityUpdateService
  Scheduler->>FeedSyncService: invoke syncFeeds()
  FeedSyncService->>VulnerabilityRepository: load vulnerability page
  FeedSyncService->>FeedService: fetchFeeds(cve, packageName, ecosystem)
  FeedService-->>FeedSyncService: return FeedResult
  FeedSyncService->>VulnerabilityUpdateService: update vulnerability
  VulnerabilityUpdateService->>VulnerabilityRepository: save updated vulnerability
Loading

Possibly related PRs

Suggested reviewers: martian56

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: ingesting KEV, EPSS, and OSV feeds for vulnerability rescoring.
Linked Issues check ✅ Passed The changes satisfy #9 by scheduling KEV/EPSS/OSV ingestion, updating priority with CVSS/EPSS/KEV/reachability, and rescoring without GitHub requests.
Out of Scope Changes check ✅ Passed No obvious out-of-scope changes are present; the added configs, clients, DTOs, and tests support the feed enrichment and rescoring flow.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/enrichment-and-fix-first-scoring

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai
coderabbitai Bot requested a review from martian56 July 12, 2026 20:06

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 9

🧹 Nitpick comments (4)
backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/OsvClient.java (1)

17-24: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Externalize the OSV API URL to configuration.

The hardcoded URL prevents environment-specific overrides (e.g., pointing to a mock or staging OSV instance) and reduces testability. Consider using @Value or a @ConfigurationProperties class.

♻️ Proposed refactor
 `@Component`
 public class OsvClient {
     private final RestTemplate restTemplate;
+    private final String osvUrl;
 
-    public OsvClient(RestTemplate restTemplate) {
+    public OsvClient(RestTemplate restTemplate,
+                    `@Value`("${osv.api.url:https://api.osv.dev/v1/query}") String osvUrl) {
         this.restTemplate = restTemplate;
+        this.osvUrl = osvUrl;
     }
 
-    private static final String URL = "https://api.osv.dev/v1/query";
-
     public OsvResponse query(String packageName, String ecosystem) {
         OsvPackage osvPackage = new OsvPackage().setName(packageName).setEcosystem(ecosystem);
         OsvRequest osvRequest = new OsvRequest().setPkg(osvPackage);
-        return restTemplate.postForObject(URL, osvRequest, OsvResponse.class);
+        return restTemplate.postForObject(osvUrl, osvRequest, OsvResponse.class);
     }
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/OsvClient.java`
around lines 17 - 24, Externalize the hardcoded OSV endpoint in OsvClient by
injecting the URL through application configuration, using the existing Spring
configuration pattern such as `@Value` or a configuration-properties class. Update
query to use the injected/configured value instead of the static URL constant,
while preserving the current request and response behavior.
backend/libs/domain/src/main/java/dev/cleat/domain/PriorityCalculator.java (1)

16-17: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add explicit parentheses for operator precedence clarity.

The expression vulnerability.kev() || vulnerability.cvss() >= 9.0 && vulnerability.reachable() == Reachable.REACHABLE relies on Java's && binding tighter than ||. While correct, this is a common readability trap that could lead to bugs during future edits.

♻️ Proposed refactor
-        if (vulnerability.kev() || vulnerability.cvss() >= 9.0 && vulnerability.reachable() == Reachable.REACHABLE) {
+        if (vulnerability.kev() || (vulnerability.cvss() >= 9.0 && vulnerability.reachable() == Reachable.REACHABLE)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/libs/domain/src/main/java/dev/cleat/domain/PriorityCalculator.java`
around lines 16 - 17, Update the condition in PriorityCalculator to add explicit
parentheses around the combined CVSS threshold and reachability checks,
preserving the existing logic that KEV vulnerabilities or high-CVSS reachable
vulnerabilities return Priority.URGENT.
backend/libs/enrichment/src/main/java/dev/cleat/enrichment/service/FeedService.java (1)

39-51: 🩺 Stability & Availability | 🔵 Trivial

Consider partial enrichment resilience.

fetchFeeds aggregates three external API calls in a single try-catch. If any one feed (e.g., OSV) is unavailable, all enrichment for that CVE is lost — KEV and EPSS updates are also discarded. For a scheduled job processing many vulnerabilities, a single flaky feed can block all enrichment across the batch.

Consider catching failures per-feed and returning partial results (e.g., null for the failed feed) so transient failures in one API don't prevent updates from the others.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/service/FeedService.java`
around lines 39 - 51, Update FeedService.fetchFeeds to isolate kevClient.isKev,
epssClient.fetchScore, and osvClient.query into separate failure-handling paths,
allowing each successful feed value to be retained while assigning the failed
feed’s result as null. Return a FeedResult with these partial values and avoid
rethrowing one feed’s exception as a FeedSyncException that discards the other
enrichments.
backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java (1)

40-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding tests for null-CVE skip and error-handling paths.

The current test covers only the happy path. Two important branches in FeedSyncService.syncFeeds are untested:

  1. Null CVE skip — entities with cve == null are logged and skipped. A test verifying that saveAll is not called with updated fields for these entities would guard against regressions.
  2. Exception handling — if fetchFeeds throws, the entity should retain its original values and processing should continue for remaining entities.

Additionally, new OsvResponse() has a null summary, so the title-update path (v.setTitle(feed.osv().getSummary())) is never exercised.

🧪 Suggested additional tests
`@Test`
void shouldSkipVulnerabilitiesWithoutCve() {
    VulnerabilityEntity noCveEntity = new VulnerabilityEntity()
            .setCve(null)
            .setPackageName("spring-core")
            .setEcosystem("maven")
            .setCvss(9.5)
            .setSeverity(Severity.HIGH)
            .setReachable(Reachable.REACHABLE);

    when(vulnerabilityRepository.findAll()).thenReturn(List.of(noCveEntity));

    feedSyncService.syncFeeds();

    verify(feedService, never()).fetchFeeds(any(), any(), any());
    verify(vulnerabilityRepository).saveAll(any());
    assertNull(noCveEntity.getKev());
}

`@Test`
void shouldContinueProcessingWhenFetchFails() {
    VulnerabilityEntity failingEntity = new VulnerabilityEntity()
            .setCve("CVE-2025-9999")
            .setPackageName("log4j-core")
            .setEcosystem("maven")
            .setCvss(7.5)
            .setSeverity(Severity.HIGH)
            .setReachable(Reachable.NOT_REACHABLE);

    when(vulnerabilityRepository.findAll()).thenReturn(List.of(failingEntity));
    when(feedService.fetchFeeds(any(), any(), any()))
            .thenThrow(new RuntimeException("Feed unavailable"));

    feedSyncService.syncFeeds();

    verify(vulnerabilityRepository).saveAll(any());
    // Entity should retain original values
    assertNull(failingEntity.getKev());
    assertNull(failingEntity.getEpss());
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java`
around lines 40 - 70, Add tests alongside shouldRescoreAllVulnerabilities for
the null-CVE and fetchFeeds exception branches in FeedSyncService.syncFeeds:
verify null-CVE entities skip feedService.fetchFeeds and retain unchanged
fields, and verify fetch failures still call saveAll while preserving original
values and allowing subsequent entities to process. Also provide a non-null
OsvResponse summary in the happy-path test to exercise title updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/apps/api/src/main/java/dev/cleat/api/CleatApiApplication.java`:
- Line 20: Add a TaskScheduler bean in CleatApiApplication alongside
`@EnableScheduling`, using ThreadPoolTaskScheduler with a pool size of 2 and the
“feed-sync-” thread-name prefix, so scheduled tasks can execute concurrently
without changing FeedSyncService.syncFeeds().

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/EpssClient.java`:
- Around line 17-24: Update fetchScore in EpssClient to construct the request
through RestTemplate URI-variable expansion or UriComponentsBuilder, passing cve
as an encoded path/query value instead of concatenating it directly with URL.
Preserve the existing response validation and score extraction behavior.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java`:
- Around line 18-24: Update KevClient.isKev and the FeedSyncService sync flow so
the CISA feed is fetched once per sync cycle or cached with an appropriate TTL,
then reuse a parsed vulnerability ID Set for membership checks instead of
calling restTemplate.getForObject for every CVE. Preserve false results for
missing or empty feed data and ensure the cache refreshes when it expires or a
new sync cycle begins.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/OsvClient.java`:
- Around line 13-15: Update the shared RestTemplate bean in RestTemplateConfig
to configure explicit connection and read timeouts, ensuring
OsvClient.postForObject calls cannot block indefinitely. Preserve the existing
bean reuse by enrichment clients and choose appropriate finite timeout values.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/config/RestTemplateConfig.java`:
- Around line 10-13: Update the restTemplate() bean in RestTemplateConfig to
construct the RestTemplate with finite connection and read timeouts using the
project’s established HTTP request factory or timeout configuration. Ensure the
configured timeouts apply to calls made by EpssClient, KevClient, and OsvClient
while preserving the existing bean contract.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/KevVulnerability.java`:
- Line 8: Update the cveId field in KevVulnerability with an explicit Jackson
property mapping for the KEV feed’s exact cveID name, preserving the existing
field and ensuring isKev() can match deserialized entries.

In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java`:
- Around line 36-63: Remove the broad transaction boundary from syncFeeds and
ensure feedService.fetchFeeds is invoked outside any transaction spanning the
full vulnerability loop. Persist updates through separate transaction
boundaries, such as a dedicated transactional update method per vulnerability or
batch, while preserving the existing skip, error handling, and priority
calculation behavior.
- Line 40: Update the vulnerability loading flow in FeedSyncService to replace
vulnerabilityRepository.findAll() with paginated retrieval using
findAll(Pageable). Process each page or batch incrementally, preserving the
existing synchronization behavior while avoiding loading the full vulnerability
dataset into memory.
- Around line 46-63: Update the per-vulnerability processing in FeedSyncService
so vulnerabilityRepository.saveAll only receives entities whose complete
enrichment, including priorityCalculator.calculate and setPriority, succeeds.
Track successfully enriched vulnerabilities separately and exclude failed
entities from persistence; do not save partially updated state from the catch
path.

---

Nitpick comments:
In `@backend/libs/domain/src/main/java/dev/cleat/domain/PriorityCalculator.java`:
- Around line 16-17: Update the condition in PriorityCalculator to add explicit
parentheses around the combined CVSS threshold and reachability checks,
preserving the existing logic that KEV vulnerabilities or high-CVSS reachable
vulnerabilities return Priority.URGENT.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/OsvClient.java`:
- Around line 17-24: Externalize the hardcoded OSV endpoint in OsvClient by
injecting the URL through application configuration, using the existing Spring
configuration pattern such as `@Value` or a configuration-properties class. Update
query to use the injected/configured value instead of the static URL constant,
while preserving the current request and response behavior.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/service/FeedService.java`:
- Around line 39-51: Update FeedService.fetchFeeds to isolate kevClient.isKev,
epssClient.fetchScore, and osvClient.query into separate failure-handling paths,
allowing each successful feed value to be retained while assigning the failed
feed’s result as null. Return a FeedResult with these partial values and avoid
rethrowing one feed’s exception as a FeedSyncException that discards the other
enrichments.

In
`@backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java`:
- Around line 40-70: Add tests alongside shouldRescoreAllVulnerabilities for the
null-CVE and fetchFeeds exception branches in FeedSyncService.syncFeeds: verify
null-CVE entities skip feedService.fetchFeeds and retain unchanged fields, and
verify fetch failures still call saveAll while preserving original values and
allowing subsequent entities to process. Also provide a non-null OsvResponse
summary in the happy-path test to exercise title updates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: bdc05b54-70e7-46e0-a9e0-1a3a0cf5befb

📥 Commits

Reviewing files that changed from the base of the PR and between 517832e and f1e765e.

📒 Files selected for processing (29)
  • backend/apps/api/build.gradle.kts
  • backend/apps/api/src/main/java/dev/cleat/api/CleatApiApplication.java
  • backend/libs/common/src/main/java/dev/cleat/common/dto/request/VulnerabilityRequestDto.java
  • backend/libs/common/src/main/java/dev/cleat/common/dto/response/VulnerabilityResponseDto.java
  • backend/libs/common/src/main/java/dev/cleat/common/exception/FeedSyncException.java
  • backend/libs/domain/src/main/java/dev/cleat/domain/PriorityCalculator.java
  • backend/libs/domain/src/main/java/dev/cleat/domain/model/Vulnerability.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/EpssClient.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/OsvClient.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/config/RestTemplateConfig.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/EpssData.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/EpssResponse.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/FeedResult.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/KevResponse.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/KevVulnerability.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/OsvPackage.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/OsvRequest.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/OsvResponse.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/service/FeedService.java
  • backend/libs/persistence/src/main/java/dev/cleat/persistence/entity/VulnerabilityEntity.java
  • backend/libs/persistence/src/main/java/dev/cleat/persistence/mapper/VulnerabilityMapper.java
  • backend/libs/persistence/src/main/resources/db/migration/V6__alter_vulnerability_table.sql
  • backend/libs/scanning/build.gradle.kts
  • backend/libs/scanning/src/main/java/dev/cleat/scanning/VulnerabilityScanner.java
  • backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java
  • backend/libs/scanning/src/main/resources/application.yml
  • backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java
  • backend/libs/scanning/src/test/java/dev/cleat/scanning/VulnerabilityScannerTest.java

Comment thread backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/apps/api/src/main/java/dev/cleat/api/config/SchedulingConfig.java`:
- Around line 8-17: Move the taskScheduler bean from SchedulingConfig into a
package scanned by CleatWorkerApplication, or define it directly in that
application’s configuration. Preserve the existing ThreadPoolTaskScheduler
settings, including pool size 4 and the "scheduler-" thread prefix, and remove
the unused API-side bean.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db4c7f11-ee19-4b4b-8a22-3c071b3c3012

📥 Commits

Reviewing files that changed from the base of the PR and between f1e765e and 948bc99.

📒 Files selected for processing (5)
  • backend/apps/api/src/main/java/dev/cleat/api/config/SchedulingConfig.java
  • backend/apps/api/src/main/resources/application.yml
  • backend/apps/worker/src/main/java/dev/cleat/worker/CleatWorkerApplication.java
  • backend/apps/worker/src/main/resources/application.yml
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/config/RestTemplateConfig.java
🚧 Files skipped from review as they are similar to previous changes (1)
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/config/RestTemplateConfig.java

Comment thread backend/apps/api/src/main/java/dev/cleat/api/config/SchedulingConfig.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java (1)

24-24: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Encapsulate cache: make getKevCache() private and return an unmodifiable set.

getKevCache() is only called internally by isKev(); exposing it publicly leaks a mutable HashSet (from Collectors.toSet()) that callers could modify, corrupting the cache. Make the method private and wrap the result in Collections.unmodifiableSet or use Set.copyOf.

♻️ Proposed refactor
-    public Set<String> getKevCache() {
+    private Set<String> getKevCache() {
         if (kevCache == null || System.currentTimeMillis() - cacheTimestamp > CACHE_TTL_MS) {
             KevResponse response = restTemplate.getForObject(URL, KevResponse.class);
             kevCache = (response == null || response.getVulnerabilities() == null)
                     ? Set.of()
                     : response.getVulnerabilities().stream()
                             .map(KevVulnerability::getCveId)
-                            .collect(Collectors.toSet());
+                            .collect(Collectors.toUnmodifiableSet());
             cacheTimestamp = System.currentTimeMillis();
         }
         return kevCache;
     }

Also applies to: 28-32

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java`
at line 24, Update getKevCache() to be private and return an unmodifiable set,
using Collections.unmodifiableSet or Set.copyOf around the collected cache
before returning it. Preserve isKev()’s internal use of the cache and ensure
callers cannot mutate the underlying HashSet.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java`:
- Around line 24-36: Make getKevCache() synchronized so reads and refreshes of
the shared kevCache and cacheTimestamp state are atomic across threads. Preserve
the existing TTL check, feed refresh, and cached return behavior while ensuring
concurrent callers cannot observe partially updated state or trigger redundant
refreshes.

---

Nitpick comments:
In
`@backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java`:
- Line 24: Update getKevCache() to be private and return an unmodifiable set,
using Collections.unmodifiableSet or Set.copyOf around the collected cache
before returning it. Preserve isKev()’s internal use of the cache and ensure
callers cannot mutate the underlying HashSet.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 69d7a66c-f2b9-4987-a4d3-c1f3a33efeba

📥 Commits

Reviewing files that changed from the base of the PR and between 948bc99 and f1e4235.

📒 Files selected for processing (3)
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/EpssClient.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/KevVulnerability.java
🚧 Files skipped from review as they are similar to previous changes (2)
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/dto/KevVulnerability.java
  • backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/EpssClient.java

Comment thread backend/libs/enrichment/src/main/java/dev/cleat/enrichment/client/KevClient.java Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java (1)

31-49: 🚀 Performance & Scalability | 🟠 Major | ⚡ Quick win

Delegation fix resolves prior transactional/partial-enrichment issues, but unpaginated findAll() remains.

Moving persistence into per-entity vulnerabilityUpdateService.update(v, feed) calls correctly resolves the earlier long-running-transaction and partial-enrichment-persisted issues. However, Line 34's vulnerabilityRepository.findAll() still loads the entire vulnerability table into memory, which was previously flagged as a major issue and has not been addressed.

♻️ Suggested pagination approach
-        List<VulnerabilityEntity> vulnerabilities = vulnerabilityRepository.findAll();
-        for (VulnerabilityEntity v : vulnerabilities) {
-            ...
-        }
+        Pageable pageable = PageRequest.of(0, BATCH_SIZE);
+        Page<VulnerabilityEntity> page;
+        do {
+            page = vulnerabilityRepository.findAll(pageable);
+            for (VulnerabilityEntity v : page.getContent()) {
+                ...
+            }
+            pageable = pageable.next();
+        } while (page.hasNext());
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java`
around lines 31 - 49, Replace the unbounded vulnerabilityRepository.findAll()
call in syncFeeds() with paginated retrieval and process one page or batch at a
time. Continue applying the existing per-entity CVE validation, feed fetching,
and vulnerabilityUpdateService.update() flow, and stop when no further pages
remain without loading the entire table into memory.
🧹 Nitpick comments (2)
backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java (1)

39-70: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider testing the skip and error-continuation branches.

The refactored test only covers the happy path where a valid CVE resolves and update() succeeds. It doesn't cover v.getCve() == null (skip branch, Line 36-39 of FeedSyncService) or vulnerabilityUpdateService.update() throwing (should log and continue to the next entity, Line 44-46). Given these are the resiliency paths this refactor is meant to preserve, dedicated tests would guard against regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java`
around lines 39 - 70, Extend FeedSyncServiceTest with dedicated tests for the
skip and error-continuation paths in feedSyncService.syncFeeds(). Verify
entities with a null CVE are skipped without fetching feeds or updating, and
verify an update() exception is logged while processing continues to the next
vulnerability entity.
backend/libs/scanning/src/main/java/dev/cleat/scanning/service/VulnerabilityUpdateService.java (1)

24-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

No dedicated unit test for VulnerabilityUpdateService.

This class contains the real business logic (field mutation, priority recalculation, persistence), but only FeedSyncServiceTest mocks it out; no test in the provided files exercises update() directly (e.g., null-epss handling, title-from-OSV branch, priority thresholds).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/VulnerabilityUpdateService.java`
around lines 24 - 35, The VulnerabilityUpdateService.update method lacks direct
unit coverage for its mutation, recalculation, and persistence behavior. Add
focused tests for update() covering null EPSS input, the OSV-summary title
update branch, and priority threshold outcomes, while verifying the updated
entity is saved and the priority calculator receives the expected Vulnerability
values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/VulnerabilityUpdateService.java`:
- Around line 24-35: Prevent lost updates in VulnerabilityUpdateService.update
by adding optimistic locking to VulnerabilityEntity with an appropriate `@Version`
field, or re-reading the current entity within the update transaction before
applying feed changes. Ensure concurrent modifications to unrelated
vulnerability columns are detected or preserved before
vulnerabilityRepository.save persists the update.

---

Duplicate comments:
In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java`:
- Around line 31-49: Replace the unbounded vulnerabilityRepository.findAll()
call in syncFeeds() with paginated retrieval and process one page or batch at a
time. Continue applying the existing per-entity CVE validation, feed fetching,
and vulnerabilityUpdateService.update() flow, and stop when no further pages
remain without loading the entire table into memory.

---

Nitpick comments:
In
`@backend/libs/scanning/src/main/java/dev/cleat/scanning/service/VulnerabilityUpdateService.java`:
- Around line 24-35: The VulnerabilityUpdateService.update method lacks direct
unit coverage for its mutation, recalculation, and persistence behavior. Add
focused tests for update() covering null EPSS input, the OSV-summary title
update branch, and priority threshold outcomes, while verifying the updated
entity is saved and the priority calculator receives the expected Vulnerability
values.

In
`@backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java`:
- Around line 39-70: Extend FeedSyncServiceTest with dedicated tests for the
skip and error-continuation paths in feedSyncService.syncFeeds(). Verify
entities with a null CVE are skipped without fetching feeds or updating, and
verify an update() exception is logged while processing continues to the next
vulnerability entity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c6d89357-e0fe-4dd8-8397-878c9f4c9d91

📥 Commits

Reviewing files that changed from the base of the PR and between f1e4235 and db8d1e8.

📒 Files selected for processing (3)
  • backend/libs/scanning/src/main/java/dev/cleat/scanning/service/FeedSyncService.java
  • backend/libs/scanning/src/main/java/dev/cleat/scanning/service/VulnerabilityUpdateService.java
  • backend/libs/scanning/src/test/java/dev/cleat/scanning/FeedSyncServiceTest.java

@Arzu-N

Arzu-N commented Jul 13, 2026

Copy link
Copy Markdown
Member Author

Hi, @martian56 !I've addressed all of the CodeRabbit review comments and completed the requested changes.The PR now passed the CodeRabbit review.When you have time could you please take a look and review it?Thanks!

@Arzu-N
Arzu-N merged commit ba925e6 into main Jul 13, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Backend service or library

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enrichment and fix-first scoring

3 participants