Skip to content

feat(report): improve remediation display with GHSA links and version range popovers (TC-4523) - #643

Merged
ruromero merged 10 commits into
guacsec:mainfrom
ruromero:TC-4523
Jul 22, 2026
Merged

feat(report): improve remediation display with GHSA links and version range popovers (TC-4523)#643
ruromero merged 10 commits into
guacsec:mainfrom
ruromero:TC-4523

Conversation

@ruromero

@ruromero ruromero commented Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • GHSA advisory links: Generate clickable GitHub advisory links from GHSA identifiers in buildAdvisoryInfo()
  • FixedIn version range popovers: Make each fixedIn version clickable, showing affected version ranges in a PatternFly Popover
  • Remediation popover logic: Show remediation.details in popover body instead of advisory title; fall back to plain advisory link when no details are available
  • Advisory link fix: Use standalone advisoryIssueTemplate template param to fix vendor-specific CVE link rewriting

Test plan

  • TrustifyResponseHandlerTest — 34/34 pass (includes GHSA URL generation assertion)
  • HtmlReportTest — advisory link rewriting verified
  • Visual inspection of generated HTML report:
    • FixedIn versions are clickable and show version range popovers
    • GHSA IDs link to github.com/advisories/...
    • Remediation entries with details show popover with details text
    • Remediation entries without details show plain advisory link

Jira: TC-4523

🤖 Generated with Claude Code

@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 thread ui/src/components/RemediationDetails.tsx Outdated
Comment thread ui/src/components/RemediationDetails.tsx Outdated
@codecov-commenter

codecov-commenter commented Jul 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.33333% with 31 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.66%. Comparing base (5a0b9c4) to head (686d039).

Files with missing lines Patch % Lines
...on/providers/trustify/TrustifyResponseHandler.java 75.00% 7 Missing and 21 partials ⚠️
.../trustifyda/integration/report/ReportTemplate.java 81.81% 0 Missing and 2 partials ⚠️
...integration/providers/ProviderResponseHandler.java 0.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff              @@
##               main     #643      +/-   ##
============================================
+ Coverage     57.19%   57.66%   +0.46%     
- Complexity      878      918      +40     
============================================
  Files            92       93       +1     
  Lines          5021     5107      +86     
  Branches        690      721      +31     
============================================
+ Hits           2872     2945      +73     
- Misses         1841     1842       +1     
- Partials        308      320      +12     
Flag Coverage Δ
integration-tests 57.66% <79.33%> (+0.46%) ⬆️

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

Files with missing lines Coverage Δ
...egration/providers/trustify/VersionComparator.java 100.00% <100.00%> (ø)
...integration/providers/ProviderResponseHandler.java 68.63% <0.00%> (+0.21%) ⬆️
.../trustifyda/integration/report/ReportTemplate.java 86.07% <81.81%> (-0.89%) ⬇️
...on/providers/trustify/TrustifyResponseHandler.java 78.96% <75.00%> (-0.58%) ⬇️
🚀 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 ruromero changed the title feat(report): display remediation details and advisory info in HTML report (TC-4523) feat(report): improve remediation display with GHSA links and version range popovers (TC-4523) Jul 15, 2026
@ruromero
ruromero force-pushed the TC-4523 branch 3 times, most recently from db98ae6 to d74d89d Compare July 17, 2026 05:59
@ruromero

Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-4523 (commit 5cfefe0)

Check Result Details
Review Feedback PASS 5 comments classified (3 suggestions, 1 code change request targeting untracked file, 1 suggestion); no actionable code changes required
Root-Cause Investigation N/A No sub-tasks created
Scope Containment FAIL report.ftl listed in task spec but not modified in PR
Diff Size WARN 8,573 total lines but ~87% is test fixture JSON; effective code diff ~1,105 lines
Commit Traceability WARN 1 of 3 commits (5cfefe0) missing TC-4523 reference
Sensitive Patterns PASS No secrets or credentials in 4,786 added lines
CI Status PASS Integration Tests pass, commitlint pass
Acceptance Criteria PASS All 6 acceptance criteria satisfied; all 4 test requirements covered
Test Quality WARN 2 new test methods lack // doc comments per CONVENTIONS.md; Eval Quality: N/A
Test Change Classification ADDITIVE New test file + new test method; no reductive signals
Verification Commands PASS All 4 commands pass (lint, build, spotless, verify — 342 tests)

Overall: FAIL

Scope Containment FAIL: The task specification lists report.ftl in Files to Modify, but this file was not changed in the PR. The implementation migrated to a React-based approach where UI changes compile into main.js/vendor.js bundles instead. If the task spec is outdated, update it to reflect the actual implementation approach.

Commit Traceability WARN: Commit 5cfefe02 ("fix(test): update batch_report.json fixture...") is missing the TC-4523 task reference.

Test Documentation WARN: testHtmlRemediationDetails() in HtmlReportTest.java and testResponseToIssuesGhsaDocumentIdFallback() in TrustifyResponseHandlerTest.java lack // documentation comments per CONVENTIONS.md.


This comment was AI-generated by sdlc-workflow/verify-pr v0.13.2.

@ruromero

Copy link
Copy Markdown
Collaborator Author

[sdlc-workflow/verify-pr] Re: @sourcery-ai[bot] review (ID: 4670658967)

Classified as:

  1. suggestion — Extract VulnerabilityRow render logic into helper/child component
  2. suggestion — Use stable React key instead of array index
  3. suggestion — Centralize hasRemediations logic

@ruromero
ruromero requested a review from a-oren July 17, 2026 14:00
@ruromero

Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-4523 (commit 1d6b413)

Check Result Details
Review Feedback PASS 5 comments classified (3 suggestions, 1 code change request, 1 nit); code change request addressed by fix commit 1d6b413
Root-Cause Investigation N/A No sub-tasks created — nothing to investigate
Scope Containment WARN All task-required files present; 16 out-of-scope files (backend handlers, tests, fixtures, generated assets, dependency bump) — functionally necessary for the feature
Diff Size WARN 23 files, +4904/-3792 lines — 85.6% is test fixture JSON; actual code delta ~+755/-257 lines
Commit Traceability WARN 3/4 commits reference TC-4523; commit 5cfefe0 (test fixture fix) omits the tag
Sensitive Patterns PASS No secrets detected; hex strings in fixtures are SHA-256 hashes
CI Status PASS All CI checks pass (build, test, spotless, yarn lint, yarn build)
Acceptance Criteria PASS 6/6 acceptance criteria verified; 4/4 test requirements met
Test Quality WARN Repetitive: PASS; Test Documentation: WARN (one test uses /** */ Javadoc violating CONVENTIONS.md line 253; most new tests lack // descriptions); Eval Quality: N/A
Test Change Classification ADDITIVE +9 test functions, ~+76 assertions, zero removals
Verification Commands PASS yarn lint (clean), yarn build (clean), mvn spotless:apply (0 changes), mvn verify (342 tests, 0 failures, BUILD SUCCESS)

Overall: WARN

Improved from prior run's FAIL. All WARNs are informational — no blocking issues remain. Scope, diff size, and commit traceability warnings reflect the feature's natural complexity (backend handler refactoring + test fixtures + React UI). Test documentation warning is minor (comment style preference).


This comment was AI-generated by sdlc-workflow/verify-pr v0.13.2.

Comment thread ui/src/components/RemediationDetails.tsx Outdated
Comment thread ui/src/components/VulnerabilityLink.tsx Outdated
Comment thread ui/src/api/report.ts Outdated
Comment thread ui/src/components/AdvisoryRemediations.tsx Outdated

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

LGTM

ruromero and others added 8 commits July 22, 2026 14:51
…SA links (TC-4523)

Restructure the remediation column to display advisory-grouped data:
- Group remediations by advisory with version range context
- Derive fixedIn versions from version ranges (highVersion where !highInclusive)
- Add GHSA advisory link generation in TrustifyResponseHandler
- Add VersionComparator for semantic version sorting
- Replace RemediationDetails with AdvisoryRemediations component
- Fix duplicate CVE rows via mergeRemediation dedup in buildVulnerabilityItems
- Rewrite advisory link URLs through branding config
- Bump trustify-da-api dependency to 2.0.12-SNAPSHOT

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

The batch report fixture was not updated when TrustifyResponseHandler was
restructured, causing testBatchSBOMAllWithToken to fail for both CYCLONEDX
and SPDX formats.

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

- Add rel="noreferrer noopener" to all target="_blank" links in
  RemediationDetails for reverse-tabnabbing protection
- Replace array-index keys with composite keys derived from
  advisory id, category, and url for stable React reconciliation
- Update hasRemediations() to also check non-empty remediations[]
  within advisories, and use it as a guard in VulnerabilityRow
  instead of duplicating the inline check

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

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

- Remove unused RemediationDetails.tsx component
- Remove stale advisory field from RemediationInfo interface
- Extract compareVersions and formatRange to shared utils/version.ts
- Deduplicate advisories by ID in mergeRemediation
- Replace array index key with composite key in AdvisoryRemediations

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Read advisory.labels["importer"] instead of advisory.issuer.name to
display the actual data source (e.g. "redhat-csaf", "github-osv").
Fall back to issuer name, then to "manual" for manually uploaded
advisories. The UI maps "manual" and "unknown" to "Other".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The hardened-images recommendations section was accidentally re-added
by db29deb after being correctly removed in 133f8be. Since the
hardened image provider is disabled in tests, the actual output has
empty recommendations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ruromero and others added 2 commits July 22, 2026 16:51
Group FixedInEntry by version across all advisories so that each
version appears once with all advisory details in its popover,
instead of showing duplicate entries when multiple advisories share
a fixed-in version.

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

The UI now displays advisory IDs in the popover instead of titles,
so the HtmlReportTest assertion needs to match GHSA-jjjh-jjxp-wpff
rather than the full advisory title text.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@ruromero
ruromero merged commit 97fcaff into guacsec:main Jul 22, 2026
3 checks passed
@ruromero
ruromero deleted the TC-4523 branch July 23, 2026 10:02
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