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">