feat: Update signature integration to display extra info (#378)

Signed-off-by: Raul-Cristian Kele <raulkeleblk@gmail.com>
This commit is contained in:
Raul Kele 2023-08-28 18:03:32 +03:00 committed by GitHub
parent ac84c375c0
commit 845726cd08
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 89 additions and 16 deletions

View File

@ -22,6 +22,29 @@ const mockImage = {
vendor: '', vendor: '',
size: '585', size: '585',
tags: '', tags: '',
isSigned: true,
signatureInfo: [
{
Tool: 'cosign',
IsTrusted: false,
Author: ''
},
{
Tool: 'cosign',
IsTrusted: false,
Author: ''
},
{
Tool: 'cosign',
IsTrusted: false,
Author: ''
},
{
Tool: 'cosign',
IsTrusted: false,
Author: ''
}
],
platforms: [{ Os: 'linux', Arch: 'amd64' }] platforms: [{ Os: 'linux', Arch: 'amd64' }]
}; };
@ -34,6 +57,8 @@ const RepoCardWrapper = (props) => {
version={image.latestVersion} version={image.latestVersion}
description={image.description} description={image.description}
vendor={image.vendor} vendor={image.vendor}
isSigned={image.isSigned}
signatureInfo={image.signatureInfo}
key={1} key={1}
lastUpdated={image.lastUpdated} lastUpdated={image.lastUpdated}
platforms={image.platforms} platforms={image.platforms}

View File

@ -84,11 +84,11 @@ const endpoints = {
repoList: ({ pageNumber = 1, pageSize = 15 } = {}) => repoList: ({ pageNumber = 1, pageSize = 15 } = {}) =>
`/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${ `/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize
}}){Results {Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description Licenses Title Source IsSigned Documentation Vendor Labels} IsStarred IsBookmarked DownloadCount}}}`, }}){Results {Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description Licenses Title Source IsSigned SignatureInfo { Tool IsTrusted Author } Documentation Vendor Labels} IsStarred IsBookmarked DownloadCount}}}`,
detailedRepoInfo: (name) => detailedRepoInfo: (name) =>
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Manifests {Digest Platform {Os Arch} Size} Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors IsStarred IsBookmarked NewestImage {RepoName IsSigned Vulnerabilities {MaxSeverity Count} Manifests {Digest} Tag Vendor Title Documentation DownloadCount Source Description Licenses}}}}`, `/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Manifests {Digest Platform {Os Arch} Size} Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors IsStarred IsBookmarked NewestImage {RepoName IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count} Manifests {Digest} Tag Vendor Title Documentation DownloadCount Source Description Licenses}}}}`,
detailedImageInfo: (name, tag) => detailedImageInfo: (name, tag) =>
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned Vulnerabilities {MaxSeverity Count} Referrers {MediaType ArtifactType Size Digest Annotations{Key Value}} Tag Manifests {History {Layer {Size Digest} HistoryDescription {CreatedBy EmptyLayer}} Digest ConfigDigest LastUpdated Size Platform {Os Arch}} Vendor Licenses }}`, `/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count} Referrers {MediaType ArtifactType Size Digest Annotations{Key Value}} Tag Manifests {History {Layer {Size Digest} HistoryDescription {CreatedBy EmptyLayer}} Digest ConfigDigest LastUpdated Size Platform {Os Arch}} Vendor Licenses }}`,
vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }, searchTerm = '') => { vulnerabilitiesForRepo: (name, { pageNumber = 1, pageSize = 15 }, searchTerm = '') => {
let query = `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${ let query = `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize
@ -113,11 +113,11 @@ const endpoints = {
dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) => dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) =>
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${ `/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize
}}){Page {TotalCount ItemCount} Results { RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned Vulnerabilities {MaxSeverity Count}}}}`, }}){Page {TotalCount ItemCount} Results { RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count}}}}`,
isDependentOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) => isDependentOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) =>
`/v2/_zot/ext/search?query={DerivedImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${ `/v2/_zot/ext/search?query={DerivedImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${
(pageNumber - 1) * pageSize (pageNumber - 1) * pageSize
}}){Page {TotalCount ItemCount} Results {RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned Vulnerabilities {MaxSeverity Count}}}}`, }}){Page {TotalCount ItemCount} Results {RepoName Tag Description Manifests {Digest Platform {Os Arch} Size} Vendor DownloadCount LastUpdated IsSigned SignatureInfo { Tool IsTrusted Author } Vulnerabilities {MaxSeverity Count}}}}`,
globalSearch: ({ globalSearch: ({
searchQuery = '""', searchQuery = '""',
pageNumber = 1, pageNumber = 1,
@ -136,7 +136,7 @@ const endpoints = {
if (filter.IsBookmarked) filterParam += ` IsBookmarked: ${filter.IsBookmarked}`; if (filter.IsBookmarked) filterParam += ` IsBookmarked: ${filter.IsBookmarked}`;
filterParam += '}'; filterParam += '}';
if (Object.keys(filter).length === 0) filterParam = ''; if (Object.keys(filter).length === 0) filterParam = '';
return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Page {TotalCount ItemCount} Repos {Name LastUpdated Size Platforms { Os Arch } IsStarred IsBookmarked NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description IsSigned Licenses Vendor Labels } DownloadCount}}}`; return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Page {TotalCount ItemCount} Repos {Name LastUpdated Size Platforms { Os Arch } IsStarred IsBookmarked NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description IsSigned SignatureInfo { Tool IsTrusted Author } Licenses Vendor Labels } DownloadCount}}}`;
}, },
imageSuggestions: ({ searchQuery = '""', pageNumber = 1, pageSize = 15 }) => { imageSuggestions: ({ searchQuery = '""', pageNumber = 1, pageSize = 15 }) => {
const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`; const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`;

View File

@ -221,6 +221,7 @@ function Explore({ searchInputValue }) {
description={item.description} description={item.description}
downloads={item.downloads} downloads={item.downloads}
isSigned={item.isSigned} isSigned={item.isSigned}
signatureInfo={item.signatureInfo}
isBookmarked={item.isBookmarked} isBookmarked={item.isBookmarked}
vendor={item.vendor} vendor={item.vendor}
platforms={item.platforms} platforms={item.platforms}

View File

@ -215,6 +215,7 @@ function Home() {
description={item.description} description={item.description}
downloads={item.downloads} downloads={item.downloads}
isSigned={item.isSigned} isSigned={item.isSigned}
signatureInfo={item.signatureInfo}
isBookmarked={item.isBookmarked} isBookmarked={item.isBookmarked}
vendor={item.vendor} vendor={item.vendor}
platforms={item.platforms} platforms={item.platforms}

View File

@ -271,7 +271,10 @@ function RepoDetails() {
</Stack> </Stack>
<Stack alignItems="center" sx={{ width: { xs: '100%', md: 'auto' } }} direction="row" spacing={2}> <Stack alignItems="center" sx={{ width: { xs: '100%', md: 'auto' } }} direction="row" spacing={2}>
<VulnerabilityIconCheck vulnerabilitySeverity={repoDetailData?.vulnerabilitySeverity} /> <VulnerabilityIconCheck vulnerabilitySeverity={repoDetailData?.vulnerabilitySeverity} />
<SignatureIconCheck isSigned={repoDetailData.isSigned} /> <SignatureIconCheck
isSigned={repoDetailData.isSigned}
signatureInfo={repoDetailData.signatureInfo}
/>
</Stack> </Stack>
{isAuthenticated() && ( {isAuthenticated() && (
<IconButton component="span" onClick={handleBookmarkClick} data-testid="bookmark-button"> <IconButton component="span" onClick={handleBookmarkClick} data-testid="bookmark-button">

View File

@ -184,6 +184,7 @@ function RepoCard(props) {
description, description,
downloads, downloads,
isSigned, isSigned,
signatureInfo,
lastUpdated, lastUpdated,
version, version,
vulnerabilityData, vulnerabilityData,
@ -290,7 +291,7 @@ function RepoCard(props) {
<VulnerabilityIconCheck {...vulnerabilityData} className="hide-on-mobile" /> <VulnerabilityIconCheck {...vulnerabilityData} className="hide-on-mobile" />
</div> </div>
<div className="hide-on-mobile"> <div className="hide-on-mobile">
<SignatureIconCheck isSigned={isSigned} className="hide-on-mobile" /> <SignatureIconCheck isSigned={isSigned} signatureInfo={signatureInfo} className="hide-on-mobile" />
</div> </div>
</Stack> </Stack>
<Tooltip title={description || 'Description not available'} placement="top"> <Tooltip title={description || 'Description not available'} placement="top">

View File

@ -0,0 +1,21 @@
import React from 'react';
import { Typography, Stack } from '@mui/material';
import { isEmpty } from 'lodash';
function SignatureTooltip({ isSigned, signatureInfo }) {
const { tool, isTrusted, author } = !isEmpty(signatureInfo)
? signatureInfo[0]
: { tool: 'Unknown', isTrusted: 'Unknown', author: 'Unknown' };
return (
<Stack direction="column">
<Typography>{isSigned ? 'Verified Signature' : 'Unverified Signature'}</Typography>
<Typography>Tool: {tool}</Typography>
<Typography>Trusted: {isTrusted ? 'Yes' : 'No'}</Typography>
<Typography>Author: {author}</Typography>
</Stack>
);
}
export default SignatureTooltip;

View File

@ -260,7 +260,10 @@ function TagDetails() {
vulnerabilitySeverity={imageDetailData.vulnerabiltySeverity} vulnerabilitySeverity={imageDetailData.vulnerabiltySeverity}
count={imageDetailData.vulnerabilityCount} count={imageDetailData.vulnerabilityCount}
/> />
<SignatureIconCheck isSigned={imageDetailData.isSigned} /> <SignatureIconCheck
isSigned={imageDetailData.isSigned}
signatureInfo={imageDetailData.signatureInfo}
/>
</Stack> </Stack>
</Stack> </Stack>
<Stack direction="row" alignItems="center" spacing="1rem"> <Stack direction="row" alignItems="center" spacing="1rem">

View File

@ -5,6 +5,7 @@ const mapToRepo = (responseRepo) => {
tags: responseRepo.NewestImage?.Labels, tags: responseRepo.NewestImage?.Labels,
description: responseRepo.NewestImage?.Description, description: responseRepo.NewestImage?.Description,
isSigned: responseRepo.NewestImage?.IsSigned, isSigned: responseRepo.NewestImage?.IsSigned,
signatureInfo: responseRepo.NewestImage?.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)),
isBookmarked: responseRepo.IsBookmarked, isBookmarked: responseRepo.IsBookmarked,
isStarred: responseRepo.IsStarred, isStarred: responseRepo.IsStarred,
platforms: responseRepo.Platforms, platforms: responseRepo.Platforms,
@ -37,6 +38,7 @@ const mapToRepoFromRepoInfo = (responseRepoInfo) => {
vulnerabilitySeverity: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity, vulnerabilitySeverity: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity,
vulnerabilityCount: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.Count, vulnerabilityCount: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.Count,
isSigned: responseRepoInfo.Summary?.NewestImage?.IsSigned, isSigned: responseRepoInfo.Summary?.NewestImage?.IsSigned,
signatureInfo: responseRepoInfo.Summary?.NewestImage?.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)),
isBookmarked: responseRepoInfo.Summary?.IsBookmarked, isBookmarked: responseRepoInfo.Summary?.IsBookmarked,
isStarred: responseRepoInfo.Summary?.IsStarred, isStarred: responseRepoInfo.Summary?.IsStarred,
logo: responseRepoInfo.Summary?.NewestImage?.Logo logo: responseRepoInfo.Summary?.NewestImage?.Logo
@ -54,6 +56,7 @@ const mapToImage = (responseImage) => {
lastUpdated: responseImage.LastUpdated, lastUpdated: responseImage.LastUpdated,
description: responseImage.Description, description: responseImage.Description,
isSigned: responseImage.IsSigned, isSigned: responseImage.IsSigned,
signatureInfo: responseImage.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)),
license: responseImage.Licenses, license: responseImage.Licenses,
labels: responseImage.Labels, labels: responseImage.Labels,
title: responseImage.Title, title: responseImage.Title,
@ -94,6 +97,20 @@ const mapCVEInfo = (cveInfo) => {
return cveList; return cveList;
}; };
const mapSignatureInfo = (signatureInfo) => {
return signatureInfo
? {
tool: signatureInfo.Tool,
isTrusted: signatureInfo.IsTrusted,
author: signatureInfo.Author
}
: {
tool: 'Unknown',
isTrusted: 'Unknown',
author: 'Unknown'
};
};
const mapReferrer = (referrer) => ({ const mapReferrer = (referrer) => ({
mediaType: referrer.MediaType, mediaType: referrer.MediaType,
artifactType: referrer.ArtifactType, artifactType: referrer.ArtifactType,

View File

@ -84,11 +84,11 @@ const VulnerabilityChipCheck = ({ vulnerabilitySeverity }) => {
return result; return result;
}; };
const SignatureIconCheck = ({ isSigned }) => { const SignatureIconCheck = ({ isSigned, signatureInfo }) => {
if (isSigned) { if (isSigned) {
return <VerifiedSignatureIcon />; return <VerifiedSignatureIcon signatureInfo={signatureInfo} />;
} else { } else {
return <UnverifiedSignatureIcon />; return <UnverifiedSignatureIcon signatureInfo={signatureInfo} />;
} }
}; };

View File

@ -3,6 +3,7 @@ import { Chip, Tooltip } from '@mui/material';
import SvgIcon from '@mui/material/SvgIcon'; import SvgIcon from '@mui/material/SvgIcon';
import { ReactComponent as failedScanBug } from '../assets/failedScan.svg'; import { ReactComponent as failedScanBug } from '../assets/failedScan.svg';
import { createSvgIcon } from '@mui/material/utils'; import { createSvgIcon } from '@mui/material/utils';
import SignatureTooltip from 'components/Shared/SignatureTooltip';
const FilledBugIcon = createSvgIcon( const FilledBugIcon = createSvgIcon(
<path d="M17.0293 5.13093V6.1543H18.3828L21.2414 3.24068L22.2621 4.27812L19.5552 7.03876L19.5879 7.12668C20.1841 8.73695 20.4862 10.4449 20.4793 12.1662C20.4793 12.5064 20.4678 12.8466 20.4448 13.186L20.4397 13.2634H24V14.7334H20.2569L20.2466 14.7932C19.9431 16.4882 19.3517 18.0338 18.5466 19.335L18.4862 19.4335L21.9276 22.9608L20.9052 24L17.6121 20.6239L17.5138 20.7365C16.0259 22.4333 14.0983 23.4514 11.9983 23.4514C9.86724 23.4514 7.91207 22.4016 6.41552 20.6573L6.31552 20.5413L3.08966 23.833L2.06897 22.792L5.45345 19.3403L5.39483 19.2436C4.61897 17.9618 4.04655 16.4478 3.75 14.7932L3.73966 14.7334H0V13.2634H3.55862L3.55345 13.1843C3.53103 12.8502 3.51897 12.509 3.51897 12.1644C3.51202 10.4654 3.80581 8.77905 4.38621 7.18646L4.41897 7.1003L1.64138 4.2535L2.66379 3.21606L5.53103 6.1543H6.96724V5.13093C6.96724 3.77012 7.49729 2.46505 8.4408 1.50281C9.3843 0.540578 10.664 0 11.9983 0C13.3326 0 14.6123 0.540578 15.5558 1.50281C16.4993 2.46505 17.0293 3.77012 17.0293 5.13093Z" />, <path d="M17.0293 5.13093V6.1543H18.3828L21.2414 3.24068L22.2621 4.27812L19.5552 7.03876L19.5879 7.12668C20.1841 8.73695 20.4862 10.4449 20.4793 12.1662C20.4793 12.5064 20.4678 12.8466 20.4448 13.186L20.4397 13.2634H24V14.7334H20.2569L20.2466 14.7932C19.9431 16.4882 19.3517 18.0338 18.5466 19.335L18.4862 19.4335L21.9276 22.9608L20.9052 24L17.6121 20.6239L17.5138 20.7365C16.0259 22.4333 14.0983 23.4514 11.9983 23.4514C9.86724 23.4514 7.91207 22.4016 6.41552 20.6573L6.31552 20.5413L3.08966 23.833L2.06897 22.792L5.45345 19.3403L5.39483 19.2436C4.61897 17.9618 4.04655 16.4478 3.75 14.7932L3.73966 14.7334H0V13.2634H3.55862L3.55345 13.1843C3.53103 12.8502 3.51897 12.509 3.51897 12.1644C3.51202 10.4654 3.80581 8.77905 4.38621 7.18646L4.41897 7.1003L1.64138 4.2535L2.66379 3.21606L5.53103 6.1543H6.96724V5.13093C6.96724 3.77012 7.49729 2.46505 8.4408 1.50281C9.3843 0.540578 10.664 0 11.9983 0C13.3326 0 14.6123 0.540578 15.5558 1.50281C16.4993 2.46505 17.0293 3.77012 17.0293 5.13093Z" />,
@ -240,9 +241,9 @@ const CriticalVulnerabilityChip = () => {
); );
}; };
const UnverifiedSignatureIcon = () => { const UnverifiedSignatureIcon = ({ signatureInfo }) => {
return ( return (
<Tooltip title="Unverified Signature" placement="top"> <Tooltip title={<SignatureTooltip isSigned={false} signatureInfo={signatureInfo} />} placement="top">
<UnverifiedShieldIcon <UnverifiedShieldIcon
sx={{ sx={{
color: '#E53935', color: '#E53935',
@ -257,9 +258,9 @@ const UnverifiedSignatureIcon = () => {
</Tooltip> </Tooltip>
); );
}; };
const VerifiedSignatureIcon = () => { const VerifiedSignatureIcon = ({ signatureInfo }) => {
return ( return (
<Tooltip title="Verified Signature" placement="top"> <Tooltip title={<SignatureTooltip isSigned={true} signatureInfo={signatureInfo} />} placement="top">
<VerifiedShieldIcon <VerifiedShieldIcon
viewBox="0 0 24 24" viewBox="0 0 24 24"
sx={{ sx={{