Commit 387a8d04 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '337882-add-vulnerability-list-cluster-field' into 'master'

Add cluster column to operational vulnerability

See merge request gitlab-org/gitlab!82631
parents 4014e3a3 4cb50f84
...@@ -96,7 +96,7 @@ export default { ...@@ -96,7 +96,7 @@ export default {
this.graphqlFilters.clusterAgentId = [this.clusterAgentId]; this.graphqlFilters.clusterAgentId = [this.clusterAgentId];
}, },
}, },
fieldsToShow: FIELD_PRESETS[REPORT_TAB.OPERATIONAL], fieldsToShow: FIELD_PRESETS[REPORT_TAB.AGENT],
filtersToShow: FILTER_PRESETS[REPORT_TAB.OPERATIONAL], filtersToShow: FILTER_PRESETS[REPORT_TAB.OPERATIONAL],
i18n: { ALERT_MESSAGE, MODAL_MESSAGE, MODAL_TITLE }, i18n: { ALERT_MESSAGE, MODAL_MESSAGE, MODAL_TITLE },
MODAL_ACTIONS, MODAL_ACTIONS,
......
...@@ -11,6 +11,7 @@ import { REPORT_TYPES_NO_CLUSTER_IMAGE } from 'ee/security_dashboard/store/const ...@@ -11,6 +11,7 @@ import { REPORT_TYPES_NO_CLUSTER_IMAGE } from 'ee/security_dashboard/store/const
import { REPORT_TYPE_CLUSTER_IMAGE_SCANNING } from '~/vue_shared/security_reports/constants'; import { REPORT_TYPE_CLUSTER_IMAGE_SCANNING } from '~/vue_shared/security_reports/constants';
export const REPORT_TAB = { export const REPORT_TAB = {
AGENT: 'AGENT',
DEVELOPMENT: 'DEVELOPMENT', DEVELOPMENT: 'DEVELOPMENT',
OPERATIONAL: 'OPERATIONAL', OPERATIONAL: 'OPERATIONAL',
DEVELOPMENT_PROJECT: 'DEVELOPMENT_PROJECT', DEVELOPMENT_PROJECT: 'DEVELOPMENT_PROJECT',
...@@ -56,10 +57,16 @@ export const FIELDS = { ...@@ -56,10 +57,16 @@ export const FIELDS = {
class: 'scanner', class: 'scanner',
sortable: true, sortable: true,
}, },
CLUSTER: {
key: 'cluster',
label: s__('Vulnerability|Cluster'),
thClass: 'gl-w-15p',
class: 'cluster gl-word-break-all',
},
ACTIVITY: { ACTIVITY: {
key: 'activity', key: 'activity',
label: s__('Vulnerability|Activity'), label: s__('Vulnerability|Activity'),
thClass: 'gl-text-right ', thClass: 'gl-text-right',
class: 'activity', class: 'activity',
}, },
}; };
...@@ -73,24 +80,15 @@ export const FILTERS = { ...@@ -73,24 +80,15 @@ export const FILTERS = {
PROJECT: projectFilter, PROJECT: projectFilter,
}; };
const BASE_FIELDS = {
START: [FIELDS.DETECTED, FIELDS.STATUS, FIELDS.SEVERITY, FIELDS.DESCRIPTION],
END: [FIELDS.ACTIVITY],
};
export const FIELD_PRESETS = { export const FIELD_PRESETS = {
DEVELOPMENT: [ DEVELOPMENT: [...BASE_FIELDS.START, FIELDS.IDENTIFIER, FIELDS.TOOL, ...BASE_FIELDS.END],
FIELDS.DETECTED, OPERATIONAL: [...BASE_FIELDS.START, FIELDS.CLUSTER, ...BASE_FIELDS.END],
FIELDS.STATUS, AGENT: [...BASE_FIELDS.START, ...BASE_FIELDS.END],
FIELDS.SEVERITY,
FIELDS.DESCRIPTION,
FIELDS.IDENTIFIER,
FIELDS.TOOL,
FIELDS.ACTIVITY,
],
OPERATIONAL: [
FIELDS.DETECTED,
FIELDS.STATUS,
FIELDS.SEVERITY,
FIELDS.DESCRIPTION,
FIELDS.CLUSTER,
FIELDS.ACTIVITY,
],
}; };
export const FILTER_PRESETS = { export const FILTER_PRESETS = {
......
...@@ -137,6 +137,15 @@ export default { ...@@ -137,6 +137,15 @@ export default {
}, },
}, },
methods: { methods: {
hasCluster(item) {
return item.location.kubernetesResource?.agent;
},
clusterName(item) {
return item.location.kubernetesResource?.agent?.name;
},
clusterUrl(item) {
return item.location.kubernetesResource?.agent?.webPath;
},
createLocationString(location) { createLocationString(location) {
const { image, file, startLine, path } = location; const { image, file, startLine, path } = location;
...@@ -378,6 +387,16 @@ export default { ...@@ -378,6 +387,16 @@ export default {
</div> </div>
</template> </template>
<template #cell(cluster)="{ item }">
<gl-link
v-if="hasCluster(item)"
:data-testid="`cluster-${item.id}`"
:href="clusterUrl(item)"
>
{{ clusterName(item) }}
</gl-link>
</template>
<template #cell(activity)="{ item }"> <template #cell(activity)="{ item }">
<div class="gl-display-flex gl-justify-content-end"> <div class="gl-display-flex gl-justify-content-end">
<auto-fix-help-text v-if="item.mergeRequest" :merge-request="item.mergeRequest" /> <auto-fix-help-text v-if="item.mergeRequest" :merge-request="item.mergeRequest" />
......
fragment VulnerabilityLocation on VulnerabilityLocation { fragment VulnerabilityLocation on VulnerabilityLocation {
... on VulnerabilityLocationClusterImageScanning {
kubernetesResource {
agent {
id
name
webPath
}
}
}
... on VulnerabilityLocationContainerScanning { ... on VulnerabilityLocationContainerScanning {
image image
} }
......
...@@ -32,7 +32,7 @@ exports[`Agent vulnerability report component default renders 1`] = ` ...@@ -32,7 +32,7 @@ exports[`Agent vulnerability report component default renders 1`] = `
</div> </div>
<vulnerability-list-graphql-stub <vulnerability-list-graphql-stub
fields="[object Object],[object Object],[object Object],[object Object],,[object Object]" fields="[object Object],[object Object],[object Object],[object Object],[object Object]"
filters="[object Object]" filters="[object Object]"
portalname="vulnerability-report-sticky" portalname="vulnerability-report-sticky"
query="[object Object]" query="[object Object]"
......
export const clusterImageScanningVulnerability = {
hasSolutions: null,
mergeRequest: null,
__typename: 'Vulnerability',
id: 'gid://gitlab/Vulnerability/22087293',
title: 'CVE-2021-29921',
state: 'DETECTED',
severity: 'CRITICAL',
detectedAt: '2021-11-04T20:01:14Z',
vulnerabilityPath:
'/gitlab-org/protect/demos/agent-cluster-image-scanning-demo/-/security/vulnerabilities/22087293',
resolvedOnDefaultBranch: false,
userNotesCount: 0,
falsePositive: false,
issueLinks: {
nodes: [],
__typename: 'VulnerabilityIssueLinkConnection',
},
identifiers: [
{
externalType: 'cve',
name: 'CVE-2021-29921',
__typename: 'VulnerabilityIdentifier',
},
],
location: {
__typename: 'VulnerabilityLocationClusterImageScanning',
kubernetesResource: {
agent: {
name: 'cis-demo',
webPath:
'/gitlab-org/protect/demos/agent-cluster-image-scanning-demo/-/cluster_agents/cis-demo',
__typename: 'ClusterAgent',
},
__typename: 'VulnerableKubernetesResource',
},
},
};
export const generateVulnerabilities = () => [ export const generateVulnerabilities = () => [
{ {
id: 'id_0', id: 'id_0',
...@@ -43,6 +82,7 @@ export const generateVulnerabilities = () => [ ...@@ -43,6 +82,7 @@ export const generateVulnerabilities = () => [
externalIssueLinks: { externalIssueLinks: {
nodes: [{ id: 'issue-1', issue: { iid: 15, externalTracker: 'jira' } }], nodes: [{ id: 'issue-1', issue: { iid: 15, externalTracker: 'jira' } }],
}, },
__typename: 'Vulnerability',
}, },
{ {
id: 'id_1', id: 'id_1',
...@@ -72,6 +112,7 @@ export const generateVulnerabilities = () => [ ...@@ -72,6 +112,7 @@ export const generateVulnerabilities = () => [
id: 'scanner-2', id: 'scanner-2',
vendor: 'GitLab', vendor: 'GitLab',
}, },
__typename: 'Vulnerability',
}, },
{ {
id: 'id_2', id: 'id_2',
...@@ -92,6 +133,7 @@ export const generateVulnerabilities = () => [ ...@@ -92,6 +133,7 @@ export const generateVulnerabilities = () => [
id: 'scanner-3', id: 'scanner-3',
vendor: 'My Custom Scanner', vendor: 'My Custom Scanner',
}, },
__typename: 'Vulnerability',
}, },
{ {
id: 'id_3', id: 'id_3',
...@@ -106,6 +148,7 @@ export const generateVulnerabilities = () => [ ...@@ -106,6 +148,7 @@ export const generateVulnerabilities = () => [
nameWithNamespace: 'Mixed Vulnerabilities / Rails App', nameWithNamespace: 'Mixed Vulnerabilities / Rails App',
}, },
scanner: {}, scanner: {},
__typename: 'Vulnerability',
}, },
{ {
id: 'id_4', id: 'id_4',
...@@ -118,6 +161,7 @@ export const generateVulnerabilities = () => [ ...@@ -118,6 +161,7 @@ export const generateVulnerabilities = () => [
nameWithNamespace: 'Administrator / Security reports', nameWithNamespace: 'Administrator / Security reports',
}, },
scanner: {}, scanner: {},
__typename: 'Vulnerability',
}, },
{ {
id: 'id_5', id: 'id_5',
...@@ -132,6 +176,7 @@ export const generateVulnerabilities = () => [ ...@@ -132,6 +176,7 @@ export const generateVulnerabilities = () => [
nameWithNamespace: 'Administrator / Security reports', nameWithNamespace: 'Administrator / Security reports',
}, },
scanner: {}, scanner: {},
__typename: 'Vulnerability',
}, },
]; ];
......
...@@ -13,11 +13,17 @@ import FalsePositiveBadge from 'ee/vulnerabilities/components/false_positive_bad ...@@ -13,11 +13,17 @@ import FalsePositiveBadge from 'ee/vulnerabilities/components/false_positive_bad
import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue'; import RemediatedBadge from 'ee/vulnerabilities/components/remediated_badge.vue';
import { trimText } from 'helpers/text_helper'; import { trimText } from 'helpers/text_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import { mountExtended } from 'helpers/vue_test_utils_helper';
import { FIELDS } from 'ee/security_dashboard/components/shared/vulnerability_report/constants'; import {
FIELD_PRESETS,
FIELDS,
} from 'ee/security_dashboard/components/shared/vulnerability_report/constants';
import { stubComponent } from 'helpers/stub_component'; import { stubComponent } from 'helpers/stub_component';
import { generateVulnerabilities, vulnerabilities } from '../../mock_data'; import {
clusterImageScanningVulnerability,
generateVulnerabilities,
vulnerabilities,
} from '../../mock_data';
const { DETECTED, STATUS, SEVERITY, DESCRIPTION, IDENTIFIER, TOOL, ACTIVITY } = FIELDS;
const portalName = 'portal-name'; const portalName = 'portal-name';
describe('Vulnerability list component', () => { describe('Vulnerability list component', () => {
...@@ -27,7 +33,7 @@ describe('Vulnerability list component', () => { ...@@ -27,7 +33,7 @@ describe('Vulnerability list component', () => {
wrapper = mountExtended(VulnerabilityList, { wrapper = mountExtended(VulnerabilityList, {
propsData: { propsData: {
vulnerabilities: [], vulnerabilities: [],
fields: [DETECTED, STATUS, SEVERITY, DESCRIPTION, IDENTIFIER, TOOL, ACTIVITY], fields: FIELD_PRESETS.DEVELOPMENT,
portalName, portalName,
...props, ...props,
}, },
...@@ -67,6 +73,7 @@ describe('Vulnerability list component', () => { ...@@ -67,6 +73,7 @@ describe('Vulnerability list component', () => {
findRow(row).findComponent(VulnerabilityCommentIcon); findRow(row).findComponent(VulnerabilityCommentIcon);
const findDataCell = (label) => wrapper.findByTestId(label); const findDataCell = (label) => wrapper.findByTestId(label);
const findDataCells = (label) => wrapper.findAll(`[data-testid="${label}"]`); const findDataCells = (label) => wrapper.findAll(`[data-testid="${label}"]`);
const findClusterCell = (id) => wrapper.findByTestId(`cluster-${id}`);
const findLocationCell = (id) => wrapper.findByTestId(`location-${id}`); const findLocationCell = (id) => wrapper.findByTestId(`location-${id}`);
const findTitleCell = (id) => wrapper.findByTestId(`title-${id}`); const findTitleCell = (id) => wrapper.findByTestId(`title-${id}`);
const findLocationTextWrapper = (cell) => cell.findComponent(GlTruncate); const findLocationTextWrapper = (cell) => cell.findComponent(GlTruncate);
...@@ -669,6 +676,7 @@ describe('Vulnerability list component', () => { ...@@ -669,6 +676,7 @@ describe('Vulnerability list component', () => {
describe('fields prop', () => { describe('fields prop', () => {
it('shows the expected columns in the table', () => { it('shows the expected columns in the table', () => {
const { STATUS, SEVERITY } = FIELDS;
const fields = [STATUS, SEVERITY]; const fields = [STATUS, SEVERITY];
createWrapper({ createWrapper({
props: { fields, vulnerabilities }, props: { fields, vulnerabilities },
...@@ -681,4 +689,19 @@ describe('Vulnerability list component', () => { ...@@ -681,4 +689,19 @@ describe('Vulnerability list component', () => {
expect(findCell(SEVERITY.class).exists()).toBe(true); expect(findCell(SEVERITY.class).exists()).toBe(true);
}); });
}); });
describe('operational vulnerabilities', () => {
beforeEach(() => {
createWrapper({
props: {
fields: FIELD_PRESETS.OPERATIONAL,
vulnerabilities: [clusterImageScanningVulnerability],
},
});
});
it('shows the cluster column', () => {
expect(findClusterCell(clusterImageScanningVulnerability.id).exists()).toBe(true);
});
});
}); });
...@@ -41027,6 +41027,9 @@ msgstr "" ...@@ -41027,6 +41027,9 @@ msgstr ""
msgid "Vulnerability|Class" msgid "Vulnerability|Class"
msgstr "" msgstr ""
msgid "Vulnerability|Cluster"
msgstr ""
msgid "Vulnerability|Code Review" msgid "Vulnerability|Code Review"
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment