Skip to content

Commit

Permalink
feat: Packages within Vulnerabilities detail page (#83)
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosthe19916 authored Jun 28, 2024
1 parent 7ff01aa commit b6f7181
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 25 deletions.
38 changes: 27 additions & 11 deletions client/src/app/api/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ export interface HubPaginatedResult<T> {
params: HubRequestParams;
}

// Base
// Advisories

export type Severity = "none" | "low" | "medium" | "high" | "critical";

export interface AdvisoryBase {
export interface Advisory {
identifier: string;
published: string;
modified: string;
Expand All @@ -45,32 +47,46 @@ export interface AdvisoryBase {
name?: string;
website?: string;
};
}

// Advisories

export type Severity = "none" | "low" | "medium" | "high" | "critical";

export interface Advisory extends AdvisoryBase {
average_severity?: Severity;
vulnerabilities: VulnerabilityWithinAdvisory[];
}

export interface AdvisoryWithinVulnerability extends AdvisoryBase {
export type StatusType = "fixed" | "not_affected" | "known_not_affected" | "affected";

export interface AdvisoryWithinVulnerability {
identifier: string;
published: string;
modified: string;
title: string;
uuid: string;
issuer?: {
name?: string;
website?: string;
};

severity?: Severity;
statuses?: {
[key in StatusType]?: {
version: string;
package: {
purl: string;
}
}[]
}
}

// Vulnerability

export interface Vulnerability {
identifier: string;
title: string;
title?: string;
average_severity?: Severity;
cwe: string;
published: string;
modified: string;

advisories: AdvisoryBase[];
advisories: AdvisoryWithinVulnerability[];
}

export interface VulnerabilityWithinAdvisory {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import {
import { useLocalTableControls } from "@app/hooks/table-controls";
import { formatDate } from "@app/utils/utils";

interface RelatedAdvisoriesProps {
interface AdvisoriesByVulnerabilityProps {
advisories: AdvisoryWithinVulnerability[];
}

export const RelatedAdvisories: React.FC<RelatedAdvisoriesProps> = ({
export const AdvisoriesByVulnerability: React.FC<AdvisoriesByVulnerabilityProps> = ({
advisories,
}) => {
const tableControls = useLocalTableControls({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import React from "react";
import { NavLink } from "react-router-dom";

import { DescriptionList, DescriptionListDescription, DescriptionListGroup, DescriptionListTerm, Label, Toolbar, ToolbarContent, ToolbarItem } from "@patternfly/react-core";
import { ExpandableRowContent, Table, Tbody, Td, Th, Thead, Tr } from "@patternfly/react-table";

import { AdvisoryWithinVulnerability, DecomposedPurl } from "@app/api/models";
import { FilterToolbar, FilterType } from "@app/components/FilterToolbar";
import { SeverityShieldAndText } from "@app/components/SeverityShieldAndText";
import { SimplePagination } from "@app/components/SimplePagination";
import {
ConditionalTableBody,
TableHeaderContentWithControls,
TableRowContentWithControls,
} from "@app/components/TableControls";
import { useLocalTableControls } from "@app/hooks/table-controls";
import { decomposePurl, formatDate } from "@app/utils/utils";
import { PackageQualifiers } from "@app/components/PackageQualifiers";

interface TableData {
purl: string;
versionRange: string;
decomposedPurl?: DecomposedPurl
}

interface PackagesByVulnerabilityProps {
advisories: AdvisoryWithinVulnerability[];
}

export const PackagesByVulnerability: React.FC<PackagesByVulnerabilityProps> = ({
advisories,
}) => {
const tableData = React.useMemo(() => {
return advisories.flatMap(advisory => advisory.statuses?.affected ?? [])
.map(item => {
const result: TableData = {
versionRange: item.version,
purl: item.package.purl,
decomposedPurl: decomposePurl(item.package.purl)
};
return result;
})
}, [advisories]);

const tableControls = useLocalTableControls({
tableName: "package-table",
idProperty: "purl",
items: tableData,
isLoading: false,
columnNames: {
name: "Name",
namespace: "Namespace",
version: "Version",
type: "Type",
qualifiers: "Qualifiers",
affectedVersions: "Affected version",
},
hasActionsColumn: true,
isSortEnabled: false,
isPaginationEnabled: true,
initialItemsPerPage: 10,
isFilterEnabled: true,
filterCategories: [
{
categoryKey: "",
title: "Filter text",
placeholderText: "Search",
type: FilterType.search,
},
],
isExpansionEnabled: true,
expandableVariant: "single",
});

const {
currentPageItems,
numRenderedColumns,
propHelpers: {
toolbarProps,
filterToolbarProps,
paginationToolbarItemProps,
paginationProps,
tableProps,
getThProps,
getTrProps,
getTdProps,
},
expansionDerivedState: { isCellExpanded },
} = tableControls;

return (
<>
{tableControls.isFilterEnabled && (
<Toolbar {...toolbarProps}>
<ToolbarContent>
<FilterToolbar showFiltersSideBySide {...filterToolbarProps} />
<ToolbarItem {...paginationToolbarItemProps}>
<SimplePagination
idPrefix="package-table"
isTop
paginationProps={paginationProps}
/>
</ToolbarItem>
</ToolbarContent>
</Toolbar>
)}
<Table {...tableProps} aria-label="Package table">
<Thead>
<Tr>
<TableHeaderContentWithControls {...tableControls}>
<Th {...getThProps({ columnKey: "name" })} />
<Th {...getThProps({ columnKey: "namespace" })} />
<Th {...getThProps({ columnKey: "version" })} />
<Th {...getThProps({ columnKey: "type" })} />
<Th {...getThProps({ columnKey: "qualifiers" })} />
<Th {...getThProps({ columnKey: "affectedVersions" })} />
</TableHeaderContentWithControls>
</Tr>
</Thead>
<ConditionalTableBody
isLoading={false}
isError={undefined}
isNoData={tableData.length === 0}
numRenderedColumns={numRenderedColumns}
>
{currentPageItems?.map((item, rowIndex) => {
return (
<Tbody key={item.purl}>
<Tr {...getTrProps({ item })}>
<TableRowContentWithControls
{...tableControls}
item={item}
rowIndex={rowIndex}
>
<Td width={20} {...getTdProps({ columnKey: "name" })}>
<NavLink
to={`/packages/${encodeURIComponent(item.purl)}`}
>
{item.decomposedPurl ? item.decomposedPurl?.name : item.purl}
</NavLink>
</Td>
<Td
width={10}
modifier="truncate"
{...getTdProps({ columnKey: "namespace" })}
>
{item.decomposedPurl?.namespace}
</Td>
<Td
width={15}
modifier="truncate"
{...getTdProps({ columnKey: "version" })}
>
{item.decomposedPurl?.version}
</Td>
<Td
width={10}
modifier="truncate"
{...getTdProps({ columnKey: "type" })}
>
{item.decomposedPurl?.type}
</Td>
<Td
width={30}
{...getTdProps({ columnKey: "qualifiers" })}
>
{item.decomposedPurl?.qualifiers && <PackageQualifiers value={item.decomposedPurl?.qualifiers} />}
</Td>
<Td
width={10}
{...getTdProps({ columnKey: "affectedVersions" })}
>
<Label color="grey">{item.versionRange}</Label>
</Td>
</TableRowContentWithControls>
</Tr>
{isCellExpanded(item) ? (
<Tr isExpanded>
<Td colSpan={7}>
<ExpandableRowContent>
<div className="pf-v5-u-m-md">
<DescriptionList>
<DescriptionListGroup>
<DescriptionListTerm>
Purl
</DescriptionListTerm>
<DescriptionListDescription>
{item.purl}
</DescriptionListDescription>
</DescriptionListGroup>
<DescriptionListGroup>
<DescriptionListTerm>
Path
</DescriptionListTerm>
<DescriptionListDescription>
{item.decomposedPurl?.path}
</DescriptionListDescription>
</DescriptionListGroup>
</DescriptionList>
</div>
</ExpandableRowContent>
</Td>
</Tr>
) : null}
</Tbody>

);
})}

</ConditionalTableBody>
</Table >
<SimplePagination
idPrefix="package-table"
isTop={false}
isCompact
paginationProps={paginationProps}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import {
import { useLocalTableControls } from "@app/hooks/table-controls";
import { formatDate } from "@app/utils/utils";

interface RelatedSBOMsProps {
interface SbomsByVulnerabilityProps {
sboms: SBOM[];
}

export const RelatedSBOMs: React.FC<RelatedSBOMsProps> = ({ sboms }) => {
export const SbomsByVulnerability: React.FC<SbomsByVulnerabilityProps> = ({ sboms }) => {
const tableControls = useLocalTableControls({
tableName: "sboms-table",
idProperty: "id",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { Link } from "react-router-dom";
import {
Breadcrumb,
BreadcrumbItem,
Button,
DescriptionList,
DescriptionListDescription,
DescriptionListGroup,
Expand All @@ -19,19 +18,17 @@ import {
TabTitleText,
Tabs,
Text,
TextContent,
TextContent
} from "@patternfly/react-core";
import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";

import { PathParam, useRouteParams } from "@app/Routes";

import { LoadingWrapper } from "@app/components/LoadingWrapper";
import { useFetchVulnerabilityById } from "@app/queries/vulnerabilities";

import { SeverityShieldAndText } from "@app/components/SeverityShieldAndText";
import { useFetchVulnerabilityById } from "@app/queries/vulnerabilities";
import { formatDate } from "@app/utils/utils";

import { RelatedAdvisories } from "./related-advisories";
import { AdvisoriesByVulnerability } from "./advisories-by-vulnerability";
import { PackagesByVulnerability } from "./packages-by-vulnerability";

export const CveDetails: React.FC = () => {
const vulnerabilityId = useRouteParams(PathParam.VULNERABILITY_ID);
Expand Down Expand Up @@ -95,7 +92,7 @@ export const CveDetails: React.FC = () => {
<Tabs defaultActiveKey={0} aria-label="CVE tabs" role="region">
<Tab
eventKey={0}
title={<TabTitleText>Related products</TabTitleText>}
title={<TabTitleText>SBOMs</TabTitleText>}
>
{/* <LoadingWrapper isFetching={isFetching} fetchError={fetchError}>
{vulnerability && (
Expand All @@ -109,11 +106,21 @@ export const CveDetails: React.FC = () => {
</Tab>
<Tab
eventKey={1}
title={<TabTitleText>Related advisories</TabTitleText>}
title={<TabTitleText>Packages</TabTitleText>}
>
<LoadingWrapper isFetching={isFetching} fetchError={fetchError}>
{vulnerability && (
<PackagesByVulnerability advisories={vulnerability.advisories} />
)}
</LoadingWrapper>
</Tab>
<Tab
eventKey={2}
title={<TabTitleText>Advisories</TabTitleText>}
>
<LoadingWrapper isFetching={isFetching} fetchError={fetchError}>
{vulnerability && (
<RelatedAdvisories advisories={vulnerability.advisories} />
<AdvisoriesByVulnerability advisories={vulnerability.advisories} />
)}
</LoadingWrapper>
</Tab>
Expand Down

0 comments on commit b6f7181

Please sign in to comment.