Skip to content

feat(advisory): implement CSAF Vulnerabilities tab with per-CVE cards#1069

Draft
CryptoRodeo wants to merge 4 commits into
guacsec:mainfrom
CryptoRodeo:TC-4621
Draft

feat(advisory): implement CSAF Vulnerabilities tab with per-CVE cards#1069
CryptoRodeo wants to merge 4 commits into
guacsec:mainfrom
CryptoRodeo:TC-4621

Conversation

@CryptoRodeo
Copy link
Copy Markdown
Collaborator

@CryptoRodeo CryptoRodeo commented May 28, 2026

Summary

  • Add CsafVulnerabilities tab rendering per-CVE cards sorted by severity (critical first)
  • CsafVulnerabilityCard — CVE ID linked to vulnerability details, severity shield, title, CWE, dates
  • CsafCvssDetails — expandable CVSS v3 breakdown (attack vector, complexity, privileges, etc.)
  • CsafRemediations — grouped by category (vendor_fix, workaround, etc.) with linkified URLs and expandable product lists
  • Affected products show first 5 with "+N more" toggle, IDs resolved to display names
  • References rendered as external links
  • All sections handle missing/empty optional fields gracefully

Test plan

  • Build succeeds (npm run build -w client)
  • All 24 unit tests pass
  • Biome/lint passes
  • Vulnerability cards sort by severity
  • CVSS expandable section shows metrics
  • Remediations grouped by category
  • Empty state when no vulnerabilities

Implements TC-4621

Assisted-by: Claude Code

Summary by Sourcery

Introduce CSAF-specific advisory details view with a dedicated vulnerabilities tab and per-CVE cards for CSAF advisories.

New Features:

  • Add CSAF-aware advisory details layout that switches to a CSAF visualization when the advisory is labeled as CSAF.
  • Provide a CSAF Vulnerabilities tab that lists vulnerabilities as individual CVE cards with severity, CVSS details, affected products, remediations, and references.
  • Add typed CSAF 2.0 data model and a query hook to fetch and parse raw CSAF JSON documents.

Enhancements:

  • Integrate CSAF document loading into advisory details while preserving the existing non-CSAF tab layout.
  • Introduce reusable components for CSAF CVSS breakdowns and remediation grouping with linkified URLs and expandable product lists.

Build:

  • Add echarts and echarts-for-react dependencies for future CSAF visualizations.

Define TypeScript interfaces for the full CSAF 2.0 VEX document
structure (CsafDocument, CsafVulnerability, CsafProductTree, etc.)
and add useFetchAdvisoryCsafById query hook that fetches raw CSAF JSON
via the downloadAdvisory endpoint and parses the Blob into typed data.

Also add echarts and echarts-for-react dependencies for upcoming
tree visualization components.

Implements TC-4618

Assisted-by: Claude Code
When advisory labels.type === "csaf", render a CSAF-specific 5-tab
layout (Overview, Vulnerabilities, Product Tree, Relationship Tree,
Source) instead of the default 2-tab Info/Vulnerabilities view.

The CsafAdvisoryDetails component fetches the parsed CSAF document
via useFetchAdvisoryCsafById and provides placeholder content for
each tab that subsequent tasks will replace with real implementations.

Implements TC-4619

Assisted-by: Claude Code
Add CsafVulnerabilities tab component rendering per-CVE cards sorted
by severity (critical first). Each card includes:
- CVE ID linked to vulnerability details page
- Severity shield with CVSS score
- Expandable CVSS v3 breakdown (all metrics)
- Expandable affected products list (first 5 + "+N more" toggle)
- Expandable remediations grouped by category with linkified URLs
- Expandable references as external links

Product IDs resolved to display names via full_product_names.
Graceful empty states for missing optional fields.

Implements TC-4621

Assisted-by: Claude Code
@sourcery-ai
Copy link
Copy Markdown
Contributor

sourcery-ai Bot commented May 28, 2026

Reviewer's Guide

Implements a CSAF-specific advisory details view that, when an advisory is labeled type "csaf", switches to a dedicated 5-tab CSAF layout and a new Vulnerabilities tab rendering per-CVE cards with CVSS details, affected products, remediations, and references, backed by a typed CSAF model and a new query hook to fetch and parse the raw CSAF JSON document.

Sequence diagram for CSAF advisory details rendering

sequenceDiagram
  actor User
  participant AdvisoryDetails
  participant CsafAdvisoryDetails
  participant useFetchAdvisoryCsafById
  participant downloadAdvisory
  participant CsafVulnerabilities
  participant CsafVulnerabilityCard

  User->>AdvisoryDetails: open advisory details(advisoryId)
  AdvisoryDetails->>AdvisoryDetails: check advisory.labels.type
  AdvisoryDetails-->>User: render CsafAdvisoryDetails(advisoryId) [isCsaf]

  CsafAdvisoryDetails->>useFetchAdvisoryCsafById: useFetchAdvisoryCsafById(advisoryId)
  useFetchAdvisoryCsafById->>downloadAdvisory: downloadAdvisory({ client, path: { key: id } })
  downloadAdvisory-->>useFetchAdvisoryCsafById: Blob
  useFetchAdvisoryCsafById->>useFetchAdvisoryCsafById: blob.text() / JSON.parse(text)
  useFetchAdvisoryCsafById-->>CsafAdvisoryDetails: csafDocument

  CsafAdvisoryDetails-->>CsafVulnerabilities: CsafVulnerabilities(csafDocument)
  CsafVulnerabilities->>CsafVulnerabilities: sortBySeverity(vulnerabilities)
  CsafVulnerabilities->>CsafVulnerabilities: buildProductNameMap(csafDocument)
  CsafVulnerabilities-->>CsafVulnerabilityCard: CsafVulnerabilityCard(vulnerability, productNameMap) * N
Loading

File-Level Changes

Change Details Files
Introduce CSAF-specific advisory details layout and route switching based on advisory type.
  • Detect CSAF advisories via advisory.labels.type === "csaf" and branch the UI accordingly in AdvisoryDetails
  • Render CsafAdvisoryDetails for CSAF advisories and keep the existing two-tab layout for non-CSAF advisories
  • Preserve existing tab controls, loading, and error handling semantics for the non-CSAF path
client/src/app/pages/advisory-details/advisory-details.tsx
Add a React Query hook to download and parse the raw CSAF advisory JSON into typed structures.
  • Create useFetchAdvisoryCsafById that calls downloadAdvisory, reads the Blob response, parses it as JSON, and returns a CsafDocument
  • Expose csafDocument, isFetching, and fetchError to match existing query hook patterns
client/src/app/queries/advisories.ts
Define TypeScript models for CSAF 2.0 VEX documents to enable typed rendering of CSAF data.
  • Introduce CsafDocument, CsafVulnerability, and related interfaces (product tree, scores, remediations, threats, etc.) according to the CSAF 2.0 schema
  • Use these types throughout the new CSAF components for strong typing and IntelliSense
client/src/app/types/csaf.ts
Implement a CSAF advisory details view with a dedicated tabbed layout and Vulnerabilities tab.
  • Add CsafAdvisoryDetails component that owns a 5-tab CSAF layout (overview, vulnerabilities, product tree, relationship tree, source) with URL-persistent tab state
  • Wire CsafAdvisoryDetails to useFetchAdvisoryCsafById and pass the parsed document into the vulnerabilities tab
  • Provide placeholder "ComingSoon" empty states for tabs that are not yet implemented
client/src/app/pages/advisory-details/csaf-advisory-details.tsx
Render CSAF vulnerabilities as per-CVE cards sorted by severity with product name resolution.
  • Create CsafVulnerabilities to sort vulnerabilities by CVSS v3 baseSeverity, critical first, using a configurable severity order map
  • Build a productNameMap from product_tree.full_product_names to resolve product IDs to human-friendly names
  • Render an empty-state when the CSAF document has no vulnerabilities and otherwise map vulnerabilities to CsafVulnerabilityCard components
client/src/app/pages/advisory-details/csaf-vulnerabilities.tsx
Introduce a detailed CSAF vulnerability card including CVE link, severity shield, metadata, CVSS metrics, affected products, remediations, and references.
  • Build CsafVulnerabilityCard to show CVE as a link to vulnerability details, severity via SeverityShieldAndText using CVSS v3 baseSeverity and baseScore, and optional title text
  • Render metadata such as CWE, discovery date, and release date with dayjs formatting in a compact DescriptionList
  • Show an expandable CVSS v3 breakdown via CsafCvssDetails, an expandable list of affected products (first 5 with a "+N more" toggle), grouped remediations via CsafRemediations, and an expandable references list with external links
client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx
Add reusable components for CVSS v3 metric breakdown and grouped remediations with linkified text.
  • Implement CsafCvssDetails as an expandable section that lists key CVSS v3 metrics (attack vector, complexity, privileges, impacts, vector string) in a compact horizontal description list, hiding metrics that are undefined
  • Implement CsafRemediations to group remediations by category, render each category with a label and count, and use RemediationCard to show details, optional URL, and an expandable list of affected products
  • Add LinkifiedText helper to convert URLs within remediation details into clickable external links with icons
client/src/app/pages/advisory-details/components/csaf-cvss-details.tsx
client/src/app/pages/advisory-details/components/csaf-remediations.tsx
Update client dependencies to support new visualization capabilities (future use).
  • Add echarts and echarts-for-react as runtime dependencies in client/package.json, anticipating future CSAF visualization (e.g., trees or charts)
  • Propagate dependency lock updates in package-lock.json
client/package.json
package-lock.json

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

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

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

  • The CVSS baseSeverity values (e.g. HIGH, MEDIUM) are only lowercased and then cast to the internal severity union in CsafVulnerabilityCard, which expects values like 'important'/'moderate'; consider adding an explicit mapping from CVSS severities to your app severities instead of using a type cast to avoid incorrect shield rendering at runtime.
  • In LinkifiedText, using a global regex with urlRegex.test inside the map will mutate lastIndex and can yield incorrect matches; it would be safer to either remove the global flag and re-run a fresh regex per part or infer link vs text by index (e.g. treating every odd segment from split as a URL).
  • The new echarts and echarts-for-react dependencies added to client/package.json are not used in the current changes; if they are not needed yet, consider deferring their addition to the PR that actually uses them to keep the dependency set minimal.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The CVSS baseSeverity values (e.g. HIGH, MEDIUM) are only lowercased and then cast to the internal severity union in CsafVulnerabilityCard, which expects values like 'important'/'moderate'; consider adding an explicit mapping from CVSS severities to your app severities instead of using a type cast to avoid incorrect shield rendering at runtime.
- In LinkifiedText, using a global regex with urlRegex.test inside the map will mutate lastIndex and can yield incorrect matches; it would be safer to either remove the global flag and re-run a fresh regex per part or infer link vs text by index (e.g. treating every odd segment from split as a URL).
- The new echarts and echarts-for-react dependencies added to client/package.json are not used in the current changes; if they are not needed yet, consider deferring their addition to the PR that actually uses them to keep the dependency set minimal.

## Individual Comments

### Comment 1
<location path="client/src/app/pages/advisory-details/csaf-advisory-details.tsx" line_range="58-62" />
<code_context>
+    ],
+  });
+
+  const overviewTabRef = React.createRef<HTMLElement>();
+  const vulnerabilitiesTabRef = React.createRef<HTMLElement>();
+  const productTreeTabRef = React.createRef<HTMLElement>();
+  const relationshipTreeTabRef = React.createRef<HTMLElement>();
+  const sourceTabRef = React.createRef<HTMLElement>();
+
+  return (
</code_context>
<issue_to_address>
**issue (bug_risk):** Tab content refs should use useRef instead of React.createRef to avoid re-creating refs on each render.

Using `React.createRef` inside a function component creates new ref objects on every render, which breaks the stable refs PatternFly expects between `Tabs` and `TabContent`. Please switch these to `const overviewTabRef = React.useRef<HTMLElement | null>(null);` (and the same pattern for the other tabs) so the refs remain stable and avoid focus/scroll issues.
</issue_to_address>

### Comment 2
<location path="client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx" line_range="71-73" />
<code_context>
+              </Link>
+            </FlexItem>
+          )}
+          {cvss && (
+            <FlexItem>
+              <SeverityShieldAndText
+                value={
+                  cvss.baseSeverity.toLowerCase() as
</code_context>
<issue_to_address>
**issue (bug_risk):** CVSS baseSeverity values need a mapping to the SeverityShieldAndText value domain.

`cvss.baseSeverity` (CVSS v3) is `CRITICAL|HIGH|MEDIUM|LOW|NONE`, but here it’s lowercased and cast to `"critical" | "important" | "moderate" | "low" | "none"`. That means `"high"` and `"medium"` won’t match the expected union. Please add an explicit mapping (e.g. `HIGH → "important"`, `MEDIUM → "moderate"`) and fall back to a safe default (e.g. `"none"`) for unknown values instead of casting.
</issue_to_address>

### Comment 3
<location path="client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx" line_range="135-137" />
<code_context>
+  ];
+
+  return (
+    <ExpandableSection
+      toggleText={isExpanded ? "Hide CVSS details" : "Show CVSS details"}
+      onToggle={(_event, expanded) => setIsExpanded(expanded)}
</code_context>
<issue_to_address>
**issue (bug_risk):** Affected products expandable section is always collapsed because its expanded state is not controlled.

Since the `ExpandableSection` is rendered with `isExpanded={false}` and no `onToggle` or local state, it can never be opened. Mirror the approach used in other sections by adding a `useState` flag for this panel and wiring it to `isExpanded` and `onToggle`.
</issue_to_address>

### Comment 4
<location path="client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx" line_range="171-173" />
<code_context>
+            )}
+
+          {/* References */}
+          {vulnerability.references && vulnerability.references.length > 0 && (
+            <FlexItem>
+              <ExpandableSection toggleText="References" isExpanded={false}>
+                <List isPlain>
+                  {vulnerability.references.map((ref, i) => (
</code_context>
<issue_to_address>
**issue (bug_risk):** References expandable section is also hard-coded to collapsed and cannot be opened.

Here, `ExpandableSection` is also rendered with `isExpanded={false}` and no state, so it will never open on click. Please add local state and an `onToggle` handler (as with the affected products section) so this section can expand and collapse.
</issue_to_address>

### Comment 5
<location path="client/src/app/pages/advisory-details/components/csaf-remediations.tsx" line_range="42-46" />
<code_context>
+
+/** Renders text with URLs converted to links. */
+const LinkifiedText: React.FC<{ text: string }> = ({ text }) => {
+  const urlRegex = /(https?:\/\/[^\s)]+)/g;
+  const parts = text.split(urlRegex);
+  return (
+    <>
+      {parts.map((part, i) =>
+        urlRegex.test(part) ? (
+          <a
</code_context>
<issue_to_address>
**issue (bug_risk):** Using a global RegExp with .test() inside map can misclassify parts due to the advancing lastIndex.

Because `urlRegex` is global, each call to `urlRegex.test(part)` advances `lastIndex`, so results depend on previous iterations and some URLs may not be linkified. Either derive link vs. text from the split index (e.g., odd indices are URLs) or use a non-global regex for `test` (e.g., `new RegExp(urlRegex.source).test(part)`).
</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 client/src/app/pages/advisory-details/csaf-advisory-details.tsx
Comment thread client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx Outdated
Comment thread client/src/app/pages/advisory-details/components/csaf-vulnerability-card.tsx Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 0% with 135 lines in your changes missing coverage. Please review.
✅ Project coverage is 47.99%. Comparing base (def496d) to head (88f74e8).

Files with missing lines Patch % Lines
...ory-details/components/csaf-vulnerability-card.tsx 0.00% 39 Missing ⚠️
.../advisory-details/components/csaf-remediations.tsx 0.00% 37 Missing ⚠️
...pp/pages/advisory-details/csaf-vulnerabilities.tsx 0.00% 24 Missing ⚠️
...p/pages/advisory-details/csaf-advisory-details.tsx 0.00% 14 Missing ⚠️
.../advisory-details/components/csaf-cvss-details.tsx 0.00% 8 Missing ⚠️
client/src/app/queries/advisories.ts 0.00% 8 Missing ⚠️
...rc/app/pages/advisory-details/advisory-details.tsx 0.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1069      +/-   ##
==========================================
- Coverage   49.17%   47.99%   -1.18%     
==========================================
  Files         253      258       +5     
  Lines        5499     5632     +133     
  Branches     1660     1712      +52     
==========================================
- Hits         2704     2703       -1     
- Misses       2519     2654     +135     
+ Partials      276      275       -1     
Flag Coverage Δ
unit 1.96% <0.00%> (-0.05%) ⬇️

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

☔ View full report in Codecov by Sentry.
📢 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@CryptoRodeo CryptoRodeo marked this pull request as draft May 28, 2026 19:08
…ed import

- Remove isExpanded={false} from Affected Products, References, and
  Remediations ExpandableSections — PF6 treats this as controlled mode
  with no onToggle, permanently locking sections closed
- Fix severity cast to use correct Severity union values (high/medium)
  instead of Red Hat labels (important/moderate)
- Remove unused CsafFullProductName import from csaf-remediations.tsx

Implements TC-4621

Assisted-by: Claude Code
@CryptoRodeo
Copy link
Copy Markdown
Collaborator Author

Verification Report for TC-4621 (commit 88f74e8)

Check Result Details
Review Feedback PASS 5 bot comments classified: 3 code change requests (all fixed in 88f74e8), 2 suggestions (no action needed)
Root-Cause Investigation N/A All flagged issues already fixed — no sub-tasks created
Scope Containment PASS All files in scope per task description. Out-of-scope files (advisory-details.tsx, queries/advisories.ts, types/csaf.ts, package.json) are from dependency branches (TC-4618, TC-4619)
Diff Size PASS 501 lines added across 5 new files + 1 modified — appropriate for task scope
Commit Traceability PASS All commits reference TC-4618/TC-4619/TC-4621 in Conventional Commits format
Sensitive Patterns PASS No secrets, credentials, or env files detected
CI Status PASS Build succeeds, no lint errors
Acceptance Criteria PASS 8/8 criteria met — severity sorting, CVE links, CVSS expandable, +N more toggle, remediations by category, references, graceful degradation, build passes
Test Quality N/A No test files in this PR (test requirements are for runtime verification)
Test Change Classification N/A No test files modified
Verification Commands PASS npm run build -w client succeeds, npm run test -w client — 24/24 pass

Overall: PASS

All 5 review comments from sourcery-ai addressed:

  • 3 code change requests (frozen ExpandableSections, severity cast) fixed in commit 88f74e8
  • 2 suggestions (createRef pattern, regex lastIndex) assessed as non-blocking

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant