Skip to content

Commit d9a005a

Browse files
feat: add Vuln Details -> Related SBOMBs -> dependencies list (#298)
1 parent 0b7425f commit d9a005a

File tree

6 files changed

+133
-34
lines changed

6 files changed

+133
-34
lines changed

client/openapi/trustd.yaml

+10-6
Original file line numberDiff line numberDiff line change
@@ -3908,13 +3908,17 @@ components:
39083908
- $ref: '#/components/schemas/SbomHead'
39093909
- type: object
39103910
required:
3911-
- status
3911+
- purl_statuses
39123912
properties:
3913-
status:
3914-
type: array
3915-
items:
3913+
purl_statuses:
3914+
type: object
3915+
additionalProperties:
3916+
type: array
3917+
items:
3918+
$ref: '#/components/schemas/PurlSummary'
3919+
uniqueItems: true
3920+
propertyNames:
39163921
type: string
3917-
uniqueItems: true
39183922
version:
39193923
type:
39203924
- string
@@ -3942,4 +3946,4 @@ components:
39423946
oneOf:
39433947
- type: 'null'
39443948
- $ref: '#/components/schemas/Severity'
3945-
description: Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories.
3949+
description: Average (arithmetic mean) severity of the vulnerability aggregated from *all* related advisories.

client/src/app/client/schemas.gen.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -2624,14 +2624,20 @@ export const VulnerabilitySbomStatusSchema = {
26242624
},
26252625
{
26262626
type: "object",
2627-
required: ["status"],
2627+
required: ["purl_statuses"],
26282628
properties: {
2629-
status: {
2630-
type: "array",
2631-
items: {
2629+
purl_statuses: {
2630+
type: "object",
2631+
additionalProperties: {
2632+
type: "array",
2633+
items: {
2634+
$ref: "#/components/schemas/PurlSummary",
2635+
},
2636+
uniqueItems: true,
2637+
},
2638+
propertyNames: {
26322639
type: "string",
26332640
},
2634-
uniqueItems: true,
26352641
},
26362642
version: {
26372643
type: ["string", "null"],

client/src/app/client/types.gen.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -963,7 +963,9 @@ export type VulnerabilityHead = {
963963
};
964964

965965
export type VulnerabilitySbomStatus = SbomHead & {
966-
status: Array<string>;
966+
purl_statuses: {
967+
[key: string]: Array<PurlSummary>;
968+
};
967969
version?: string | null;
968970
};
969971

client/src/app/hooks/domain-controls/useSbomsOfVulnerability.ts

+24-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import React from "react";
22

33
import { VulnerabilityStatus } from "@app/api/models";
4-
import { SbomHead, VulnerabilityAdvisorySummary } from "@app/client";
4+
import {
5+
PurlSummary,
6+
VulnerabilityAdvisorySummary,
7+
VulnerabilitySbomStatus,
8+
} from "@app/client";
59
import { useFetchVulnerabilityById } from "@app/queries/vulnerabilities";
610

711
const areSbomOfVulnerabilityEqual = (
@@ -12,16 +16,18 @@ const areSbomOfVulnerabilityEqual = (
1216
};
1317

1418
interface FlatSbomOfVulnerability {
15-
sbom: SbomHead & { version: string | null };
19+
sbom: VulnerabilitySbomStatus;
1620
sbomStatus: VulnerabilityStatus;
1721
advisory: VulnerabilityAdvisorySummary;
22+
packages: PurlSummary[];
1823
}
1924

2025
interface SbomOfVulnerability {
21-
sbom: SbomHead & { version: string | null };
26+
sbom: VulnerabilitySbomStatus;
2227
sbomStatus: VulnerabilityStatus;
2328
relatedPackages: {
2429
advisory: VulnerabilityAdvisorySummary;
30+
packages: PurlSummary[];
2531
}[];
2632
}
2733

@@ -40,17 +46,19 @@ const advisoryToModels = (advisories: VulnerabilityAdvisorySummary[]) => {
4046
return (
4147
(advisory.sboms ?? [])
4248
.flatMap((sbomStatuses) => {
43-
return sbomStatuses.status.map((sbomStatus) => {
44-
const result: FlatSbomOfVulnerability = {
45-
sbom: {
46-
...sbomStatuses,
47-
version: sbomStatuses.version || null,
48-
},
49-
sbomStatus: sbomStatus as VulnerabilityStatus,
50-
advisory: advisory,
51-
};
52-
return result;
53-
});
49+
return Object.entries(sbomStatuses.purl_statuses || {}).map(
50+
([status, packages]) => {
51+
const result: FlatSbomOfVulnerability = {
52+
sbom: {
53+
...sbomStatuses,
54+
},
55+
sbomStatus: status as VulnerabilityStatus,
56+
advisory: advisory,
57+
packages: packages,
58+
};
59+
return result;
60+
}
61+
);
5462
})
5563
// group
5664
.reduce((prev, current) => {
@@ -69,6 +77,7 @@ const advisoryToModels = (advisories: VulnerabilityAdvisorySummary[]) => {
6977
...existingElement.relatedPackages,
7078
{
7179
advisory: current.advisory,
80+
packages: current.packages,
7281
},
7382
],
7483
};
@@ -81,6 +90,7 @@ const advisoryToModels = (advisories: VulnerabilityAdvisorySummary[]) => {
8190
relatedPackages: [
8291
{
8392
advisory: current.advisory,
93+
packages: current.packages,
8494
},
8595
],
8696
};

client/src/app/pages/vulnerability-details/advisories-by-vulnerability.stories.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ export const PrimaryState: Story = {
7676
],
7777
name: "quarkus-bom",
7878
version: "3.2.12.Final-redhat-00002",
79-
status: ["affected"],
79+
purl_statuses: {
80+
affected: [],
81+
},
8082
number_of_packages: 1,
8183
},
8284
{
@@ -93,7 +95,9 @@ export const PrimaryState: Story = {
9395
],
9496
name: "quarkus-bom",
9597
version: "3.2.11.Final-redhat-00001",
96-
status: ["affected"],
98+
purl_statuses: {
99+
affected: [],
100+
},
97101
number_of_packages: 1,
98102
},
99103
],

client/src/app/pages/vulnerability-details/sboms-by-vulnerability.tsx

+79-6
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ import { Link } from "react-router-dom";
44
import dayjs from "dayjs";
55

66
import { Toolbar, ToolbarContent, ToolbarItem } from "@patternfly/react-core";
7-
import { Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";
7+
import {
8+
ExpandableRowContent,
9+
Table,
10+
Tbody,
11+
Td,
12+
Th,
13+
Thead,
14+
Tr,
15+
} from "@patternfly/react-table";
816

917
import { FilterType } from "@app/components/FilterToolbar";
1018
import { SimplePagination } from "@app/components/SimplePagination";
@@ -17,7 +25,8 @@ import { VulnerabilityStatusLabel } from "@app/components/VulnerabilityStatusLab
1725
import { useSbomsOfVulnerability } from "@app/hooks/domain-controls/useSbomsOfVulnerability";
1826
import { useLocalTableControls } from "@app/hooks/table-controls";
1927
import { useWithUiId } from "@app/utils/query-utils";
20-
import { formatDate } from "@app/utils/utils";
28+
import { decomposePurl, formatDate } from "@app/utils/utils";
29+
import { PackageQualifiers } from "@app/components/PackageQualifiers";
2130

2231
interface SbomsByVulnerabilityProps {
2332
vulnerabilityId: string;
@@ -41,7 +50,7 @@ export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({
4150
tableName: "sboms-table",
4251
idProperty: "_ui_unique_id",
4352
items: tableDataWithUiId,
44-
isLoading: false,
53+
isLoading: isFetching,
4554
columnNames: {
4655
name: "Name",
4756
version: "Version",
@@ -69,7 +78,8 @@ export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({
6978
getItemValue: (item) => item.sbom?.name ?? "",
7079
},
7180
],
72-
isExpansionEnabled: false,
81+
isExpansionEnabled: true,
82+
expandableVariant: "compound",
7383
});
7484

7585
const {
@@ -84,6 +94,7 @@ export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({
8494
getThProps,
8595
getTrProps,
8696
getTdProps,
97+
getExpandedContentTdProps,
8798
},
8899
expansionDerivedState: { isCellExpanded },
89100
} = tableControls;
@@ -149,9 +160,14 @@ export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({
149160
</Td>
150161
<Td
151162
width={10}
152-
{...getTdProps({ columnKey: "dependencies" })}
163+
{...getTdProps({
164+
columnKey: "dependencies",
165+
isCompoundExpandToggle: true,
166+
item: item,
167+
rowIndex,
168+
})}
153169
>
154-
{item?.sbom?.number_of_packages}
170+
{item.relatedPackages.length}
155171
</Td>
156172
<Td width={10} {...getTdProps({ columnKey: "supplier" })}>
157173
{item?.sbom?.authors.join(", ")}
@@ -165,6 +181,63 @@ export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({
165181
</Td>
166182
</TableRowContentWithControls>
167183
</Tr>
184+
{isCellExpanded(item) ? (
185+
<Tr isExpanded>
186+
<Td
187+
{...getExpandedContentTdProps({
188+
item,
189+
})}
190+
>
191+
<ExpandableRowContent>
192+
{isCellExpanded(item, "dependencies") ? (
193+
<>
194+
<Table variant="compact">
195+
<Thead>
196+
<Tr>
197+
<Th>Type</Th>
198+
<Th>Namespace</Th>
199+
<Th>Name</Th>
200+
<Th>Version</Th>
201+
<Th>Path</Th>
202+
<Th>Qualifiers</Th>
203+
</Tr>
204+
</Thead>
205+
<Tbody>
206+
{item.relatedPackages
207+
.flatMap((item) => item.packages)
208+
.map((purl, index) => {
209+
const decomposedPurl = decomposePurl(
210+
purl.purl
211+
);
212+
return (
213+
<Tr key={`${index}-purl`}>
214+
<Td>{decomposedPurl?.type}</Td>
215+
<Td>{decomposedPurl?.namespace}</Td>
216+
<Td>
217+
<Link to={`/packages/${purl.uuid}`}>
218+
{decomposedPurl?.name}
219+
</Link>
220+
</Td>
221+
<Td>{decomposedPurl?.version}</Td>
222+
<Td>{decomposedPurl?.path}</Td>
223+
<Td>
224+
{decomposedPurl?.qualifiers && (
225+
<PackageQualifiers
226+
value={decomposedPurl?.qualifiers}
227+
/>
228+
)}
229+
</Td>
230+
</Tr>
231+
);
232+
})}
233+
</Tbody>
234+
</Table>
235+
</>
236+
) : null}
237+
</ExpandableRowContent>
238+
</Td>
239+
</Tr>
240+
) : null}
168241
</Tbody>
169242
);
170243
})}

0 commit comments

Comments
 (0)