Skip to content

Commit

Permalink
fix(quay): fix the security scan sorting in quay plugin (#2125)
Browse files Browse the repository at this point in the history
* fix(quay): fix the security scan sorting in quay plugin

* fix playwright test
  • Loading branch information
karthikjeeyar authored Sep 12, 2024
1 parent 9e239c5 commit c017e5e
Show file tree
Hide file tree
Showing 8 changed files with 357 additions and 9 deletions.
46 changes: 46 additions & 0 deletions plugins/quay/dev/__data__/security_vulnerabilities.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
Layer,
SecurityDetailsResponse,
VulnerabilitySeverity,
} from '../../src/types';
Expand Down Expand Up @@ -327,3 +328,48 @@ export const securityDetails: SecurityDetailsResponse = {
},
},
};

export const v1securityDetails: SecurityDetailsResponse = {
...securityDetails,
status: 'unsupported',
data: {
...securityDetails.data,
Layer: {
...(securityDetails?.data?.Layer ?? {}),
Features: [],
} as Layer,
},
};

export const v2securityDetails: SecurityDetailsResponse = {
...securityDetails,
status: 'queued',
data: {
...securityDetails.data,
Layer: {
...(securityDetails?.data?.Layer ?? {}),
Features: securityDetails.data?.Layer?.Features?.slice(0, 5) ?? [],
} as Layer,
},
};

export const v3securityDetails: SecurityDetailsResponse = {
...securityDetails,
data: {
...securityDetails.data,
Layer: {
...(securityDetails?.data?.Layer ?? {}),
Features: [],
} as Layer,
},
};
export const v4securityDetails: SecurityDetailsResponse = {
...securityDetails,
data: {
...securityDetails.data,
Layer: {
...(securityDetails?.data?.Layer ?? {}),
Features: securityDetails.data?.Layer?.Features?.slice(0, 5) ?? [],
} as Layer,
},
};
40 changes: 40 additions & 0 deletions plugins/quay/dev/__data__/tags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,46 @@ export const tags = {
size: 275862608,
last_modified: 'Tue, 06 Feb 2024 09:39:24 -0000',
},
{
name: 'v4',
reversion: false,
start_ts: 1707212364,
manifest_digest:
'sha256:29c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775d',
is_manifest_list: false,
size: 265862608,
last_modified: 'Tue, 06 Feb 2024 09:39:24 -0000',
},
{
name: 'v3',
reversion: false,
start_ts: 1707212364,
manifest_digest:
'sha256:79c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775d',
is_manifest_list: false,
size: 265862608,
last_modified: 'Tue, 06 Feb 2024 09:39:24 -0000',
},
{
name: 'v2',
reversion: false,
start_ts: 1707212364,
manifest_digest:
'sha256:89c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775e',
is_manifest_list: false,
size: 235862608,
last_modified: 'Tue, 06 Feb 2024 09:39:24 -0000',
},
{
name: 'v1',
reversion: false,
start_ts: 1707212364,
manifest_digest:
'sha256:99c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775f',
is_manifest_list: false,
size: 225862608,
last_modified: 'Tue, 06 Feb 2024 09:39:24 -0000',
},
],
page: 1,
has_additional: false,
Expand Down
37 changes: 35 additions & 2 deletions plugins/quay/dev/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ import { quayApiRef, QuayApiV1 } from '../src/api';
import { QuayPage, quayPlugin } from '../src/plugin';
import { labels } from './__data__/labels';
import { manifestDigest } from './__data__/manifest_digest';
import { securityDetails } from './__data__/security_vulnerabilities';
import {
securityDetails,
v1securityDetails,
v2securityDetails,
v3securityDetails,
v4securityDetails,
} from './__data__/security_vulnerabilities';
import { tags } from './__data__/tags';

const mockEntity: Entity = {
Expand Down Expand Up @@ -45,7 +51,34 @@ export class MockQuayApiClient implements QuayApiV1 {
return manifestDigest;
}

async getSecurityDetails() {
async getSecurityDetails(_: string, __: string, digest: string) {
if (
digest ===
'sha256:79c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775d'
) {
return v3securityDetails;
}

if (
digest ===
'sha256:89c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775e'
) {
return v2securityDetails;
}
if (
digest ===
'sha256:99c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775f'
) {
return v1securityDetails;
}

if (
digest ===
'sha256:29c96c750aa532d92d9cb56cad59159b7cc26b10e39ff4a895c28345d2cd775d'
) {
return v4securityDetails;
}

return securityDetails;
}
}
Expand Down
14 changes: 11 additions & 3 deletions plugins/quay/src/components/QuayRepository/tableHeading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Link, Progress, TableColumn } from '@backstage/core-components';
import { Tooltip } from '@material-ui/core';
import makeStyles from '@material-ui/core/styles/makeStyles';

import { vulnerabilitySummary } from '../../lib/utils';
import { securityScanComparator, vulnerabilitySummary } from '../../lib/utils';
import type { QuayTagData } from '../../types';

export const columns: TableColumn<QuayTagData>[] = [
Expand Down Expand Up @@ -52,10 +52,18 @@ export const columns: TableColumn<QuayTagData>[] = [

const tagManifest = rowData.manifest_digest_raw;
const retStr = vulnerabilitySummary(rowData.securityDetails);
return <Link to={`tag/${tagManifest}`}>{retStr}</Link>;
return (
<Link
data-testid={`${rowData.name}-security-scan`}
to={`tag/${tagManifest}`}
>
{retStr}
</Link>
);
},
id: 'securityScan',
sorting: false,
customSort: (a: QuayTagData, b: QuayTagData) =>
securityScanComparator(a, b),
},
{
title: 'Size',
Expand Down
131 changes: 130 additions & 1 deletion plugins/quay/src/lib/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import {
securityDetails,
v1securityDetails,
v2securityDetails,
v3securityDetails,
} from '../../dev/__data__/security_vulnerabilities';
import { tags } from '../../dev/__data__/tags';
import { Layer, VulnerabilitySeverity } from '../types';
import { SEVERITY_COLORS, vulnerabilitySummary } from './utils';
import {
securityScanComparator,
SEVERITY_COLORS,
vulnerabilitySummary,
} from './utils';
import { mockLayer } from './utils.data';

describe('SEVERITY_COLORS', () => {
Expand Down Expand Up @@ -45,3 +56,121 @@ describe('vulnerabilitySummary', () => {
expect(result).toMatch('High: 3, Medium: 2, Low: 1');
});
});

describe('compareSecurityScans', () => {
const { tags: tagArray } = tags;

const data = [
{
...tagArray[0],
securityStatus: 'scanned',
securityDetails: mockLayer,
},
{
...tagArray[0],
name: 'stable',
securityStatus: 'scanned',
securityDetails: securityDetails?.data?.Layer,
},
{
...tagArray[1],
securityStatus: 'scanned',
securityDetails: v3securityDetails?.data?.Layer,
},
{
...tagArray[2],
securityStatus: 'scanned',
securityDetails: {
...securityDetails?.data?.Layer,
Features: [],
},
},
{
...tagArray[3],
securityStatus: 'queued',
securityDetails: v2securityDetails?.data?.Layer,
},
{
...tagArray[4],
securityStatus: 'unsupported',
securityDetails: v1securityDetails?.data?.Layer,
},
] as any[];

it('should sort security scan values in the ascending order', () => {
const expected = [
'latest-linux-arm64', // High: 3, Medium: 2, Low: 1 ; High value
'stable', // High: 2, Medium: 2, Low: 1 ; High value
'v4', // Medium: 1; No High, but has Medium and Low
'v3', // Passed
'v2', // Queued;
'v1', // Unsupported
];

const names = data
.sort((a, b) => securityScanComparator(a, b, 'asc'))
.map(tag => tag.name);
expect(names).toEqual(expected);
});
it('should sort security scan values in the descending order', () => {
const expected = [
'v1', // Unsupported
'v2', // Queued;
'v4', // Passed
'v3', // Medium: 1; No High, but has Medium and Low
'stable', // High: 2, Medium: 2, Low: 1 ; High value
'latest-linux-arm64', // High: 3, Medium: 2, Low: 1 ; High value
];

const names = data
.sort((a, b) => securityScanComparator(a, b, 'desc'))
.map(tag => tag.name);
expect(names).toEqual(expected);
});

it('should not perform sort on the scanning row', () => {
const mockData = [
{
...tagArray[0],
name: 'v1beta',
securityStatus: 'scanning',
},
...data,
];
const expected = [
'v1beta', // Scanning; Show loading indicator in UI.
'v1', // Unsupported
'v2', // Queued;
'v4', // Passed
'v3', // Medium: 1; No High, but has Medium and Low
'stable', // High: 2, Medium: 2, Low: 1 ; High value
'latest-linux-arm64', // High: 3, Medium: 2, Low: 1 ; High value
];

// Scanning row should not change the order
const names = mockData
.sort((a, b) => securityScanComparator(a, b, 'desc'))
.map(tag => tag.name);
expect(names).toEqual(expected);

const mockData1 = [
data[0], // v1
{
...tagArray[0],
name: 'v1beta',
securityStatus: 'scanning',
},
data[1], // v2
];
const expectedNames = [
'v1', // Unsupported
'v1beta', // Scanning; Show loading indicator in UI.
'v2', // Queued;
];

const tagNames = mockData1
.sort((a, b) => securityScanComparator(a, b, 'desc'))
.map(tag => tag.name);
expect(tagNames).toEqual(expectedNames);
});
});
Loading

0 comments on commit c017e5e

Please sign in to comment.