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 { ...@@ -110,6 +110,7 @@ export default {
:dashboard-documentation="dashboardDocumentation" :dashboard-documentation="dashboardDocumentation"
:empty-state-svg-path="emptyStateSvgPath" :empty-state-svg-path="emptyStateSvgPath"
:vulnerabilities="vulnerabilities" :vulnerabilities="vulnerabilities"
should-show-project-namespace
> >
<template #emptyState> <template #emptyState>
<gl-empty-state <gl-empty-state
......
...@@ -105,6 +105,7 @@ export default { ...@@ -105,6 +105,7 @@ export default {
:dashboard-documentation="dashboardDocumentation" :dashboard-documentation="dashboardDocumentation"
:empty-state-svg-path="emptyStateSvgPath" :empty-state-svg-path="emptyStateSvgPath"
:vulnerabilities="vulnerabilities" :vulnerabilities="vulnerabilities"
should-show-project-namespace
> >
<template #emptyState> <template #emptyState>
<gl-empty-state <gl-empty-state
......
...@@ -46,6 +46,11 @@ export default { ...@@ -46,6 +46,11 @@ export default {
required: false, required: false,
default: false, default: false,
}, },
shouldShowProjectNamespace: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return { return {
...@@ -122,6 +127,14 @@ export default { ...@@ -122,6 +127,14 @@ export default {
this.$set(this.selectedVulnerabilities, `${vulnerability.id}`, vulnerability); 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, VULNERABILITIES_PER_PAGE,
}; };
...@@ -166,6 +179,18 @@ export default { ...@@ -166,6 +179,18 @@ export default {
<gl-link class="text-body js-description" :href="item.vulnerabilityPath"> <gl-link class="text-body js-description" :href="item.vulnerabilityPath">
{{ item.title }} {{ item.title }}
</gl-link> </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" /> <remediated-badge v-if="item.resolved_on_default_branch" class="ml-2" />
</template> </template>
......
...@@ -4,4 +4,8 @@ fragment Vulnerability on Vulnerability { ...@@ -4,4 +4,8 @@ fragment Vulnerability on Vulnerability {
state state
severity severity
vulnerabilityPath vulnerabilityPath
location
project {
nameWithNamespace
}
} }
...@@ -53,6 +53,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => { ...@@ -53,6 +53,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => {
filters: null, filters: null,
isLoading: true, isLoading: true,
shouldShowSelection: false, shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities: [], vulnerabilities: [],
}); });
}); });
...@@ -143,6 +144,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => { ...@@ -143,6 +144,7 @@ describe('First Class Group Dashboard Vulnerabilities Component', () => {
filters: null, filters: null,
isLoading: false, isLoading: false,
shouldShowSelection: false, shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities, vulnerabilities,
}); });
}); });
......
...@@ -78,6 +78,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => { ...@@ -78,6 +78,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => {
filters: null, filters: null,
isLoading: true, isLoading: true,
shouldShowSelection: false, shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities: [], vulnerabilities: [],
}); });
}); });
...@@ -159,6 +160,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => { ...@@ -159,6 +160,7 @@ describe('First Class Instance Dashboard Vulnerabilities Component', () => {
filters: null, filters: null,
isLoading: false, isLoading: false,
shouldShowSelection: false, shouldShowSelection: false,
shouldShowProjectNamespace: true,
vulnerabilities, vulnerabilities,
}); });
}); });
......
...@@ -4,12 +4,25 @@ export const generateVulnerabilities = () => [ ...@@ -4,12 +4,25 @@ export const generateVulnerabilities = () => [
title: 'Vulnerability 1', title: 'Vulnerability 1',
severity: 'critical', severity: 'critical',
state: 'dismissed', state: 'dismissed',
location: JSON.stringify({
image:
'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff',
}),
project: {
nameWithNamespace: 'Administrator / Security reports',
},
}, },
{ {
id: 'id_1', id: 'id_1',
title: 'Vulnerability 2', title: 'Vulnerability 2',
severity: 'high', severity: 'high',
state: 'opened', 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', () => { ...@@ -33,8 +33,12 @@ describe('Vulnerability list component', () => {
const findSelectionSummary = () => wrapper.find(SelectionSummary); const findSelectionSummary = () => wrapper.find(SelectionSummary);
const findCheckAllCheckboxCell = () => wrapper.find('thead tr th'); const findCheckAllCheckboxCell = () => wrapper.find('thead tr th');
const findFirstCheckboxCell = () => wrapper.find('tbody tr td'); 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', () => { describe('with vulnerabilities', () => {
let newVulnerabilities; let newVulnerabilities;
...@@ -94,6 +98,56 @@ describe('Vulnerability list component', () => { ...@@ -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', () => { describe('when a vulnerability is resolved on the default branch', () => {
let newVulnerabilities; 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