feat(advisory): implement CSAF Relationship Tree tab with ECharts visualization#1067
feat(advisory): implement CSAF Relationship Tree tab with ECharts visualization#1067CryptoRodeo wants to merge 3 commits into
Conversation
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
…ualization Add interactive ECharts tree rendering of CSAF product relationships grouped by relates_to_product_reference. Features color-coded nodes by relationship type (default_component_of, external_component_of, installed_on, installed_with, optional_component_of) with legend. Product IDs are resolved to display names via full_product_names. Empty state shown when no relationships exist. Implements TC-4623 Assisted-by: Claude Code
Reviewer's GuideImplements a CSAF-specific advisory details view with a new 5-tab CSAF layout, including an ECharts-based relationship tree visualization that groups CSAF product relationships by parent product and colors nodes by relationship category, along with CSAF type definitions and a hook to fetch and parse CSAF JSON documents. Sequence diagram for CSAF advisory relationship tree renderingsequenceDiagram
actor User
participant AdvisoryDetails
participant CsafAdvisoryDetails
participant useFetchAdvisoryCsafById
participant downloadAdvisory
participant CsafRelationshipTree
participant transformRelationshipsToTreeData
participant ReactECharts
User->>AdvisoryDetails: navigate to AdvisoryDetails
AdvisoryDetails->>AdvisoryDetails: evaluate isCsaf
AdvisoryDetails-->>CsafAdvisoryDetails: render when isCsaf
CsafAdvisoryDetails->>useFetchAdvisoryCsafById: useFetchAdvisoryCsafById(advisoryId)
useFetchAdvisoryCsafById->>downloadAdvisory: downloadAdvisory({ client, path })
downloadAdvisory-->>useFetchAdvisoryCsafById: Blob
useFetchAdvisoryCsafById->>useFetchAdvisoryCsafById: blob.text() and JSON.parse
useFetchAdvisoryCsafById-->>CsafAdvisoryDetails: csafDocument
CsafAdvisoryDetails-->>CsafRelationshipTree: render CsafRelationshipTree(csafDocument)
CsafRelationshipTree->>transformRelationshipsToTreeData: transformRelationshipsToTreeData(relationships, full_product_names)
transformRelationshipsToTreeData-->>CsafRelationshipTree: EChartsTreeNode treeData
CsafRelationshipTree-->>ReactECharts: option with treeData
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In
CsafAdvisoryDetails, the tab refs are created withReact.createRef()inside the component body, which re-creates them on every render; consider switching touseRefso the refs are stable across renders. - In
useFetchAdvisoryCsafById,JSON.parseof the blob text is unchecked; wrapping this in a try/catch (and surfacing a more explicit parse error) would make failures due to malformed CSAF documents easier to diagnose.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `CsafAdvisoryDetails`, the tab refs are created with `React.createRef()` inside the component body, which re-creates them on every render; consider switching to `useRef` so the refs are stable across renders.
- In `useFetchAdvisoryCsafById`, `JSON.parse` of the blob text is unchecked; wrapping this in a try/catch (and surfacing a more explicit parse error) would make failures due to malformed CSAF documents easier to diagnose.
## 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):** Switch tab refs from `createRef` to `useRef` to avoid recreating refs on every render.
Because this is a function component, `React.createRef` will allocate new ref objects on every render, which can break any logic relying on stable ref identity (e.g., `tabContentRef`, focus/scroll handlers). Prefer `useRef` so the same ref instance is preserved across renders:
```ts
const overviewTabRef = React.useRef<HTMLElement>(null);
const vulnerabilitiesTabRef = React.useRef<HTMLElement>(null);
// ...etc.
```
</issue_to_address>
### Comment 2
<location path="client/src/app/pages/advisory-details/csaf-relationship-tree.tsx" line_range="91-100" />
<code_context>
+ tooltip: {
+ trigger: "item" as const,
+ triggerOn: "mousemove" as const,
+ formatter: (params: { data?: { name?: string; value?: string } }) => {
+ const category = params.data?.value;
+ return category
+ ? `${params.data?.name}<br/><em>${category.replace(/_/g, " ")}</em>`
+ : params.data?.name || "";
+ },
+ },
</code_context>
<issue_to_address>
**suggestion:** Differentiate tooltip content for parent vs child nodes to avoid treating product IDs as relationship categories.
The tooltip formatter currently assumes `data.value` is always a relationship category and applies `replace(/_/g, " ")`. For parent nodes, `value` holds the product ID, so it’s formatted as if it were a category. Consider either storing the relationship category in a separate field (e.g. `data.category`) or only rendering the italicized category line when `data.value` matches a known relationship category (e.g. keys of `RELATIONSHIP_CATEGORY_COLORS`). This avoids misformatting product IDs in parent tooltips.
```suggestion
tooltip: {
trigger: "item" as const,
triggerOn: "mousemove" as const,
formatter: (params: { data?: { name?: string; value?: string } }) => {
const name = params.data?.name ?? "";
const value = params.data?.value;
// Only treat `value` as a relationship category when it matches a known category key.
const knownCategories = Object.keys(RELATIONSHIP_CATEGORY_COLORS ?? {});
const isRelationshipCategory =
typeof value === "string" && knownCategories.includes(value);
if (isRelationshipCategory) {
return `${name}<br/><em>${value.replace(/_/g, " ")}</em>`;
}
// For parent nodes (where `value` is a product ID), just show the product name.
return name;
},
},
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| tooltip: { | ||
| trigger: "item" as const, | ||
| triggerOn: "mousemove" as const, | ||
| formatter: (params: { data?: { name?: string; value?: string } }) => { | ||
| const category = params.data?.value; | ||
| return category | ||
| ? `${params.data?.name}<br/><em>${category.replace(/_/g, " ")}</em>` | ||
| : params.data?.name || ""; | ||
| }, | ||
| }, |
There was a problem hiding this comment.
suggestion: Differentiate tooltip content for parent vs child nodes to avoid treating product IDs as relationship categories.
The tooltip formatter currently assumes data.value is always a relationship category and applies replace(/_/g, " "). For parent nodes, value holds the product ID, so it’s formatted as if it were a category. Consider either storing the relationship category in a separate field (e.g. data.category) or only rendering the italicized category line when data.value matches a known relationship category (e.g. keys of RELATIONSHIP_CATEGORY_COLORS). This avoids misformatting product IDs in parent tooltips.
| tooltip: { | |
| trigger: "item" as const, | |
| triggerOn: "mousemove" as const, | |
| formatter: (params: { data?: { name?: string; value?: string } }) => { | |
| const category = params.data?.value; | |
| return category | |
| ? `${params.data?.name}<br/><em>${category.replace(/_/g, " ")}</em>` | |
| : params.data?.name || ""; | |
| }, | |
| }, | |
| tooltip: { | |
| trigger: "item" as const, | |
| triggerOn: "mousemove" as const, | |
| formatter: (params: { data?: { name?: string; value?: string } }) => { | |
| const name = params.data?.name ?? ""; | |
| const value = params.data?.value; | |
| // Only treat `value` as a relationship category when it matches a known category key. | |
| const knownCategories = Object.keys(RELATIONSHIP_CATEGORY_COLORS ?? {}); | |
| const isRelationshipCategory = | |
| typeof value === "string" && knownCategories.includes(value); | |
| if (isRelationshipCategory) { | |
| return `${name}<br/><em>${value.replace(/_/g, " ")}</em>`; | |
| } | |
| // For parent nodes (where `value` is a product ID), just show the product name. | |
| return name; | |
| }, | |
| }, |
There was a problem hiding this comment.
[sdlc-workflow/verify-pr] Classified as suggestion — tooltip improvement — valid enhancement but not a bug. No sub-task created.
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1067 +/- ##
==========================================
- Coverage 49.17% 48.52% -0.65%
==========================================
Files 253 256 +3
Lines 5499 5570 +71
Branches 1660 1673 +13
==========================================
- Hits 2704 2703 -1
- Misses 2519 2592 +73
+ Partials 276 275 -1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Verification Report for TC-4623 (commit edfdecb)
Overall: PASSThis comment was AI-generated by sdlc-workflow/verify-pr v0.9.1. |
Summary
relates_to_product_referencefull_product_namescsaf-relationship-helpers.tsreusesEChartsTreeNodetype from tree helpersTest plan
npm run build -w client)Implements TC-4623
Assisted-by: Claude Code
Summary by Sourcery
Add CSAF-specific advisory details view with a new relationship tree visualization and supporting CSAF data plumbing.
New Features:
Enhancements:
Build: