344 lines
12 KiB
TypeScript
344 lines
12 KiB
TypeScript
/*
|
|
* Copyright (c) All respective contributors to the Peridot Project. All rights reserved.
|
|
* Copyright (c) 2021-2022 Rocky Enterprise Software Foundation, Inc. All rights reserved.
|
|
* Copyright (c) 2021-2022 Ctrl IQ, Inc. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software without
|
|
* specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
import {
|
|
Alert,
|
|
AlertDescription,
|
|
AlertIcon,
|
|
AlertTitle,
|
|
Box,
|
|
Breadcrumb,
|
|
BreadcrumbItem,
|
|
BreadcrumbLink,
|
|
Heading,
|
|
HStack,
|
|
Link,
|
|
ListItem,
|
|
Spinner,
|
|
Tab,
|
|
TabList,
|
|
TabPanel,
|
|
TabPanels,
|
|
Tabs,
|
|
Text,
|
|
UnorderedList,
|
|
useColorModeValue,
|
|
VStack,
|
|
} from '@chakra-ui/react';
|
|
import {
|
|
severityToBadge,
|
|
severityToText,
|
|
typeToText,
|
|
} from 'apollo/ui/src/enumToText';
|
|
import {
|
|
V1Advisory,
|
|
V1AdvisoryType,
|
|
} from 'bazel-bin/apollo/proto/v1/client_typescript';
|
|
import { reqap } from 'common/ui/reqap';
|
|
import React, { useState } from 'react';
|
|
import { RouteComponentProps } from 'react-router';
|
|
import { Link as RouterLink } from 'react-router-dom';
|
|
|
|
import { api } from '../api';
|
|
import { COLOR_RESF_GREEN } from '../styles';
|
|
|
|
interface ShowErrataParams {
|
|
id: string;
|
|
}
|
|
|
|
export interface ShowErrataProps
|
|
extends RouteComponentProps<ShowErrataParams> {}
|
|
|
|
export const ShowErrata = (props: ShowErrataProps) => {
|
|
const id = props.match.params.id;
|
|
|
|
const cardBg = useColorModeValue('white', 'gray.800');
|
|
const sideBg = useColorModeValue('gray.100', 'gray.700');
|
|
const linkBlue = useColorModeValue('blue.600', 'blue.300');
|
|
const linkPurple = useColorModeValue('purple.600', 'purple.300');
|
|
|
|
const [errata, setErrata] = useState<V1Advisory>();
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [isError, setIsError] = useState(false);
|
|
|
|
React.useEffect(() => {
|
|
const fetch = async () => {
|
|
setIsLoading(true);
|
|
|
|
const [err, res] = await reqap(() => api.getAdvisory({ id }));
|
|
|
|
setIsLoading(false);
|
|
|
|
if (err || !res) {
|
|
setIsError(true);
|
|
setErrata(undefined);
|
|
return;
|
|
}
|
|
|
|
setIsError(false);
|
|
|
|
setErrata(res.advisory);
|
|
};
|
|
|
|
fetch();
|
|
}, [id]);
|
|
|
|
return (
|
|
<Box
|
|
w="100%"
|
|
h="100%"
|
|
display="flex"
|
|
flexDirection="column"
|
|
p={4}
|
|
alignItems="stretch"
|
|
maxWidth="1300px"
|
|
m="auto"
|
|
>
|
|
<Breadcrumb mb={4}>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbLink as={RouterLink} to="/">
|
|
Product Errata
|
|
</BreadcrumbLink>
|
|
</BreadcrumbItem>
|
|
<BreadcrumbItem>
|
|
<BreadcrumbLink isCurrentPage>{id}</BreadcrumbLink>
|
|
</BreadcrumbItem>
|
|
</Breadcrumb>
|
|
{isLoading ? (
|
|
<Spinner
|
|
m="auto"
|
|
size="xl"
|
|
alignSelf="center"
|
|
color={COLOR_RESF_GREEN}
|
|
thickness="3px"
|
|
/>
|
|
) : isError ? (
|
|
<Alert
|
|
status="error"
|
|
m="auto"
|
|
flexDirection="column"
|
|
width="300px"
|
|
borderRadius="md"
|
|
>
|
|
<AlertIcon mr="0" />
|
|
<AlertTitle>Something has gone wrong</AlertTitle>
|
|
<AlertDescription>Failed to load errata</AlertDescription>
|
|
</Alert>
|
|
) : (
|
|
errata && (
|
|
<>
|
|
<HStack
|
|
alignItems="center"
|
|
backgroundColor={cardBg}
|
|
py="2"
|
|
px="4"
|
|
spacing="6"
|
|
mb={2}
|
|
>
|
|
{severityToBadge(errata.severity, errata.type, 40)}
|
|
<VStack alignItems="stretch" spacing="0" flexGrow={1}>
|
|
<HStack justifyContent="space-between">
|
|
<Text fontSize="lg" fontWeight="bold">
|
|
{errata.name}
|
|
</Text>
|
|
</HStack>
|
|
<Text fontSize="sm">{errata.synopsis}</Text>
|
|
</VStack>
|
|
</HStack>
|
|
<Tabs backgroundColor={cardBg} p="2">
|
|
<TabList>
|
|
<Tab>Erratum</Tab>
|
|
<Tab>Updated Packages</Tab>
|
|
</TabList>
|
|
<Box
|
|
display="flex"
|
|
flexDir="row"
|
|
alignItems="stretch"
|
|
flexWrap="wrap"
|
|
justifyContent="space-between"
|
|
>
|
|
<TabPanels maxWidth="850px" px="2">
|
|
<TabPanel>
|
|
<Heading as="h2" size="md">
|
|
Topic
|
|
</Heading>
|
|
{errata.topic?.split('\n').map((p, i) => (
|
|
<Text key={i} mt={2}>
|
|
{p}
|
|
</Text>
|
|
))}
|
|
<Heading as="h2" size="md" mt={4}>
|
|
Description
|
|
</Heading>
|
|
{errata.description?.split('\n').map((p, i) => (
|
|
<Text key={i} mt={2}>
|
|
{p}
|
|
</Text>
|
|
))}
|
|
</TabPanel>
|
|
<TabPanel>
|
|
<VStack alignItems="flex-start" spacing="6">
|
|
{Object.keys(errata.rpms || {}).map((product) => (
|
|
<div key={product}>
|
|
<Heading as="h2" size="lg" mb={4} fontWeight="300">
|
|
{product}
|
|
</Heading>
|
|
<Heading as="h3" size="md" mt={2}>
|
|
SRPMs
|
|
</Heading>
|
|
<UnorderedList pl="4">
|
|
{errata.rpms?.[product]?.nvras
|
|
?.filter((x) => x.indexOf('.src.rpm') !== -1)
|
|
.map((x) => (
|
|
<ListItem key={x}>{x}</ListItem>
|
|
))}
|
|
</UnorderedList>
|
|
<Heading as="h3" size="md" mt={2}>
|
|
RPMs
|
|
</Heading>
|
|
<UnorderedList pl="4">
|
|
{errata.rpms?.[product]?.nvras
|
|
?.filter((x) => x.indexOf('.src.rpm') === -1)
|
|
.map((x) => (
|
|
<ListItem key={x}>{x}</ListItem>
|
|
))}
|
|
</UnorderedList>
|
|
</div>
|
|
))}
|
|
</VStack>
|
|
</TabPanel>
|
|
</TabPanels>
|
|
<VStack
|
|
py="4"
|
|
px="8"
|
|
alignItems="flex-start"
|
|
minWidth="300px"
|
|
spacing="5"
|
|
flexShrink={0}
|
|
backgroundColor={sideBg}
|
|
>
|
|
<Text>
|
|
<b>Issued:</b> {errata.publishedAt?.toLocaleDateString()}
|
|
</Text>
|
|
<Text>
|
|
<b>Type:</b> {typeToText(errata.type)}
|
|
</Text>
|
|
{errata.type === V1AdvisoryType.Security && (
|
|
<Text>
|
|
<b>Severity:</b> {severityToText(errata.severity)}
|
|
</Text>
|
|
)}
|
|
<Box>
|
|
<Text fontWeight="bold">
|
|
Affected Product
|
|
{(errata.affectedProducts?.length || 0) > 1 ? 's' : ''}
|
|
</Text>
|
|
<UnorderedList>
|
|
{errata.affectedProducts?.map((x, idx) => (
|
|
<ListItem key={idx}>{x}</ListItem>
|
|
))}
|
|
</UnorderedList>
|
|
</Box>
|
|
<Box>
|
|
<Text fontWeight="bold">Fixes</Text>
|
|
<UnorderedList>
|
|
{errata.fixes?.map((x, idx) => (
|
|
<ListItem key={idx}>
|
|
<Link
|
|
href={x.sourceLink}
|
|
isExternal
|
|
color={linkBlue}
|
|
_visited={{
|
|
color: linkPurple,
|
|
}}
|
|
>
|
|
{x.sourceBy} - {x.ticket}
|
|
</Link>
|
|
</ListItem>
|
|
))}
|
|
</UnorderedList>
|
|
</Box>
|
|
<Box>
|
|
<Text fontWeight="bold">CVEs</Text>
|
|
<UnorderedList>
|
|
{!!errata.cves?.length ? (
|
|
errata.cves?.map((x, idx) => {
|
|
let text = `${x.name}${
|
|
x.sourceBy !== '' && ` (Source: ${x.sourceBy})`
|
|
}`;
|
|
|
|
return (
|
|
<ListItem key={idx}>
|
|
{x.sourceLink === '' ? (
|
|
<span>{text}</span>
|
|
) : (
|
|
<Link
|
|
href={x.sourceLink}
|
|
isExternal
|
|
color={linkBlue}
|
|
_visited={{
|
|
color: linkPurple,
|
|
}}
|
|
>
|
|
{text}
|
|
</Link>
|
|
)}
|
|
</ListItem>
|
|
);
|
|
})
|
|
) : (
|
|
<ListItem>No CVEs</ListItem>
|
|
)}
|
|
</UnorderedList>
|
|
</Box>
|
|
<Box>
|
|
<Text fontWeight="bold">References</Text>
|
|
<UnorderedList>
|
|
{!!errata.references?.length ? (
|
|
errata.references?.map((x, idx) => (
|
|
<ListItem key={idx}>{x}</ListItem>
|
|
))
|
|
) : (
|
|
<ListItem>No references</ListItem>
|
|
)}
|
|
</UnorderedList>
|
|
</Box>
|
|
</VStack>
|
|
</Box>
|
|
</Tabs>
|
|
</>
|
|
)
|
|
)}
|
|
</Box>
|
|
);
|
|
};
|