Commit 05fecab7 authored by Savas Vedova's avatar Savas Vedova Committed by Phil Hughes

Add vulnerability location

- Retrieve the location from GQL and display it in the list
parent a73020ba
......@@ -110,6 +110,7 @@ export default {
:dashboard-documentation="dashboardDocumentation"
:empty-state-svg-path="emptyStateSvgPath"
:vulnerabilities="vulnerabilities"
should-show-project-namespace
>
<template #emptyState>
<gl-empty-state
......
......@@ -105,6 +105,7 @@ export default {
:dashboard-documentation="dashboardDocumentation"
:empty-state-svg-path="emptyStateSvgPath"
:vulnerabilities="vulnerabilities"
should-show-project-namespace
>
<template #emptyState>
<gl-empty-state
......
......@@ -46,6 +46,11 @@ export default {
required: false,
default: false,
},
shouldShowProjectNamespace: {
type: Boolean,
required: false,
default: false,
},
},
data() {
return {
......@@ -122,6 +127,14 @@ export default {
this.$set(this.selectedVulnerabilities, `${vulnerability.id}`, vulnerability);
}
},
parseLocation(item) {
try {
const parsed = JSON.parse(item.location);
return `${parsed.image || parsed.file}`;
} catch (e) {
return '';
}
},
},
VULNERABILITIES_PER_PAGE,
};
......@@ -166,6 +179,18 @@ export default {
<gl-link class="text-body js-description" :href="item.vulnerabilityPath">
{{ item.title }}
</gl-link>
<div
v-if="item.location"
:data-testid="`location-${item.id}`"
class="gl-text-color-secondary gl-font-size-small"
>
<div v-if="shouldShowProjectNamespace">
{{ item.project.nameWithNamespace }}
</div>
<div class="monospace">
{{ parseLocation(item) }}
</div>
</div>
<remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" />
</template>
......
......@@ -4,4 +4,8 @@ fragment Vulnerability on Vulnerability {
state
severity
vulnerabilityPath
location
project {
nameWithNamespace
}
}
......@@ -53,6 +53,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => {
filters: null,
isLoading: true,
shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities: [],
});
});
......@@ -143,6 +144,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => {
filters: null,
isLoading: false,
shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities,
});
});
......
......@@ -78,6 +78,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => {
filters: null,
isLoading: true,
shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities: [],
});
});
......@@ -159,6 +160,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => {
filters: null,
isLoading: false,
shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities,
});
});
......
......@@ -4,12 +4,25 @@ export const generateVulnerabilities = () => [
title: 'Vulnerability 1',
severity: 'critical',
state: 'dismissed',
location: JSON.stringify({
image:
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff',
}),
project: {
nameWithNamespace: 'Administrator / Security reports',
},
},
{
id: 'id_1',
title: 'Vulnerability 2',
severity: 'high',
state: 'opened',
location: JSON.stringify({
file: 'src/main/java/com/gitlab/security_products/tests/App.java',
}),
project: {
nameWithNamespace: 'Administrator / Vulnerability reports',
},
},
];
......
......@@ -33,8 +33,12 @@ describe('Vulnerability list component', () => {
const findSelectionSummary = () => wrapper.find(SelectionSummary);
const findCheckAllCheckboxCell = () => wrapper.find('thead tr th');
const findFirstCheckboxCell = () => wrapper.find('tbody tr td');
const findLocation = id => wrapper.find(`[data-testid="location-${id}"]`);
afterEach(() => wrapper.destroy());
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('with vulnerabilities', () => {
let newVulnerabilities;
......@@ -94,6 +98,56 @@ describe('Vulnerability list component', () => {
});
});
describe('when displayed on instance or group level dashboard', () => {
let newVulnerabilities;
beforeEach(() => {
newVulnerabilities = generateVulnerabilities();
wrapper = createWrapper({
props: { vulnerabilities: newVulnerabilities, shouldShowProjectNamespace: true },
});
});
it('should display the vulnerability locations', () => {
expect(findLocation(newVulnerabilities[0].id).text()).toContain(
'Administrator / Security reports',
);
expect(findLocation(newVulnerabilities[0].id).text()).toContain(
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff',
);
expect(findLocation(newVulnerabilities[1].id).text()).toContain(
'Administrator / Vulnerability reports',
);
expect(findLocation(newVulnerabilities[1].id).text()).toContain(
'src/main/java/com/gitlab/security_products/tests/App.java',
);
});
});
describe('when displayed on a project level dashboard', () => {
let newVulnerabilities;
beforeEach(() => {
newVulnerabilities = generateVulnerabilities();
wrapper = createWrapper({
props: { vulnerabilities: newVulnerabilities },
});
});
it('should not display the vulnerability locations', () => {
expect(findLocation(newVulnerabilities[0].id).text()).not.toContain(
'Administrator / Security reports',
);
expect(findLocation(newVulnerabilities[0].id).text()).toContain(
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff',
);
expect(findLocation(newVulnerabilities[1].id).text()).not.toContain(
'Administrator / Vulnerability reports',
);
expect(findLocation(newVulnerabilities[1].id).text()).toContain(
'src/main/java/com/gitlab/security_products/tests/App.java',
);
});
});
describe('when a vulnerability is resolved on the default branch', () => {
let newVulnerabilities;
......
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