Added signature and vulnerabilities in RepoCard, PreviewCard, RepoDetails and TagDetails with uses and used by tabs
Signed-off-by: Amelia-Maria Breda <ambreda@cisco.com>
This commit is contained in:
parent
4d339506b2
commit
4446aa03c7
@ -30,10 +30,14 @@ const mockImageList = {
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: 'w',
|
||||
IsSigned: true,
|
||||
IsSigned: false,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 7
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -43,23 +47,82 @@ const mockImageList = {
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: false,
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'nodeUnique',
|
||||
Name: 'node',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: false,
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'centos',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'NONE',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'debian',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'MEDIUM',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'mysql',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'UNKNOWN',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -77,7 +140,7 @@ describe('Explore component', () => {
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findByText(/alpine/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/mongo/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/nodeUnique/i)).toBeInTheDocument();
|
||||
expect(await screen.findByText(/centos/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays the no data message if no data is received', async () => {
|
||||
@ -91,8 +154,20 @@ describe('Explore component', () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findAllByTestId('unverified-chip')).toHaveLength(2);
|
||||
expect(await screen.findAllByTestId('verified-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('unverified-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('verified-chip')).toHaveLength(5);
|
||||
});
|
||||
|
||||
it('renders vulnerability chips', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<StateExploreWrapper />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('high-vulnerability-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('critical-vulnerability-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('none-vulnerability-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('medium-vulnerability-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('unknown-vulnerability-chip')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
|
@ -23,7 +23,11 @@ const mockImageList = {
|
||||
IsSigned: false,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 7
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -36,7 +40,11 @@ const mockImageList = {
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 2
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
@ -49,7 +57,62 @@ const mockImageList = {
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: ''
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'centos',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'NONE',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'debian',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'MEDIUM',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
Name: 'mysql',
|
||||
Size: '369311301',
|
||||
LastUpdated: '2022-08-23T00:20:40.144281895Z',
|
||||
NewestImage: {
|
||||
Tag: 'latest',
|
||||
Description: '',
|
||||
IsSigned: true,
|
||||
Licenses: '',
|
||||
Vendor: '',
|
||||
Labels: '',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'UNKNOWN',
|
||||
Count: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -79,11 +142,21 @@ describe('Home component', () => {
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<Home />);
|
||||
expect(await screen.findAllByTestId('unverified-icon')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('verified-icon')).toHaveLength(2);
|
||||
expect(await screen.findAllByTestId('verified-icon')).toHaveLength(3);
|
||||
expect(await screen.findAllByTestId('unverified-chip')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('verified-chip')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('renders vulnerability icons', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageList } });
|
||||
render(<Home />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-icon')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('high-vulnerability-icon')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('critical-vulnerability-icon')).toHaveLength(1);
|
||||
expect(await screen.findAllByTestId('none-vulnerability-icon')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should log an error when data can't be fetched", async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
|
@ -19,23 +19,138 @@ jest.mock('react-router-dom', () => ({
|
||||
},
|
||||
useLocation: () => {
|
||||
return mockUseLocationValue;
|
||||
}
|
||||
},
|
||||
useNavigate: () => jest.fn()
|
||||
}));
|
||||
|
||||
const mockRepoDetailsData = {
|
||||
ExpandedRepoInfo: {
|
||||
Manifests: [
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest',
|
||||
Layers: [
|
||||
{
|
||||
Size: '2798889',
|
||||
Digest: '2408cc74d12b6cd092bb8b516ba7d5e290f485d3eb9672efc00f0583730179e8'
|
||||
}
|
||||
]
|
||||
Tag: 'latest'
|
||||
}
|
||||
]
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const mockRepoDetailsNone = {
|
||||
ExpandedRepoInfo: {
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest'
|
||||
}
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test1',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'NONE',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mockRepoDetailsUnknown = {
|
||||
ExpandedRepoInfo: {
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest'
|
||||
}
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test1',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'UNKNOWN',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mockRepoDetailsLow = {
|
||||
ExpandedRepoInfo: {
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest'
|
||||
}
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test1',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mockRepoDetailsMedium = {
|
||||
ExpandedRepoInfo: {
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest'
|
||||
}
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test1',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'MEDIUM',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const mockRepoDetailsHigh = {
|
||||
ExpandedRepoInfo: {
|
||||
Images: [
|
||||
{
|
||||
Digest: '2aa7ff5ca352d4d25fc6548f9930a436aacd64d56b1bd1f9ff4423711b9c8718',
|
||||
Tag: 'latest'
|
||||
}
|
||||
],
|
||||
Summary: {
|
||||
Name: 'test1',
|
||||
NewestImage: {
|
||||
RepoName: 'mongo',
|
||||
IsSigned: true,
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -52,6 +167,33 @@ describe('Repo details component', () => {
|
||||
expect(await screen.findByText('test')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders vulnerability chips', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsData } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('critical-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsNone } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('none-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsUnknown } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('unknown-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsLow } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsMedium } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('medium-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockRepoDetailsHigh } });
|
||||
render(<RepoDetails />);
|
||||
expect(await screen.findAllByTestId('high-vulnerability-chip')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should log error if data can't be fetched", async () => {
|
||||
jest.spyOn(api, 'get').mockRejectedValue({ status: 500, data: {} });
|
||||
const error = jest.spyOn(console, 'error').mockImplementation(() => {});
|
||||
|
@ -9,19 +9,35 @@ const mockDependenciesList = {
|
||||
BaseImageList: [
|
||||
{
|
||||
RepoName: 'project-stacker/c3/static-ubuntu-amd64',
|
||||
Tag: 'tag1'
|
||||
Tag: 'tag1',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag2',
|
||||
Tag: 'tag2'
|
||||
Tag: 'tag2',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag3',
|
||||
Tag: 'tag3'
|
||||
Tag: 'tag3',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 7
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag4',
|
||||
Tag: 'tag4'
|
||||
Tag: 'tag4',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -9,19 +9,35 @@ const mockDependentsList = {
|
||||
DerivedImageList: [
|
||||
{
|
||||
RepoName: 'project-stacker/c3/static-ubuntu-amd64',
|
||||
Tag: 'tag1'
|
||||
Tag: 'tag1',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag2',
|
||||
Tag: 'tag2'
|
||||
Tag: 'tag2',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 2
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag3',
|
||||
Tag: 'tag3'
|
||||
Tag: 'tag3',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 5
|
||||
}
|
||||
},
|
||||
{
|
||||
RepoName: 'tag4',
|
||||
Tag: 'tag4'
|
||||
Tag: 'tag4',
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 3
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ const mockImage = {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'CRITICAL',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS',
|
||||
History: [
|
||||
{
|
||||
@ -57,6 +61,106 @@ const mockImage = {
|
||||
}
|
||||
};
|
||||
|
||||
const mockImageNone = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'NONE',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
const mockImageUnknown = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'UNKNOWN',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
const mockImageLow = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'LOW',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
const mockImageMedium = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'MEDIUM',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
const mockImageHigh = {
|
||||
Image: {
|
||||
RepoName: 'centos',
|
||||
Tag: '8',
|
||||
Digest: 'sha256:63a795ca90aa6e7cca60941e826810a4cd0a2e73ea02bf458241df2a5c973e29',
|
||||
LastUpdated: '2020-12-08T00:22:52.526672082Z',
|
||||
Size: '75183423',
|
||||
ConfigDigest: 'sha256:8dd57e171a61368ffcfde38045ddb6ed74a32950c271c1da93eaddfb66a77e78',
|
||||
Platform: {
|
||||
Os: 'linux',
|
||||
Arch: 'amd64'
|
||||
},
|
||||
Vulnerabilities: {
|
||||
MaxSeverity: 'HIGH',
|
||||
Count: 10
|
||||
},
|
||||
Vendor: 'CentOS'
|
||||
}
|
||||
};
|
||||
|
||||
// mock clipboard copy fn
|
||||
const mockCopyToClipboard = jest.fn();
|
||||
Object.assign(navigator, {
|
||||
@ -104,6 +208,7 @@ describe('Tags details', () => {
|
||||
render(<TagDetails />);
|
||||
await waitFor(() => expect(error).toBeCalledTimes(1));
|
||||
});
|
||||
|
||||
it('should show tag details metadata', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
@ -111,6 +216,33 @@ describe('Tags details', () => {
|
||||
expect(await screen.findByTestId('tagDetailsMetadata-container')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders vulnerability chips', async () => {
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImage } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('critical-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageNone } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('none-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageUnknown } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('unknown-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageLow } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('low-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageMedium } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('medium-vulnerability-chip')).toHaveLength(1);
|
||||
// @ts-ignore
|
||||
jest.spyOn(api, 'get').mockResolvedValue({ status: 200, data: { data: mockImageHigh } });
|
||||
render(<TagDetails />);
|
||||
expect(await screen.findAllByTestId('high-vulnerability-chip')).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should copy the pull string to clipboard', async () => {
|
||||
jest
|
||||
.spyOn(api, 'get')
|
||||
|
12
src/api.js
12
src/api.js
@ -63,11 +63,11 @@ const endpoints = {
|
||||
repoList: ({ pageNumber = 1, pageSize = 15 } = {}) =>
|
||||
`/v2/_zot/ext/search?query={RepoListWithNewestImage(requestedPage: {limit:${pageSize} offset:${
|
||||
(pageNumber - 1) * pageSize
|
||||
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Description Licenses Logo Title Source IsSigned Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
|
||||
}}){Name LastUpdated Size Platforms {Os Arch} NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description Licenses Logo Title Source IsSigned Documentation History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}} Vendor Labels} DownloadCount}}`,
|
||||
detailedRepoInfo: (name) =>
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Tag LastUpdated Vendor Size Platform {Os Arch} } Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName Layers {Size Digest} Digest Tag Logo Title Documentation DownloadCount Source Description Licenses History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
`/v2/_zot/ext/search?query={ExpandedRepoInfo(repo:"${name}"){Images {Digest Vulnerabilities {MaxSeverity Count} Tag LastUpdated Vendor Size Platform {Os Arch}} Summary {Name LastUpdated Size Platforms {Os Arch} Vendors NewestImage {RepoName IsSigned Vulnerabilities {MaxSeverity Count} Layers {Size Digest} Digest Tag Logo Title Documentation DownloadCount Source Description Licenses History {Layer {Size Digest} HistoryDescription {Created CreatedBy Author Comment EmptyLayer}}}}}}`,
|
||||
detailedImageInfo: (name, tag) =>
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName Tag Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor Licenses Logo}}`,
|
||||
`/v2/_zot/ext/search?query={Image(image: "${name}:${tag}"){RepoName IsSigned Vulnerabilities {MaxSeverity Count} Tag Digest LastUpdated Size ConfigDigest Platform {Os Arch} Vendor Licenses Logo}}`,
|
||||
vulnerabilitiesForRepo: (name) =>
|
||||
`/v2/_zot/ext/search?query={CVEListForImage(image: "${name}"){Tag, CVEList {Id Title Description Severity PackageList {Name InstalledVersion FixedVersion}}}}`,
|
||||
layersDetailsForImage: (name) =>
|
||||
@ -75,9 +75,9 @@ const endpoints = {
|
||||
imageListWithCVEFixed: (cveId, repoName) =>
|
||||
`/v2/_zot/ext/search?query={ImageListWithCVEFixed(id:"${cveId}", image:"${repoName}") {Tag}}`,
|
||||
dependsOnForImage: (name) =>
|
||||
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned}}`,
|
||||
`/v2/_zot/ext/search?query={BaseImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}`,
|
||||
isDependentOnForImage: (name) =>
|
||||
`/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned}}`,
|
||||
`/v2/_zot/ext/search?query={DerivedImageList(image: "${name}"){RepoName Tag Description Digest Vendor DownloadCount LastUpdated Size Platform {Os Arch} IsSigned Vulnerabilities {MaxSeverity Count}}}`,
|
||||
globalSearch: ({ searchQuery = '""', pageNumber = 1, pageSize = 15, filter = {} }) => {
|
||||
const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`;
|
||||
const paginationParam = `requestedPage: {limit:${pageSize} offset:${(pageNumber - 1) * pageSize}}`;
|
||||
@ -87,7 +87,7 @@ const endpoints = {
|
||||
if (filter.HasToBeSigned) filterParam += ` HasToBeSigned: ${filter.HasToBeSigned}`;
|
||||
filterParam += '}';
|
||||
if (Object.keys(filter).length === 0) filterParam = '';
|
||||
return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Description IsSigned Logo Licenses Vendor Labels } DownloadCount}}}`;
|
||||
return `/v2/_zot/ext/search?query={GlobalSearch(${searchParam}, ${paginationParam} ${filterParam}) {Repos {Name LastUpdated Size Platforms { Os Arch } NewestImage { Tag Vulnerabilities {MaxSeverity Count} Description IsSigned Logo Licenses Vendor Labels } DownloadCount}}}`;
|
||||
},
|
||||
imageSuggestions: ({ searchQuery = '""', pageNumber = 1, pageSize = 15 }) => {
|
||||
const searchParam = searchQuery !== '' ? `query:"${searchQuery}"` : `query:""`;
|
||||
|
@ -78,8 +78,8 @@ function DependsOn(props) {
|
||||
.get(`${host()}${endpoints.dependsOnForImage(name)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let images = response.data.data.BaseImageList;
|
||||
setImages(images);
|
||||
let imagesData = response.data.data.BaseImageList;
|
||||
setImages(imagesData);
|
||||
}
|
||||
setIsLoading(false);
|
||||
})
|
||||
@ -105,6 +105,7 @@ function DependsOn(props) {
|
||||
size={dependence.Size}
|
||||
digest={dependence.Digest}
|
||||
key={index}
|
||||
vulnerabiltySeverity={dependence.Vulnerabilities?.MaxSeverity}
|
||||
lastUpdated={dependence.LastUpdated}
|
||||
/>
|
||||
);
|
||||
|
@ -113,6 +113,8 @@ function Explore() {
|
||||
vendor={item.vendor}
|
||||
platforms={item.platforms}
|
||||
key={index}
|
||||
vulnerabiltySeverity={item.vulnerabiltySeverity}
|
||||
vulnerabilityCount={item.vulnerabilityCount}
|
||||
lastUpdated={item.lastUpdated}
|
||||
logo={item.logo}
|
||||
/>
|
||||
|
@ -96,7 +96,14 @@ function Home() {
|
||||
homeData.slice(0, 4).map((item, index) => {
|
||||
return (
|
||||
<Grid item xs={3} key={index}>
|
||||
<PreviewCard name={item.name} lastUpdated={item.lastUpdated} isSigned={item.isSigned} logo={item.logo} />
|
||||
<PreviewCard
|
||||
name={item.name}
|
||||
lastUpdated={item.lastUpdated}
|
||||
isSigned={item.isSigned}
|
||||
vulnerabiltySeverity={item.vulnerabiltySeverity}
|
||||
vulnerabilityCount={item.vulnerabilityCount}
|
||||
logo={item.logo}
|
||||
/>
|
||||
</Grid>
|
||||
);
|
||||
})
|
||||
@ -140,6 +147,8 @@ function Home() {
|
||||
vendor={item.vendor}
|
||||
platforms={item.platforms}
|
||||
key={index}
|
||||
vulnerabiltySeverity={item.vulnerabiltySeverity}
|
||||
vulnerabilityCount={item.vulnerabilityCount}
|
||||
lastUpdated={item.lastUpdated}
|
||||
logo={item.logo}
|
||||
/>
|
||||
|
@ -78,8 +78,8 @@ function IsDependentOn(props) {
|
||||
.get(`${host()}${endpoints.isDependentOnForImage(name)}`, abortController.signal)
|
||||
.then((response) => {
|
||||
if (response.data && response.data.data) {
|
||||
let images = response.data.data.DerivedImageList;
|
||||
setImages(images);
|
||||
let imageData = response.data.data.DerivedImageList;
|
||||
setImages(imageData);
|
||||
}
|
||||
setIsLoading(false);
|
||||
})
|
||||
@ -105,6 +105,7 @@ function IsDependentOn(props) {
|
||||
size={dependence.Size}
|
||||
digest={dependence.Digest}
|
||||
key={index}
|
||||
vulnerabiltySeverity={dependence.Vulnerabilities?.MaxSeverity}
|
||||
lastUpdated={dependence.LastUpdated}
|
||||
/>
|
||||
);
|
||||
|
@ -12,6 +12,8 @@ import repocube4 from '../assets/repocube-4.png';
|
||||
//icons
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
||||
import PestControlIcon from '@mui/icons-material/PestControl';
|
||||
import { isEmpty } from 'lodash';
|
||||
//import GppMaybeOutlinedIcon from '@mui/icons-material/GppMaybeOutlined';
|
||||
|
||||
@ -67,31 +69,118 @@ const useStyles = makeStyles(() => ({
|
||||
}
|
||||
}));
|
||||
|
||||
//function that returns a random element from an array
|
||||
// function getRandom (list) {
|
||||
// return list[Math.floor((Math.random()*list.length))];
|
||||
// }
|
||||
|
||||
function PreviewCard(props) {
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigate();
|
||||
const { name, isSigned, logo } = props;
|
||||
const { name, isSigned, vulnerabiltySeverity, vulnerabilityCount, logo } = props;
|
||||
|
||||
const goToDetails = () => {
|
||||
navigate(`/image/${encodeURIComponent(name)}`);
|
||||
};
|
||||
|
||||
// const vulnerabilityCheck = () => {
|
||||
// const noneVulnerability = <PestControlOutlinedIcon sx={{ color: "#43A047!important", padding:"0.2rem", background: "#E8F5E9", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
// const unknownVulnerability = <PestControlOutlinedIcon sx={{ color: "#52637A!important", padding:"0.2rem", background: "#ECEFF1", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
// const lowVulnerability = <PestControlOutlinedIcon sx={{ color: "#FB8C00!important", padding:"0.2rem", background: "#FFF3E0", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
// const mediumVulnerability = <PestControlIcon sx={{ color: "#FB8C00!important", padding:"0.2rem", background: "#FFF3E0", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
// const highVulnerability = <PestControlOutlinedIcon sx={{ color: "#E53935!important", padding:"0.2rem", background: "#FEEBEE", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
// const criticalVulnerability = <PestControlIcon sx={{ color: "#E53935!important", padding:"0.2rem", background: "#FEEBEE", borderRadius: "1rem", height:"1.5rem", width:"1.6rem" }} />;
|
||||
const vulnerabilityCheck = () => {
|
||||
const noneVulnerability = (
|
||||
<PestControlOutlinedIcon
|
||||
sx={{
|
||||
color: '#43A047!important',
|
||||
padding: '0.2rem',
|
||||
background: '#E8F5E9',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="none-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
const unknownVulnerability = (
|
||||
<PestControlOutlinedIcon
|
||||
sx={{
|
||||
color: '#52637A!important',
|
||||
padding: '0.2rem',
|
||||
background: '#ECEFF1',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="unknown-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
const lowVulnerability = (
|
||||
<PestControlOutlinedIcon
|
||||
sx={{
|
||||
color: '#FB8C00!important',
|
||||
padding: '0.2rem',
|
||||
background: '#FFF3E0',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="low-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
const mediumVulnerability = (
|
||||
<PestControlIcon
|
||||
sx={{
|
||||
color: '#FB8C00!important',
|
||||
padding: '0.2rem',
|
||||
background: '#FFF3E0',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="medium-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
const highVulnerability = (
|
||||
<PestControlOutlinedIcon
|
||||
sx={{
|
||||
color: '#E53935!important',
|
||||
padding: '0.2rem',
|
||||
background: '#FEEBEE',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="high-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
const criticalVulnerability = (
|
||||
<PestControlIcon
|
||||
sx={{
|
||||
color: '#E53935!important',
|
||||
padding: '0.2rem',
|
||||
background: '#FEEBEE',
|
||||
borderRadius: '1rem',
|
||||
height: '1.5rem',
|
||||
width: '1.6rem'
|
||||
}}
|
||||
data-testid="critical-vulnerability-icon"
|
||||
/>
|
||||
);
|
||||
|
||||
// const arrVulnerability = [noneVulnerability, unknownVulnerability, lowVulnerability, mediumVulnerability, highVulnerability, criticalVulnerability]
|
||||
// return(getRandom(arrVulnerability));
|
||||
// }
|
||||
let result;
|
||||
switch (vulnerabiltySeverity) {
|
||||
case 'NONE':
|
||||
result = noneVulnerability;
|
||||
break;
|
||||
case 'LOW':
|
||||
result = lowVulnerability;
|
||||
break;
|
||||
case 'MEDIUM':
|
||||
result = mediumVulnerability;
|
||||
break;
|
||||
case 'HIGH':
|
||||
result = highVulnerability;
|
||||
break;
|
||||
case 'CRITICAL':
|
||||
result = criticalVulnerability;
|
||||
break;
|
||||
default:
|
||||
result = unknownVulnerability;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const signatureCheck = () => {
|
||||
const unverifiedSignature = (
|
||||
@ -134,7 +223,7 @@ function PreviewCard(props) {
|
||||
<CardContent className={classes.content}>
|
||||
<Grid container spacing={1}>
|
||||
<Grid container item xs={12}>
|
||||
<Stack direction="row" spacing={3} sx={{ display: 'flex', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<Stack direction="row" spacing={4} sx={{ display: 'flex', alignItems: 'left', flexWrap: 'wrap' }}>
|
||||
<CardMedia
|
||||
classes={{
|
||||
root: classes.media,
|
||||
@ -152,7 +241,7 @@ function PreviewCard(props) {
|
||||
size: '1.5rem',
|
||||
lineHeight: '2rem',
|
||||
color: '#220052',
|
||||
width: '8rem',
|
||||
width: '5rem',
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis'
|
||||
@ -161,8 +250,12 @@ function PreviewCard(props) {
|
||||
{name}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
{/* {vulnerabilityCheck()} */}
|
||||
{signatureCheck()}
|
||||
<Stack direction="row" spacing={0.5} sx={{ marginLeft: 'auto', marginRight: 0 }}>
|
||||
<Tooltip title={!isNaN(vulnerabilityCount) ? vulnerabilityCount : ''} placement="top">
|
||||
{vulnerabilityCheck()}
|
||||
</Tooltip>
|
||||
{signatureCheck()}
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Grid>
|
||||
<Grid item xs={12} mt={2}></Grid>
|
||||
|
@ -18,6 +18,8 @@ import repocube4 from '../assets/repocube-4.png';
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import { Markdown } from 'utilities/MarkdowntojsxWrapper';
|
||||
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
||||
import PestControlIcon from '@mui/icons-material/PestControl';
|
||||
import { isEmpty } from 'lodash';
|
||||
|
||||
// temporary utility to get image
|
||||
@ -92,23 +94,120 @@ const useStyles = makeStyles(() => ({
|
||||
function RepoCard(props) {
|
||||
const classes = useStyles();
|
||||
const navigate = useNavigate();
|
||||
const { name, vendor, platforms, description, downloads, isSigned, lastUpdated, version, logo } = props;
|
||||
const {
|
||||
name,
|
||||
vendor,
|
||||
platforms,
|
||||
description,
|
||||
downloads,
|
||||
isSigned,
|
||||
lastUpdated,
|
||||
logo,
|
||||
version,
|
||||
vulnerabiltySeverity,
|
||||
vulnerabilityCount
|
||||
} = props;
|
||||
|
||||
const goToDetails = () => {
|
||||
navigate(`/image/${encodeURIComponent(name)}`);
|
||||
};
|
||||
|
||||
// const vulnerabilityCheck = () => {
|
||||
// const noneVulnerability = <Chip label="None Vulnerability" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
|
||||
// const unknownVulnerability = <Chip label="Unknown Vulnerability" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
||||
// const lowVulnerability = <Chip label="Low Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
||||
// const mediumVulnerability = <Chip label="Medium Vulnerability" sx={{backgroundColor: "#FFF3E0",color: "#FB8C00",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#FB8C00!important" }} />}/>;
|
||||
// const highVulnerability = <Chip label="High Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||
// const criticalVulnerability = <Chip label="Critical Vulnerability" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <PestControlIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||
const vulnerabilityCheck = () => {
|
||||
const noneVulnerability = (
|
||||
<Chip
|
||||
label="None Vulnerability"
|
||||
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#388E3C!important' }} />}
|
||||
data-testid="none-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const unknownVulnerability = (
|
||||
<Chip
|
||||
label="Unknown Vulnerability"
|
||||
sx={{ backgroundColor: '#ECEFF1', color: '#52637A', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#52637A!important' }} />}
|
||||
data-testid="unknown-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const lowVulnerability = (
|
||||
<Chip
|
||||
label="Low Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="low-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const mediumVulnerability = (
|
||||
<Chip
|
||||
label="Medium Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="medium-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const highVulnerability = (
|
||||
<Chip
|
||||
label="High Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="high-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const criticalVulnerability = (
|
||||
<Chip
|
||||
label="Critical Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="critical-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
let result;
|
||||
switch (vulnerabiltySeverity) {
|
||||
case 'NONE':
|
||||
result = noneVulnerability;
|
||||
break;
|
||||
case 'LOW':
|
||||
result = lowVulnerability;
|
||||
break;
|
||||
case 'MEDIUM':
|
||||
result = mediumVulnerability;
|
||||
break;
|
||||
case 'HIGH':
|
||||
result = highVulnerability;
|
||||
break;
|
||||
case 'CRITICAL':
|
||||
result = criticalVulnerability;
|
||||
break;
|
||||
default:
|
||||
result = unknownVulnerability;
|
||||
}
|
||||
|
||||
// const arrVulnerability = [noneVulnerability, unknownVulnerability, lowVulnerability, mediumVulnerability, highVulnerability, criticalVulnerability]
|
||||
// return(getRandom(arrVulnerability));
|
||||
// };
|
||||
return result;
|
||||
};
|
||||
|
||||
const signatureCheck = () => {
|
||||
const unverifiedSignature = (
|
||||
@ -202,7 +301,9 @@ function RepoCard(props) {
|
||||
{name}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
{/* {vulnerabilityCheck()} */}
|
||||
<Tooltip title={!isNaN(vulnerabilityCount) ? vulnerabilityCount : ''} placement="top">
|
||||
{vulnerabilityCheck()}
|
||||
</Tooltip>
|
||||
{signatureCheck()}
|
||||
{/* <Chip label="Verified licensee" sx={{ backgroundColor: "#E8F5E9", color: "#388E3C" }} variant="filled" onDelete={() => { return }} deleteIcon={vulnerabilityCheck()} /> */}
|
||||
</Stack>
|
||||
|
@ -7,7 +7,7 @@ import { api, endpoints } from '../api';
|
||||
|
||||
// components
|
||||
import Tags from './Tags.jsx';
|
||||
import { Box, Card, CardContent, CardMedia, Chip, Grid, Stack, Tab, Typography } from '@mui/material';
|
||||
import { Box, Card, CardContent, CardMedia, Chip, Grid, Stack, Tab, Tooltip, Typography } from '@mui/material';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host } from '../host';
|
||||
|
||||
@ -17,6 +17,12 @@ import repocube2 from '../assets/repocube-2.png';
|
||||
import repocube3 from '../assets/repocube-3.png';
|
||||
import repocube4 from '../assets/repocube-4.png';
|
||||
import { TabContext, TabList, TabPanel } from '@mui/lab';
|
||||
|
||||
//icons
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
||||
import PestControlIcon from '@mui/icons-material/PestControl';
|
||||
import RepoDetailsMetadata from './RepoDetailsMetadata';
|
||||
import Loading from './Loading';
|
||||
import { isEmpty } from 'lodash';
|
||||
@ -149,6 +155,9 @@ function RepoDetails() {
|
||||
downloads: repoInfo.Summary?.NewestImage?.DownloadCount,
|
||||
overview: repoInfo.Summary?.NewestImage?.Documentation,
|
||||
license: repoInfo.Summary?.NewestImage?.Licenses,
|
||||
vulnerabiltySeverity: repoInfo.Summary?.NewestImage?.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: repoInfo.Summary?.NewestImage?.Vulnerabilities?.Count,
|
||||
isSigned: repoInfo.Summary?.NewestImage?.IsSigned,
|
||||
logo: repoInfo.Summary?.NewestImage?.Logo
|
||||
};
|
||||
setRepoDetailData(imageData);
|
||||
@ -167,6 +176,135 @@ function RepoDetails() {
|
||||
};
|
||||
}, [name]);
|
||||
|
||||
const signatureCheck = () => {
|
||||
const unverifiedSignature = (
|
||||
<Chip
|
||||
label="Unverified Signature"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<GppBadOutlinedIcon sx={{ color: '#E53935!important' }} />}
|
||||
/>
|
||||
);
|
||||
const verifiedSignature = (
|
||||
<Chip
|
||||
label="Verified Signature"
|
||||
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<GppGoodOutlinedIcon sx={{ color: '#388E3C!important' }} />}
|
||||
/>
|
||||
);
|
||||
// @ts-ignore
|
||||
if (repoDetailData.isSigned) {
|
||||
return verifiedSignature;
|
||||
} else {
|
||||
return unverifiedSignature;
|
||||
}
|
||||
};
|
||||
|
||||
const vulnerabilityCheck = () => {
|
||||
const noneVulnerability = (
|
||||
<Chip
|
||||
label="None Vulnerability"
|
||||
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#388E3C!important' }} />}
|
||||
data-testid="none-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const unknownVulnerability = (
|
||||
<Chip
|
||||
label="Unknown Vulnerability"
|
||||
sx={{ backgroundColor: '#ECEFF1', color: '#52637A', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#52637A!important' }} />}
|
||||
data-testid="unknown-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const lowVulnerability = (
|
||||
<Chip
|
||||
label="Low Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="low-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const mediumVulnerability = (
|
||||
<Chip
|
||||
label="Medium Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="medium-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const highVulnerability = (
|
||||
<Chip
|
||||
label="High Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="high-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const criticalVulnerability = (
|
||||
<Chip
|
||||
label="Critical Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="critical-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
let result;
|
||||
// @ts-ignore
|
||||
switch (repoDetailData.vulnerabiltySeverity) {
|
||||
case 'NONE':
|
||||
result = noneVulnerability;
|
||||
break;
|
||||
case 'LOW':
|
||||
result = lowVulnerability;
|
||||
break;
|
||||
case 'MEDIUM':
|
||||
result = mediumVulnerability;
|
||||
break;
|
||||
case 'HIGH':
|
||||
result = highVulnerability;
|
||||
break;
|
||||
case 'CRITICAL':
|
||||
result = criticalVulnerability;
|
||||
break;
|
||||
default:
|
||||
result = unknownVulnerability;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const platformChips = () => {
|
||||
// @ts-ignore
|
||||
const platforms = repoDetailData?.platforms || [];
|
||||
@ -195,6 +333,7 @@ function RepoDetails() {
|
||||
));
|
||||
};
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore
|
||||
const handleTabChange = (event, newValue) => {
|
||||
setSelectedTab(newValue);
|
||||
@ -249,8 +388,16 @@ function RepoDetails() {
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
{name}
|
||||
</Typography>
|
||||
{/* {vulnerabilityCheck()}
|
||||
{signatureCheck()} */}
|
||||
<Tooltip
|
||||
title={
|
||||
// @ts-ignore
|
||||
!isNaN(repoDetailData.vulnerabilityCount) ? repoDetailData.vulnerabilityCount : ''
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
{vulnerabilityCheck()}
|
||||
</Tooltip>
|
||||
{signatureCheck()}
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
|
@ -1,3 +1,4 @@
|
||||
// @ts-nocheck
|
||||
// react global
|
||||
import { useParams } from 'react-router-dom';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
@ -11,6 +12,7 @@ import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardMedia,
|
||||
Chip,
|
||||
Grid,
|
||||
FormControl,
|
||||
IconButton,
|
||||
@ -18,12 +20,19 @@ import {
|
||||
Select,
|
||||
MenuItem,
|
||||
Tab,
|
||||
Tooltip,
|
||||
Typography
|
||||
} from '@mui/material';
|
||||
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
|
||||
import makeStyles from '@mui/styles/makeStyles';
|
||||
import { host, hostRoot } from '../host';
|
||||
|
||||
//icons
|
||||
import GppBadOutlinedIcon from '@mui/icons-material/GppBadOutlined';
|
||||
import GppGoodOutlinedIcon from '@mui/icons-material/GppGoodOutlined';
|
||||
import PestControlOutlinedIcon from '@mui/icons-material/PestControlOutlined';
|
||||
import PestControlIcon from '@mui/icons-material/PestControl';
|
||||
|
||||
// placeholder images
|
||||
import repocube1 from '../assets/repocube-1.png';
|
||||
import repocube2 from '../assets/repocube-2.png';
|
||||
@ -166,6 +175,9 @@ function TagDetails() {
|
||||
vendor: imageInfo.Vendor,
|
||||
history: imageInfo.History,
|
||||
license: imageInfo.Licenses,
|
||||
vulnerabiltySeverity: imageInfo.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: imageInfo.Vulnerabilities?.Count,
|
||||
isSigned: imageInfo.IsSigned,
|
||||
logo: imageInfo.Logo
|
||||
};
|
||||
setImageDetailData(imageData);
|
||||
@ -182,14 +194,134 @@ function TagDetails() {
|
||||
};
|
||||
}, [name, tag]);
|
||||
|
||||
// const signatureCheck = () => {
|
||||
// const unverifiedSignature = <Chip label="Unverified Signature" sx={{backgroundColor: "#FEEBEE",color: "#E53935",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppBadOutlinedIcon sx={{ color: "#E53935!important" }} />}/>;
|
||||
// const untrustedSignature = <Chip label="Untrusted Signature" sx={{backgroundColor: "#ECEFF1",color: "#52637A",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppMaybeOutlinedIcon sx={{ color: "#52637A!important" }} />}/>;
|
||||
// const verifiedSignature = <Chip label="Verified Signature" sx={{backgroundColor: "#E8F5E9",color: "#388E3C",fontSize: "0.8125rem",}} variant="filled" onDelete={() => { return; }} deleteIcon={ <GppGoodOutlinedIcon sx={{ color: "#388E3C!important" }} />}/>;
|
||||
const signatureCheck = () => {
|
||||
const unverifiedSignature = (
|
||||
<Chip
|
||||
label="Unverified Signature"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<GppBadOutlinedIcon sx={{ color: '#E53935!important' }} />}
|
||||
/>
|
||||
);
|
||||
const verifiedSignature = (
|
||||
<Chip
|
||||
label="Verified Signature"
|
||||
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<GppGoodOutlinedIcon sx={{ color: '#388E3C!important' }} />}
|
||||
/>
|
||||
);
|
||||
// @ts-ignore
|
||||
if (imageDetailData.isSigned) {
|
||||
return verifiedSignature;
|
||||
} else {
|
||||
return unverifiedSignature;
|
||||
}
|
||||
};
|
||||
|
||||
// const arrSignature = [unverifiedSignature, untrustedSignature, verifiedSignature]
|
||||
// return(getRandom(arrSignature));
|
||||
// }
|
||||
const vulnerabilityCheck = () => {
|
||||
const noneVulnerability = (
|
||||
<Chip
|
||||
label="None Vulnerability"
|
||||
sx={{ backgroundColor: '#E8F5E9', color: '#388E3C', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#388E3C!important' }} />}
|
||||
data-testid="none-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const unknownVulnerability = (
|
||||
<Chip
|
||||
label="Unknown Vulnerability"
|
||||
sx={{ backgroundColor: '#ECEFF1', color: '#52637A', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#52637A!important' }} />}
|
||||
data-testid="unknown-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const lowVulnerability = (
|
||||
<Chip
|
||||
label="Low Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="low-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const mediumVulnerability = (
|
||||
<Chip
|
||||
label="Medium Vulnerability"
|
||||
sx={{ backgroundColor: '#FFF3E0', color: '#FB8C00', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#FB8C00!important' }} />}
|
||||
data-testid="medium-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const highVulnerability = (
|
||||
<Chip
|
||||
label="High Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlOutlinedIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="high-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
const criticalVulnerability = (
|
||||
<Chip
|
||||
label="Critical Vulnerability"
|
||||
sx={{ backgroundColor: '#FEEBEE', color: '#E53935', fontSize: '0.8125rem' }}
|
||||
variant="filled"
|
||||
onDelete={() => {
|
||||
return;
|
||||
}}
|
||||
deleteIcon={<PestControlIcon sx={{ color: '#E53935!important' }} />}
|
||||
data-testid="critical-vulnerability-chip"
|
||||
/>
|
||||
);
|
||||
let result;
|
||||
// @ts-ignore
|
||||
switch (imageDetailData.vulnerabiltySeverity) {
|
||||
case 'NONE':
|
||||
result = noneVulnerability;
|
||||
break;
|
||||
case 'LOW':
|
||||
result = lowVulnerability;
|
||||
break;
|
||||
case 'MEDIUM':
|
||||
result = mediumVulnerability;
|
||||
break;
|
||||
case 'HIGH':
|
||||
result = highVulnerability;
|
||||
break;
|
||||
case 'CRITICAL':
|
||||
result = criticalVulnerability;
|
||||
break;
|
||||
default:
|
||||
result = unknownVulnerability;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const getPlatform = () => {
|
||||
// @ts-ignore
|
||||
@ -225,15 +357,25 @@ function TagDetails() {
|
||||
image={
|
||||
// @ts-ignore
|
||||
// eslint-disable-next-line prettier/prettier
|
||||
!isEmpty(imageDetailData?.logo) ? `data:image/ png;base64, ${imageDetailData?.logo}` : randomImage()
|
||||
!isEmpty(imageDetailData?.logo)
|
||||
? `data:image/ png;base64, ${imageDetailData?.logo}`
|
||||
: randomImage()
|
||||
}
|
||||
alt="icon"
|
||||
/>
|
||||
<Typography variant="h3" className={classes.repoName}>
|
||||
{name}:{tag}
|
||||
</Typography>
|
||||
{/* {vulnerabilityCheck()}
|
||||
{signatureCheck()} */}
|
||||
<Tooltip
|
||||
title={
|
||||
// @ts-ignore
|
||||
!isNaN(imageDetailData.vulnerabilityCount) ? imageDetailData.vulnerabilityCount : ''
|
||||
}
|
||||
placement="top"
|
||||
>
|
||||
{vulnerabilityCheck()}
|
||||
</Tooltip>
|
||||
{signatureCheck()}
|
||||
{/* <BookmarkIcon sx={{color:"#52637A"}}/> */}
|
||||
</Stack>
|
||||
<Typography
|
||||
|
@ -11,7 +11,9 @@ const mapToRepo = (responseRepo) => {
|
||||
vendor: responseRepo.NewestImage?.Vendor,
|
||||
logo: responseRepo.NewestImage?.Logo,
|
||||
lastUpdated: responseRepo.LastUpdated,
|
||||
downloads: responseRepo.DownloadCount
|
||||
downloads: responseRepo.DownloadCount,
|
||||
vulnerabiltySeverity: responseRepo.NewestImage?.Vulnerabilities?.MaxSeverity,
|
||||
vulnerabilityCount: responseRepo.NewestImage?.Vulnerabilities?.Count
|
||||
};
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user