Skip to content

refactor(remediation): give each CVE its own severity and advisories - #635

Merged
Strum355 merged 2 commits into
guacsec:mainfrom
Strum355:nsc/remediate-gh-action-2
Sep 15, 2026
Merged

Strum355 merged 2 commits into
guacsec:mainfrom
Strum355:nsc/remediate-gh-action-2

Conversation

@Strum355

@Strum355 Strum355 commented Sep 11, 2026 •

Copy link
Copy Markdown
Member

Description

extractRemediations flattened every CVE on a dependency into one severity, one CVE list, and one merged advisory list. That is lossy: a dependency with a MEDIUM and a CRITICAL CVE reported both as CRITICAL, and advisories could no longer be traced back to the CVE they belong to. The report generator consumed exactly that flattened data, so its per-CVE table showed every row at the dependency's max severity with a shared advisory list — the extractor had the correct per-CVE answer at parse time and was discarding it before the report could use it.

Model vulnerabilities as a per-CVE array instead, each entry keeping the severity and advisories it was reported with. The report now renders one row per CVE from its own data. A dependency-level severity is derived on demand via maxSeverity (used by the bundled and dry-run views) rather than stored, so it cannot drift out of sync with the underlying CVEs.

Before:
image

After:
image

Part of TC-5615

Checklist

  • I have followed this repository's contributing guidelines.
  • I will adhere to the project's code of conduct.

Summary by Sourcery

Preserve vulnerability details per CVE and derive dependency-level severity from the underlying vulnerabilities.

New Features:

  • Preserve each CVE’s individual severity and associated advisories in remediation data.
  • Expose maxSeverity for deriving dependency-level severity when needed.

Bug Fixes:

  • Prevent remediation reports from inflating lower-severity CVEs to a dependency’s highest severity or assigning unrelated advisories to them.

Enhancements:

  • Update dependency, bundled, and dry-run reports to consume per-CVE vulnerability data while deriving aggregate severity on demand.

Tests:

  • Add coverage for per-CVE severity and advisory attribution, deduplication, aggregate severity, recommendation-only remediations, and updated report output.

Chores:

  • Update remediation type definitions and consumers to replace flattened CVE, severity, and advisory fields with vulnerabilities.

@Strum355
Strum355 requested a review from ruromero September 11, 2026 12:04
@sourcery-ai

sourcery-ai Bot commented Sep 11, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

The PR changes remediation data from lossy dependency-level CVE, severity, and advisory fields to a per-CVE vulnerabilities array, then derives aggregate severity only where needed and renders each CVE with its own severity and advisories. Documentation and tests are updated to cover extraction, merging, reporting, and recommendation-only behavior.

Flow diagram for per-CVE remediation extraction and reporting

flowchart LR
    A["Provider issue data"] --> B["extractRemediations"]
    B --> C["Remediation with vulnerabilities[]"]
    C --> D["Each CVE keeps severity and advisories"]
    C --> E["maxSeverity(vulnerabilities)"]
    D --> F["Per-dependency report: one row per CVE"]
    E --> G["Bundled and dry-run views: dependency severity"]
Loading

File-Level Changes

Change Details Files
Replace flattened dependency vulnerability fields with per-CVE vulnerability records.
  • Define Remediation and Vulnerability shapes with per-CVE severity and advisories.
  • Accumulate, deduplicate, normalize, and merge vulnerabilities by CVE while preserving the highest severity and union of advisories.
  • Represent recommendation-only remediations with an empty vulnerability list.
src/remediation.js
src/remediate.js
Derive dependency severity and update report generation for the new vulnerability model.
  • Add maxSeverity for on-demand dependency severity used by dry-run and bundled grouping.
  • Render each CVE using its own severity and advisory list in per-dependency reports.
  • Aggregate CVE ids and de-duplicated advisories for bundled output.
src/remediation.js
src/remediation_report.js
Update API documentation and tests to validate the per-CVE data flow.
  • Replace assertions for cves, severity, and top-level advisories with vulnerabilities assertions.
  • Add coverage for distinct CVE severities/advisories, duplicate CVE merging, derived severity, and recommendation-only entries.
  • Update report tests for per-CVE rendering and null/undefined vulnerability handling.
src/index.js
test/remediate.test.js
test/remediation.test.js
test/remediation_report.test.js

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[bot]
sourcery-ai Bot previously approved these changes Sep 11, 2026

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

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Approved.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@codecov-commenter

codecov-commenter commented Sep 11, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.26087% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 92.10%. Comparing base (efdc87d) to head (2179c79).

Files with missing lines Patch % Lines
src/remediation.js 97.10% 2 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #635      +/-   ##
==========================================
+ Coverage   92.02%   92.10%   +0.07%     
==========================================
  Files          45       45              
  Lines       10602    10668      +66     
  Branches     1921     1930       +9     
==========================================
+ Hits         9757     9826      +69     
+ Misses        845      842       -3     
Flag Coverage Δ
unit-tests 92.10% <98.26%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/remediate.js 95.53% <100.00%> (-0.15%) ⬇️
src/remediation_report.js 99.04% <100.00%> (+0.11%) ⬆️
src/remediation.js 93.56% <97.10%> (+1.39%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Sep 14, 2026

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

Sourcery assessment

Approved.

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Two test fixture bugs confirmed by running the test suite and verifying against the backend API shape. The production code is correct — the changes to extractAdvisories and getFixedInPurl target the actual backend format.

Bug 1 — fixedIn must be an array (3 tests currently failing)

The backend Remediation.fixedIn has always been List<String>. The PR correctly removed the old typeof fixedIn === 'string' guard, but 3 of the new fixtures in per-CVE vulnerabilities still pass a string PURL. Running npx mocha test/remediation.test.js confirms: 3 failing, 32 passing — all 3 fail with TypeError: fixedInVersions.filter is not a function.

Fix: wrap each string in an array.

Bug 2 — Advisory fixture format wrong (2 tests fail after Bug 1 is fixed)

The backend produces advisories under issue.remediation.advisories[].advisory.{id,url} — exactly the shape the new extractAdvisories reads. issue.advisories at the issue level never existed in this backend. Two of the new tests set advisories at issue level; extractAdvisories silently drops them, causing vuln.advisories to be [].

Fix: move advisory data to issue.remediation.advisories = [{ advisory: { id, url } }].

Minor gap — maxSeverity not re-exported from the public entry point

maxSeverity is exported from src/remediation.js but not from src/index.js. External callers who previously read rem.severity (now removed from the Remediation type) have no accessible replacement without reaching into the internal module path.

Suggest adding it to src/index.js line 27:

export { extractRemediations, maxSeverity } from "./remediation.js"

Comment thread test/remediation.test.js Outdated
Comment thread test/remediation.test.js Outdated
Comment thread test/remediation.test.js Outdated
Comment thread test/remediation.test.js Outdated
Comment thread test/remediation.test.js Outdated
extractRemediations flattened every CVE on a dependency into one
severity, one CVE list, and one merged advisory list. That is lossy: a
dependency with a MEDIUM and a CRITICAL CVE reported both as CRITICAL,
and advisories could no longer be traced back to the CVE they belong to.
The report generator consumed exactly that flattened data, so its
per-CVE table showed every row at the dependency's max severity with a
shared advisory list — the extractor had the correct per-CVE answer at
parse time and was discarding it before the report could use it.

Model vulnerabilities as a per-CVE array instead, each entry keeping the
severity and advisories it was reported with. The report now renders one
row per CVE from its own data. A dependency-level severity is derived on
demand via maxSeverity (used by the bundled and dry-run views) rather
than stored, so it cannot drift out of sync with the underlying CVEs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
sourcery-ai[bot]
sourcery-ai Bot previously approved these changes Sep 15, 2026

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

Sourcery assessment

Approved.

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

Sourcery assessment

Approved.

@ruromero ruromero left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM Thanks!

@Strum355
Strum355 merged commit 43fdb9f into guacsec:main Sep 15, 2026
7 checks passed
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.

3 participants