Skip to content

feat(report): display remediation details and advisory info in HTML report (TC-4523)#643

Open
ruromero wants to merge 4 commits into
guacsec:mainfrom
ruromero:TC-4523
Open

feat(report): display remediation details and advisory info in HTML report (TC-4523)#643
ruromero wants to merge 4 commits into
guacsec:mainfrom
ruromero:TC-4523

Conversation

@ruromero

@ruromero ruromero commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Add RemediationDetails React component that renders remediation entries with category labels (Workaround, Vendor Fix, etc.), details links, and advisory information
  • Integrate into VulnerabilityRow priority chain: trustedContent > fixedIn > remediations > no remediations
  • Add TypeScript types (RemediationCategory, AdvisoryInfo, RemediationInfo) to report.ts and update hasRemediations() to include the new remediations field
  • Add testHtmlRemediationDetails integration test verifying CVE-2022-42003 renders workaround category, details link, and advisory title

Jira

TC-4523

Test plan

  • mvn verify — all 341 tests pass
  • yarn lint — no lint errors
  • yarn build — UI builds successfully
  • mvn spotless:apply — code formatted
  • New integration test testHtmlRemediationDetails validates DOM structure

🤖 Generated with Claude Code

Summary by Sourcery

Display detailed remediation information in HTML vulnerability reports and surface it when no fixed version or trusted content is available.

New Features:

  • Add support for structured remediation entries on vulnerabilities, including category, details, links, and advisory metadata.
  • Render remediation details in vulnerability rows, prioritizing trusted content, fixed versions, then remediation entries.
  • Introduce new TypeScript types for remediation categories, advisory info, and remediation details in the report API.

Tests:

  • Add an HTML integration test validating remediation details for CVE-2022-42003, including category label, details link, and advisory title.

…eport (TC-4523)

Add RemediationDetails component to render remediation entries with category
labels, details links, and advisory information when no fixedIn version is
available. The component slots into the existing priority chain:
trustedContent > fixedIn > remediations > no remediations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR extends the HTML vulnerability report to show structured remediation details (category labels, linked details, and advisory info) and wires those into the existing remediation priority chain, backed by new TypeScript types and an integration test for the rendered DOM.

Flow diagram for remediation rendering priority in VulnerabilityRow

flowchart LR
  start([VulnerabilityRow render]) --> hasTrustedContent{remediation.trustedContent}
  hasTrustedContent -->|yes| showTrusted[Render RemediationLink]
  hasTrustedContent -->|no| hasFixedIn{remediation.fixedIn}
  hasFixedIn -->|yes| showFixedIn[Render VulnerabilityLink]
  hasFixedIn -->|no| hasRemediations{remediation.remediations length > 0}
  hasRemediations -->|yes| showDetails[Render RemediationDetails]
  hasRemediations -->|no| hasAnyRemediation{hasRemediations helper}
  hasAnyRemediation -->|no| showEmpty[Render empty span]
  hasAnyRemediation -->|yes| endNode([Render nothing])
Loading

File-Level Changes

Change Details Files
Add structured remediation data model and presence check to the report API.
  • Extend Vulnerability.remediation to include a remediations array of RemediationInfo objects.
  • Introduce RemediationCategory, AdvisoryInfo, and RemediationInfo TypeScript types to describe remediation entries and linked advisories.
  • Update hasRemediations() to treat trustedContent, fixedIn, and non-empty remediations as valid remediation sources.
ui/src/api/report.ts
Render remediation entries with category labels, details/advisory links, and integrate them into the remediation priority chain in the HTML report UI.
  • Create RemediationDetails React component that maps remediation categories to human-readable labels and renders details as plain text or links, plus optional advisory links or titles.
  • Update VulnerabilityRow to insert RemediationDetails when remediations are present, after trustedContent and fixedIn but before the empty state, preserving the existing priority chain.
  • Wire the new component into the main UI bundle via the generated main.js template (no behavioral changes shown in diff).
ui/src/components/RemediationDetails.tsx
ui/src/components/VulnerabilityRow.tsx
src/main/resources/freemarker/templates/generated/main.js
Add an integration test that verifies remediation details and advisory information are rendered correctly for a specific CVE in the HTML report.
  • Add testHtmlRemediationDetails to HtmlReportTest, exercising the /api/v5/analysis HTML flow with a CycloneDX SBOM and navigating to transitive vulnerabilities.
  • Locate the CVE-2022-42003 row and assert the remediation cell shows the workaround category label, a remediation details link with correct href and text, and the advisory title in the cell text.
src/test/java/io/github/guacsec/trustifyda/integration/HtmlReportTest.java

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

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The VulnerabilityRow render logic is getting hard to follow with the nested ternary chain; consider extracting the remediation selection into a small helper function or child component to make the priority order (trustedContent > fixedIn > remediations) clearer and easier to maintain.
  • In RemediationDetails, using the array index as the key can lead to unnecessary re-renders or subtle bugs if the data changes order; if possible, derive a more stable key (e.g., from advisory id + category + url) to improve React reconciliation.
  • The hasRemediations function and the VulnerabilityRow both independently check for remediation.remediations?.length > 0; consider centralizing that logic in hasRemediations and relying on it in the component to avoid duplication and keep the remediation presence rules in one place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `VulnerabilityRow` render logic is getting hard to follow with the nested ternary chain; consider extracting the remediation selection into a small helper function or child component to make the priority order (trustedContent > fixedIn > remediations) clearer and easier to maintain.
- In `RemediationDetails`, using the array index as the `key` can lead to unnecessary re-renders or subtle bugs if the data changes order; if possible, derive a more stable key (e.g., from advisory id + category + url) to improve React reconciliation.
- The `hasRemediations` function and the `VulnerabilityRow` both independently check for `remediation.remediations?.length > 0`; consider centralizing that logic in `hasRemediations` and relying on it in the component to avoid duplication and keep the remediation presence rules in one place.

## Individual Comments

### Comment 1
<location path="ui/src/components/RemediationDetails.tsx" line_range="17-26" />
<code_context>
+export const RemediationDetails: React.FC<RemediationDetailsProps> = ({ remediations }) => {
+  return (
+    <>
+      {remediations.map((rem, index) => {
+        const label = rem.category
+          ? categoryLabels[rem.category] ?? rem.category
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Use a more stable key than the array index for mapped remediations

Using the array index as a React key can cause subtle UI bugs when the list is reordered or items are added/removed. Prefer a stable identifier (e.g., combining `rem.advisory?.id`, `rem.category`, or `rem.url`) so React can correctly track item identity.

```suggestion
export const RemediationDetails: React.FC<RemediationDetailsProps> = ({ remediations }) => {
  return (
    <>
      {remediations.map((rem, index) => {
        const label = rem.category
          ? categoryLabels[rem.category] ?? rem.category
          : undefined;

        const remediationKeyParts = [
          rem.advisory?.id,
          rem.category,
          rem.url,
        ].filter(Boolean) as string[];

        const remediationKey =
          remediationKeyParts.length > 0
            ? remediationKeyParts.join('|')
            : `remediation-${index}`;

        return (
          <div key={remediationKey}>
```
</issue_to_address>

### Comment 2
<location path="ui/src/components/RemediationDetails.tsx" line_range="32-41" />
<code_context>
+              <span>
+                {label ? ': ' : ''}
+                {rem.url ? (
+                  <a href={rem.url} target="_blank" rel="noreferrer">
+                    {rem.details}
+                  </a>
+                ) : (
+                  rem.details
+                )}
+              </span>
+            )}
+            {!rem.details && rem.url && (
+              <span>
+                {label ? ': ' : ''}
+                <a href={rem.url} target="_blank" rel="noreferrer">
+                  Details
+                </a>
+              </span>
+            )}
+            {rem.advisory && (
+              <div>
+                {rem.advisory.url ? (
+                  <a href={rem.advisory.url} target="_blank" rel="noreferrer">
+                    {rem.advisory.title || rem.advisory.id}
+                  </a>
</code_context>
<issue_to_address>
**🚨 issue (security):** Include `noopener` in `rel` for `target="_blank"` links to avoid opener access

These `target="_blank"` anchors only use `rel="noreferrer"`. Without `noopener`, the new page can access `window.opener`. Please add `noopener` (e.g., `rel="noreferrer noopener"`) to these links.
</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 +17 to +26
export const RemediationDetails: React.FC<RemediationDetailsProps> = ({ remediations }) => {
return (
<>
{remediations.map((rem, index) => {
const label = rem.category
? categoryLabels[rem.category] ?? rem.category
: undefined;

return (
<div key={index}>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (bug_risk): Use a more stable key than the array index for mapped remediations

Using the array index as a React key can cause subtle UI bugs when the list is reordered or items are added/removed. Prefer a stable identifier (e.g., combining rem.advisory?.id, rem.category, or rem.url) so React can correctly track item identity.

Suggested change
export const RemediationDetails: React.FC<RemediationDetailsProps> = ({ remediations }) => {
return (
<>
{remediations.map((rem, index) => {
const label = rem.category
? categoryLabels[rem.category] ?? rem.category
: undefined;
return (
<div key={index}>
export const RemediationDetails: React.FC<RemediationDetailsProps> = ({ remediations }) => {
return (
<>
{remediations.map((rem, index) => {
const label = rem.category
? categoryLabels[rem.category] ?? rem.category
: undefined;
const remediationKeyParts = [
rem.advisory?.id,
rem.category,
rem.url,
].filter(Boolean) as string[];
const remediationKey =
remediationKeyParts.length > 0
? remediationKeyParts.join('|')
: `remediation-${index}`;
return (
<div key={remediationKey}>

Comment on lines +32 to +41
<a href={rem.url} target="_blank" rel="noreferrer">
{rem.details}
</a>
) : (
rem.details
)}
</span>
)}
{!rem.details && rem.url && (
<span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚨 issue (security): Include noopener in rel for target="_blank" links to avoid opener access

These target="_blank" anchors only use rel="noreferrer". Without noopener, the new page can access window.opener. Please add noopener (e.g., rel="noreferrer noopener") to these links.

@codecov-commenter

codecov-commenter commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 85.71429% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 57.23%. Comparing base (0d846da) to head (e80cb56).

Files with missing lines Patch % Lines
.../trustifyda/integration/report/ReportTemplate.java 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #643      +/-   ##
============================================
+ Coverage     57.19%   57.23%   +0.03%     
  Complexity      878      878              
============================================
  Files            92       92              
  Lines          5021     5027       +6     
  Branches        691      692       +1     
============================================
+ Hits           2872     2877       +5     
  Misses         1841     1841              
- Partials        308      309       +1     
Flag Coverage Δ
integration-tests 57.23% <85.71%> (+0.03%) ⬆️

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

Files with missing lines Coverage Δ
.../trustifyda/integration/report/ReportTemplate.java 86.66% <85.71%> (-0.29%) ⬇️
🚀 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.

ruromero and others added 3 commits July 10, 2026 16:59
…overs (TC-4523)

Replace the mutually exclusive if/else cascade in VulnerabilityRow with
independent conditional renders so trustedContent, fixedIn, and remediations
can all appear in the same cell. Show actual fixedIn version strings instead
of a redundant CVE-to-deps.dev link. Render remediations as PatternFly
Popover badges with clickable category labels expanding to full details.
Replace provider/unknown with provider/Other. Remove dead remediationLink
utility function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… rewriting (TC-4523)

- Fix getSourceName() to replace literal "unknown" strings from the backend with "Other"
- Add advisoryLink() utility to rewrite advisory URLs using branding config template
- Add advisoryIssueTemplate to BrandingConfig interface and backend config mapping
- Skip redundant Popover when remediation details duplicate vulnerability title
- Improve remediation vertical spacing with div wrappers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The advisoryLink() rewriter was only applied to advisory.url but not to
the remediation URL itself. This caused broken links like
https://www.redhat.com/#CVE-2023-2454 to remain unrewritten even when
branding.advisory-issue-template was configured.

Co-Authored-By: Claude Opus 4.6 <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

None yet

Development

Successfully merging this pull request may close these issues.

2 participants