From 845726cd084f3dea6654813a55c44f0170add381 Mon Sep 17 00:00:00 2001 From: Raul Kele Date: Mon, 28 Aug 2023 18:03:32 +0300 Subject: [PATCH] feat: Update signature integration to display extra info (#378) Signed-off-by: Raul-Cristian Kele --- src/__tests__/Shared/RepoCard.test.js | 25 +++++++++++++++++++ src/api.js | 12 ++++----- src/components/Explore/Explore.jsx | 1 + src/components/Home/Home.jsx | 1 + src/components/Repo/RepoDetails.jsx | 5 +++- src/components/Shared/RepoCard.jsx | 3 ++- src/components/Shared/SignatureTooltip.jsx | 21 ++++++++++++++++ src/components/Tag/TagDetails.jsx | 5 +++- src/utilities/objectModels.js | 17 +++++++++++++ .../vulnerabilityAndSignatureCheck.jsx | 6 ++--- .../vulnerabilityAndSignatureComponents.jsx | 9 ++++--- 11 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 src/components/Shared/SignatureTooltip.jsx diff --git a/src/__tests__/Shared/RepoCard.test.js b/src/__tests__/Shared/RepoCard.test.js index 4ca5afd6..2aa201e6 100644 --- a/src/__tests__/Shared/RepoCard.test.js +++ b/src/__tests__/Shared/RepoCard.test.js @@ -22,6 +22,29 @@ const mockImage = { vendor: '', size: '585', 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' }] }; @@ -34,6 +57,8 @@ const RepoCardWrapper = (props) => { version={image.latestVersion} description={image.description} vendor={image.vendor} + isSigned={image.isSigned} + signatureInfo={image.signatureInfo} key={1} lastUpdated={image.lastUpdated} platforms={image.platforms} diff --git a/src/api.js b/src/api.js index ea4a6531..c0e2e4cf 100644 --- a/src/api.js +++ b/src/api.js @@ -84,11 +84,11 @@ const endpoints = { repoList: ({ pageNumber = 1, pageSize = 15 } = {}) => `/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${ (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) => - `/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) => - `/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 = '') => { let query = `/v2/_zot/ext/search?query={CVEListForImage(image: "${name}", requestedPage: {limit:${pageSize} offset:${ (pageNumber - 1) * pageSize @@ -113,11 +113,11 @@ const endpoints = { dependsOnForImage: (name, { pageNumber = 1, pageSize = 15 } = {}) => `/v2/_zot/ext/search?query={BaseImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${ (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 } = {}) => `/v2/_zot/ext/search?query={DerivedImageList(image: "${name}", requestedPage: {limit:${pageSize} offset:${ (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: ({ searchQuery = '""', pageNumber = 1, @@ -136,7 +136,7 @@ const endpoints = { if (filter.IsBookmarked) filterParam += ` IsBookmarked: ${filter.IsBookmarked}`; 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 }) => { const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`; diff --git a/src/components/Explore/Explore.jsx b/src/components/Explore/Explore.jsx index d8adb7ab..27d663a6 100644 --- a/src/components/Explore/Explore.jsx +++ b/src/components/Explore/Explore.jsx @@ -221,6 +221,7 @@ function Explore({ searchInputValue }) { description={item.description} downloads={item.downloads} isSigned={item.isSigned} + signatureInfo={item.signatureInfo} isBookmarked={item.isBookmarked} vendor={item.vendor} platforms={item.platforms} diff --git a/src/components/Home/Home.jsx b/src/components/Home/Home.jsx index 82407294..8da2eddc 100644 --- a/src/components/Home/Home.jsx +++ b/src/components/Home/Home.jsx @@ -215,6 +215,7 @@ function Home() { description={item.description} downloads={item.downloads} isSigned={item.isSigned} + signatureInfo={item.signatureInfo} isBookmarked={item.isBookmarked} vendor={item.vendor} platforms={item.platforms} diff --git a/src/components/Repo/RepoDetails.jsx b/src/components/Repo/RepoDetails.jsx index 95bc8289..f6d50f8b 100644 --- a/src/components/Repo/RepoDetails.jsx +++ b/src/components/Repo/RepoDetails.jsx @@ -271,7 +271,10 @@ function RepoDetails() { - + {isAuthenticated() && ( diff --git a/src/components/Shared/RepoCard.jsx b/src/components/Shared/RepoCard.jsx index 5060e323..f2003e48 100644 --- a/src/components/Shared/RepoCard.jsx +++ b/src/components/Shared/RepoCard.jsx @@ -184,6 +184,7 @@ function RepoCard(props) { description, downloads, isSigned, + signatureInfo, lastUpdated, version, vulnerabilityData, @@ -290,7 +291,7 @@ function RepoCard(props) {
- +
diff --git a/src/components/Shared/SignatureTooltip.jsx b/src/components/Shared/SignatureTooltip.jsx new file mode 100644 index 00000000..b925c196 --- /dev/null +++ b/src/components/Shared/SignatureTooltip.jsx @@ -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 ( + + {isSigned ? 'Verified Signature' : 'Unverified Signature'} + Tool: {tool} + Trusted: {isTrusted ? 'Yes' : 'No'} + Author: {author} + + ); +} + +export default SignatureTooltip; diff --git a/src/components/Tag/TagDetails.jsx b/src/components/Tag/TagDetails.jsx index a9cf0c6a..a9579bd7 100644 --- a/src/components/Tag/TagDetails.jsx +++ b/src/components/Tag/TagDetails.jsx @@ -260,7 +260,10 @@ function TagDetails() { vulnerabilitySeverity={imageDetailData.vulnerabiltySeverity} count={imageDetailData.vulnerabilityCount} /> - + diff --git a/src/utilities/objectModels.js b/src/utilities/objectModels.js index b548bd3e..7b577839 100644 --- a/src/utilities/objectModels.js +++ b/src/utilities/objectModels.js @@ -5,6 +5,7 @@ const mapToRepo = (responseRepo) => { tags: responseRepo.NewestImage?.Labels, description: responseRepo.NewestImage?.Description, isSigned: responseRepo.NewestImage?.IsSigned, + signatureInfo: responseRepo.NewestImage?.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)), isBookmarked: responseRepo.IsBookmarked, isStarred: responseRepo.IsStarred, platforms: responseRepo.Platforms, @@ -37,6 +38,7 @@ const mapToRepoFromRepoInfo = (responseRepoInfo) => { vulnerabilitySeverity: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity, vulnerabilityCount: responseRepoInfo.Summary?.NewestImage?.Vulnerabilities?.Count, isSigned: responseRepoInfo.Summary?.NewestImage?.IsSigned, + signatureInfo: responseRepoInfo.Summary?.NewestImage?.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)), isBookmarked: responseRepoInfo.Summary?.IsBookmarked, isStarred: responseRepoInfo.Summary?.IsStarred, logo: responseRepoInfo.Summary?.NewestImage?.Logo @@ -54,6 +56,7 @@ const mapToImage = (responseImage) => { lastUpdated: responseImage.LastUpdated, description: responseImage.Description, isSigned: responseImage.IsSigned, + signatureInfo: responseImage.SignatureInfo?.map((sigInfo) => mapSignatureInfo(sigInfo)), license: responseImage.Licenses, labels: responseImage.Labels, title: responseImage.Title, @@ -94,6 +97,20 @@ const mapCVEInfo = (cveInfo) => { 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) => ({ mediaType: referrer.MediaType, artifactType: referrer.ArtifactType, diff --git a/src/utilities/vulnerabilityAndSignatureCheck.jsx b/src/utilities/vulnerabilityAndSignatureCheck.jsx index 296680fa..5f679199 100644 --- a/src/utilities/vulnerabilityAndSignatureCheck.jsx +++ b/src/utilities/vulnerabilityAndSignatureCheck.jsx @@ -84,11 +84,11 @@ const VulnerabilityChipCheck = ({ vulnerabilitySeverity }) => { return result; }; -const SignatureIconCheck = ({ isSigned }) => { +const SignatureIconCheck = ({ isSigned, signatureInfo }) => { if (isSigned) { - return ; + return ; } else { - return ; + return ; } }; diff --git a/src/utilities/vulnerabilityAndSignatureComponents.jsx b/src/utilities/vulnerabilityAndSignatureComponents.jsx index 8ec78f96..9f85953f 100644 --- a/src/utilities/vulnerabilityAndSignatureComponents.jsx +++ b/src/utilities/vulnerabilityAndSignatureComponents.jsx @@ -3,6 +3,7 @@ import { Chip, Tooltip } from '@mui/material'; import SvgIcon from '@mui/material/SvgIcon'; import { ReactComponent as failedScanBug } from '../assets/failedScan.svg'; import { createSvgIcon } from '@mui/material/utils'; +import SignatureTooltip from 'components/Shared/SignatureTooltip'; const FilledBugIcon = createSvgIcon( , @@ -240,9 +241,9 @@ const CriticalVulnerabilityChip = () => { ); }; -const UnverifiedSignatureIcon = () => { +const UnverifiedSignatureIcon = ({ signatureInfo }) => { return ( - + } placement="top"> { ); }; -const VerifiedSignatureIcon = () => { +const VerifiedSignatureIcon = ({ signatureInfo }) => { return ( - + } placement="top">