Skip to content

feat(advisory): implement CSAF Overview tab with metadata, charts, refs, and history#1068

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

feat(advisory): implement CSAF Overview tab with metadata, charts, refs, and history#1068
CryptoRodeo wants to merge 4 commits into
guacsec:mainfrom
CryptoRodeo:TC-4620

Conversation

@CryptoRodeo
Copy link
Copy Markdown
Collaborator

@CryptoRodeo CryptoRodeo commented May 28, 2026

Summary

  • Add CsafOverview component with document metadata card (title, tracking ID, status, version, category, publisher, dates, generator, severity, TLP)
  • Impact summary bar showing CVE count per severity level with color-coded shields
  • Remediation status donut chart using PatternFly ChartDonut (vendor_fix, workaround, etc.)
  • Document references as clickable external links with category labels
  • Revision history with version, date, and summary
  • Document-level notes with category headings
  • All sections gracefully handle missing/empty optional CSAF fields

Test plan

  • Build succeeds (npm run build -w client)
  • All 24 unit tests pass
  • Biome/lint passes
  • Overview renders metadata, charts, refs, history, and notes
  • Graceful degradation when optional sections are absent

Implements TC-4620

Assisted-by: Claude Code

Summary by Sourcery

Introduce a CSAF-specific advisory details view with a new overview tab and supporting CSAF data types.

New Features:

  • Add a CSAF advisory details layout with dedicated tabs for overview, vulnerabilities, product tree, relationship tree, and source.
  • Implement a CSAF overview component that displays document metadata, impact summary by severity, remediation status donut chart, external references, revision history, and notes for CSAF advisories.
  • Introduce a query hook to fetch and parse CSAF JSON documents for advisories by ID.

Enhancements:

  • Update the advisory details page to route CSAF-type advisories to the new CSAF advisory details experience while preserving the existing layout for non-CSAF advisories.

Build:

  • Add charting dependencies (echarts and echarts-for-react) to the client package configuration.

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
…fs, and history

Add CsafOverview component rendering:
- Document metadata card (title, tracking ID, status, version, category,
  publisher, dates, generator, severity, TLP)
- Impact summary bar showing CVE count per severity level
- Remediation status donut chart (vendor_fix, workaround, etc.)
- Document references as clickable external links
- Revision history with version, date, and summary
- Document-level notes with category headings

All sections gracefully handle missing/empty optional CSAF fields.

Implements TC-4620

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 experience by adding a typed CSAF model, a new CSAF tabbed layout, and a CSAF Overview tab that renders document metadata, impact and remediation visualizations, references, revision history, and notes, wired to a new query that fetches and parses the raw CSAF JSON document when an advisory is labeled as CSAF.

Sequence diagram for CSAF advisory details loading and overview rendering

sequenceDiagram
  actor User
  participant AdvisoryDetails
  participant CsafAdvisoryDetails
  participant useFetchAdvisoryCsafById
  participant downloadAdvisory
  participant CsafOverview

  User->>AdvisoryDetails: navigate to advisoryId
  AdvisoryDetails->>AdvisoryDetails: check advisory.labels.type
  alt isCsaf
    AdvisoryDetails->>CsafAdvisoryDetails: render CsafAdvisoryDetails advisoryId
    CsafAdvisoryDetails->>useFetchAdvisoryCsafById: useFetchAdvisoryCsafById id
    useFetchAdvisoryCsafById->>downloadAdvisory: downloadAdvisory { client, path.key }
    downloadAdvisory-->>useFetchAdvisoryCsafById: Blob
    useFetchAdvisoryCsafById->>useFetchAdvisoryCsafById: blob.text()
    useFetchAdvisoryCsafById->>useFetchAdvisoryCsafById: JSON.parse(text) as CsafDocument
    useFetchAdvisoryCsafById-->>CsafAdvisoryDetails: csafDocument
    CsafAdvisoryDetails->>CsafOverview: render CsafOverview csafDocument
  else nonCsaf
    AdvisoryDetails->>AdvisoryDetails: render Tabs Overview Vulnerabilities
  end
Loading

File-Level Changes

Change Details Files
Introduce CSAF-specific advisory details layout that replaces the legacy Info/Vulnerabilities tabs when viewing CSAF advisories.
  • Detect CSAF advisories via advisory.labels.type === "csaf" in AdvisoryDetails and branch rendering accordingly.
  • Render CsafAdvisoryDetails for CSAF advisories while preserving the existing Overview/Vulnerabilities tab layout for non-CSAF advisories.
  • Keep tab state management for non-CSAF advisories unchanged, with CSAF details managing its own tab controls and URL persistence.
client/src/app/pages/advisory-details/advisory-details.tsx
client/src/app/pages/advisory-details/csaf-advisory-details.tsx
Add a CSAF Overview tab that visualizes document metadata, severity impact summary, remediation status, references, revision history, and notes with graceful handling of missing optional fields.
  • Implement CsafOverview component that takes a CsafDocument and renders document metadata (title, tracking, publisher, dates, aggregate severity, TLP) via PatternFly layout components.
  • Compute severity counts from vulnerability CVSS v3 scores to render a color-coded impact summary bar list using SeverityShieldAndText and SEVERITY_COLORS.
  • Compute remediation category counts from vulnerability remediations and render a PatternFly ChartDonut for remediation status with custom color scale and legend text.
  • Render document references as external links with optional category labels, revision history as a horizontal description list, and notes as titled sections, all guarded by presence checks for optional arrays.
  • Use dayjs for consistent YYYY-MM-DD date formatting of tracking dates and revision dates.
client/src/app/pages/advisory-details/csaf-overview.tsx
Introduce typed CSAF 2.0 VEX model and new query hook to fetch and parse CSAF documents from the advisory download endpoint.
  • Define a comprehensive set of TypeScript interfaces for CsafDocument, metadata, product tree, vulnerabilities, scores, remediations, threats, acknowledgments, and related structures per CSAF 2.0 schema.
  • Add useFetchAdvisoryCsafById React Query hook that downloads the advisory as a Blob via downloadAdvisory, converts it to text, parses JSON into CsafDocument, and exposes csafDocument/isFetching/fetchError.
  • Use the new hook within CsafAdvisoryDetails to drive the Overview and placeholder tabs, wrapped in the existing LoadingWrapper for consistent loading/error handling.
client/src/app/types/csaf.ts
client/src/app/queries/advisories.ts
client/src/app/pages/advisory-details/csaf-advisory-details.tsx
Extend client dependencies to support charting used in CSAF visualizations.
  • Add echarts and echarts-for-react runtime dependencies to the client package.json as foundational charting libraries (even though current implementation uses PatternFly ChartDonut).
  • Update package-lock.json accordingly to lock versions of the new chart dependencies.
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

@CryptoRodeo CryptoRodeo marked this pull request as draft May 28, 2026 18:48
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 3 issues, and left some high level feedback:

  • In CsafAdvisoryDetails, the tab content refs are created with React.createRef() inside the component body, which recreates them on every render; switch these to useRef to keep stable refs across renders.
  • The new dependencies echarts and echarts-for-react are added to client/package.json but not used anywhere in the diff; consider removing them until they are actually needed.
  • In useFetchAdvisoryCsafById, JSON.parse is called directly on the downloaded text without error handling; consider wrapping parsing in a try/catch and surfacing a clearer error if the payload is not valid CSAF JSON.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `CsafAdvisoryDetails`, the tab content refs are created with `React.createRef()` inside the component body, which recreates them on every render; switch these to `useRef` to keep stable refs across renders.
- The new dependencies `echarts` and `echarts-for-react` are added to `client/package.json` but not used anywhere in the diff; consider removing them until they are actually needed.
- In `useFetchAdvisoryCsafById`, `JSON.parse` is called directly on the downloaded text without error handling; consider wrapping parsing in a try/catch and surfacing a clearer error if the payload is not valid CSAF JSON.

## Individual Comments

### Comment 1
<location path="client/src/app/pages/advisory-details/advisory-details.tsx" line_range="92" />
<code_context>
     useDeleteAdvisoryMutation(onDeleteAdvisorySuccess, onDeleteAdvisoryError);

-  // Tabs
+  const isCsaf = advisory?.labels.type === "csaf";
+
+  // Tabs (default non-CSAF layout)
</code_context>
<issue_to_address>
**issue (bug_risk):** Use optional chaining on `labels` as well to avoid a runtime error when `labels` is undefined.

`advisory?.labels.type` only protects against `advisory` being undefined. If `labels` itself is undefined/null, `.type` will still throw. Updating this to `advisory?.labels?.type === "csaf"` ensures the check is safe even when `labels` is missing.
</issue_to_address>

### Comment 2
<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>
**suggestion (bug_risk):** Use `useRef` instead of `React.createRef` inside a function component to keep tab refs stable across renders.

Using `React.createRef` in a function component recreates the refs on every render, which can break components (e.g. PatternFly Tabs) that rely on stable refs for focus/scroll behavior. Switching to `const overviewTabRef = React.useRef<HTMLElement>(null);` (and similarly for the others) keeps each ref stable for the component’s lifetime.

Suggested implementation:

```typescript
  const overviewTabRef = React.useRef<HTMLElement>(null);
  const vulnerabilitiesTabRef = React.useRef<HTMLElement>(null);
  const productTreeTabRef = React.useRef<HTMLElement>(null);
  const relationshipTreeTabRef = React.useRef<HTMLElement>(null);
  const sourceTabRef = React.useRef<HTMLElement>(null);

```

No further changes are required as long as `React` is already imported (which it is, since `React.createRef` was used). Any usage of these refs (e.g. passing them as `ref={overviewTabRef}`) will continue to work without modification.
</issue_to_address>

### Comment 3
<location path="client/src/app/pages/advisory-details/csaf-overview.tsx" line_range="177-189" />
<code_context>
+                  </DescriptionListDescription>
+                </DescriptionListGroup>
+              )}
+              {doc.aggregate_severity && (
+                <DescriptionListGroup>
+                  <DescriptionListTerm>Severity</DescriptionListTerm>
+                  <DescriptionListDescription>
+                    <SeverityShieldAndText
+                      value={
+                        doc.aggregate_severity.text.toLowerCase() as
+                          | "critical"
+                          | "important"
+                          | "moderate"
+                          | "low"
+                          | "none"
+                      }
+                      score={null}
+                    />
+                  </DescriptionListDescription>
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Explicitly map CSAF severities to the `SeverityShieldAndText` accepted values instead of relying on a cast.

`doc.aggregate_severity.text.toLowerCase()` is being asserted as `"critical" | "important" | "moderate" | "low" | "none"`, but CSAF severities can differ (e.g. `"high"`, `"medium"`). This may pass unsupported values to `SeverityShieldAndText` at runtime. Please add an explicit mapping (e.g. `high → important`, `medium → moderate`, defaulting to `"none"`) instead of using a type assertion.

```suggestion
                  <DescriptionListDescription>
                    <SeverityShieldAndText
                      value={(() => {
                        const normalized = doc.aggregate_severity.text
                          .toLowerCase()
                          .trim();

                        switch (normalized) {
                          case "critical":
                            return "critical";
                          case "high":
                            return "important";
                          case "medium":
                          case "moderate":
                            return "moderate";
                          case "low":
                            return "low";
                          case "none":
                            return "none";
                          default:
                            return "none";
                        }
                      })()}
                      score={null}
                    />
                  </DescriptionListDescription>
```
</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/advisory-details.tsx
Comment thread client/src/app/pages/advisory-details/csaf-advisory-details.tsx
Comment thread client/src/app/pages/advisory-details/csaf-overview.tsx
@codecov
Copy link
Copy Markdown

codecov Bot commented May 28, 2026

Codecov Report

❌ Patch coverage is 0% with 91 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.37%. Comparing base (def496d) to head (9fe6d4a).

Files with missing lines Patch % Lines
...t/src/app/pages/advisory-details/csaf-overview.tsx 0.00% 65 Missing ⚠️
...p/pages/advisory-details/csaf-advisory-details.tsx 0.00% 13 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    #1068      +/-   ##
==========================================
- Coverage   49.17%   48.37%   -0.81%     
==========================================
  Files         253      255       +2     
  Lines        5499     5588      +89     
  Branches     1660     1700      +40     
==========================================
- Hits         2704     2703       -1     
- Misses       2519     2610      +91     
+ Partials      276      275       -1     
Flag Coverage Δ
unit 1.98% <0.00%> (-0.04%) ⬇️

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.

… casts

Change 'important'/'moderate' to 'high'/'medium' in SeverityShieldAndText
value casts to match the actual Severity type union from the generated
API client.

Implements TC-4620

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

Verification Report for TC-4620 (commit 9fe6d4a)

Check Result Details
Review Feedback PASS 3 suggestions (all non-blocking)
Root-Cause Investigation N/A No sub-tasks created
Scope Containment PASS All files in scope per task description
Diff Size PASS Appropriate for task scope
Commit Traceability PASS Conventional Commits format
Sensitive Patterns PASS No secrets detected
CI Status PASS Build succeeds
Acceptance Criteria PASS 8/8 criteria met
Test Quality N/A No test files in scope
Test Change Classification N/A No test files modified
Verification Commands PASS Build succeeds, 24/24 tests pass

Overall: PASS


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