Commit 4ccf6f1b authored by Dheeraj Joshi's avatar Dheeraj Joshi

Display location instead of project name in Security Dashboard

In the current Security Dashboard, at the project level, every row has
the current project name. It would be more useful for users to have
the location of the vulnerability instead. Except for Group Security
Dashboard, this change will show the file name / location
instead of project full name.
parent 249e94f7
---
title: Display location in the Security Project Dashboard
merge_request: 22376
author:
type: other
import initSecurityDashboard from 'ee/security_dashboard/index'; import initSecurityDashboard from 'ee/security_dashboard/index';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
initSecurityDashboard(); initSecurityDashboard(DASHBOARD_TYPES.GROUP);
}); });
<script> <script>
import { mapActions } from 'vuex'; import { mapState, mapActions } from 'vuex';
import { GlButton, GlSkeletonLoading } from '@gitlab/ui'; import { GlButton, GlSkeletonLoading } from '@gitlab/ui';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import VulnerabilityActionButtons from './vulnerability_action_buttons.vue'; import VulnerabilityActionButtons from './vulnerability_action_buttons.vue';
import VulnerabilityIssueLink from './vulnerability_issue_link.vue'; import VulnerabilityIssueLink from './vulnerability_issue_link.vue';
import { DASHBOARD_TYPES } from '../store/constants';
export default { export default {
name: 'SecurityDashboardTableRow', name: 'SecurityDashboardTableRow',
...@@ -35,9 +36,12 @@ export default { ...@@ -35,9 +36,12 @@ export default {
severity() { severity() {
return this.vulnerability.severity || ' '; return this.vulnerability.severity || ' ';
}, },
projectFullName() { vulnerabilityNamepace() {
const { project } = this.vulnerability; const { project, location } = this.vulnerability;
return project && project.full_name; if (this.dashboardType === DASHBOARD_TYPES.GROUP) {
return project && project.full_name;
}
return location && (location.image || location.file || location.path);
}, },
isDismissed() { isDismissed() {
return Boolean(this.vulnerability.dismissal_feedback); return Boolean(this.vulnerability.dismissal_feedback);
...@@ -55,6 +59,7 @@ export default { ...@@ -55,6 +59,7 @@ export default {
const path = this.vulnerability.create_vulnerability_feedback_issue_path; const path = this.vulnerability.create_vulnerability_feedback_issue_path;
return Boolean(path) && !this.hasIssue; return Boolean(path) && !this.hasIssue;
}, },
...mapState(['dashboardType']),
}, },
methods: { methods: {
...mapActions('vulnerabilities', ['openModal']), ...mapActions('vulnerabilities', ['openModal']),
...@@ -105,8 +110,8 @@ export default { ...@@ -105,8 +110,8 @@ export default {
:project-name="vulnerability.project.name" :project-name="vulnerability.project.name"
/> />
<br /> <br />
<span v-if="projectFullName" class="vulnerability-namespace"> <span v-if="vulnerabilityNamepace" class="vulnerability-namespace">
{{ projectFullName }} {{ vulnerabilityNamepace }}
</span> </span>
</template> </template>
</div> </div>
......
...@@ -6,7 +6,7 @@ import createRouter from './store/router'; ...@@ -6,7 +6,7 @@ import createRouter from './store/router';
import projectsPlugin from './store/plugins/projects'; import projectsPlugin from './store/plugins/projects';
import syncWithRouter from './store/plugins/sync_with_router'; import syncWithRouter from './store/plugins/sync_with_router';
export default function() { export default function(dashboardType) {
const el = document.getElementById('js-group-security-dashboard'); const el = document.getElementById('js-group-security-dashboard');
const { isUnavailable, dashboardDocumentation, emptyStateSvgPath } = el.dataset; const { isUnavailable, dashboardDocumentation, emptyStateSvgPath } = el.dataset;
...@@ -25,7 +25,10 @@ export default function() { ...@@ -25,7 +25,10 @@ export default function() {
} }
const router = createRouter(); const router = createRouter();
const store = createStore({ plugins: [projectsPlugin, syncWithRouter(router)] }); const store = createStore({
dashboardType,
plugins: [projectsPlugin, syncWithRouter(router)],
});
return new Vue({ return new Vue({
el, el,
store, store,
......
...@@ -28,3 +28,10 @@ export const REPORT_TYPES = { ...@@ -28,3 +28,10 @@ export const REPORT_TYPES = {
dependency_scanning: s__('ciReport|Dependency Scanning'), dependency_scanning: s__('ciReport|Dependency Scanning'),
sast: s__('ciReport|SAST'), sast: s__('ciReport|SAST'),
}; };
export const DASHBOARD_TYPES = {
PROJECT: 'project',
PIPELINE: 'pipeline',
GROUP: 'group',
INSTANCE: 'instance',
};
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { DASHBOARD_TYPES } from './constants';
import mediator from './plugins/mediator'; import mediator from './plugins/mediator';
import filters from './modules/filters/index'; import filters from './modules/filters/index';
...@@ -8,8 +10,11 @@ import vulnerableProjects from './modules/vulnerable_projects/index'; ...@@ -8,8 +10,11 @@ import vulnerableProjects from './modules/vulnerable_projects/index';
Vue.use(Vuex); Vue.use(Vuex);
export default ({ plugins = [] } = {}) => export default ({ dashboardType = DASHBOARD_TYPES.PROJECT, plugins = [] } = {}) =>
new Vuex.Store({ new Vuex.Store({
state: () => ({
dashboardType,
}),
modules: { modules: {
vulnerableProjects, vulnerableProjects,
filters, filters,
......
...@@ -163,7 +163,7 @@ exports[`DependenciesTableRow component when a dependency with vulnerabilities i ...@@ -163,7 +163,7 @@ exports[`DependenciesTableRow component when a dependency with vulnerabilities i
size="16" size="16"
/> />
7 vulnerabilities 8 vulnerabilities
</gl-button-stub> </gl-button-stub>
</div> </div>
......
...@@ -3,11 +3,12 @@ import component from 'ee/security_dashboard/components/security_dashboard_table ...@@ -3,11 +3,12 @@ import component from 'ee/security_dashboard/components/security_dashboard_table
import createStore from 'ee/security_dashboard/store'; import createStore from 'ee/security_dashboard/store';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper'; import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import mockDataVulnerabilities from '../store/vulnerabilities/data/mock_data_vulnerabilities.json'; import mockDataVulnerabilities from '../store/vulnerabilities/data/mock_data_vulnerabilities.json';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
describe('Security Dashboard Table Row', () => { describe('Security Dashboard Table Row', () => {
let vm; let vm;
let props; let props;
const store = createStore(); let store = createStore();
const Component = Vue.extend(component); const Component = Vue.extend(component);
describe('when loading', () => { describe('when loading', () => {
...@@ -40,7 +41,7 @@ describe('Security Dashboard Table Row', () => { ...@@ -40,7 +41,7 @@ describe('Security Dashboard Table Row', () => {
}); });
describe('when loaded', () => { describe('when loaded', () => {
const vulnerability = mockDataVulnerabilities[0]; let vulnerability = mockDataVulnerabilities[0];
beforeEach(() => { beforeEach(() => {
props = { vulnerability }; props = { vulnerability };
...@@ -76,7 +77,7 @@ describe('Security Dashboard Table Row', () => { ...@@ -76,7 +77,7 @@ describe('Security Dashboard Table Row', () => {
it('should render the project namespace', () => { it('should render the project namespace', () => {
expect(vm.$el.querySelectorAll('.table-mobile-content')[2].textContent).toContain( expect(vm.$el.querySelectorAll('.table-mobile-content')[2].textContent).toContain(
props.vulnerability.project.full_name, props.vulnerability.location.file,
); );
}); });
...@@ -90,6 +91,46 @@ describe('Security Dashboard Table Row', () => { ...@@ -90,6 +91,46 @@ describe('Security Dashboard Table Row', () => {
}); });
}); });
}); });
describe('Group Security Dashboard', () => {
beforeEach(() => {
store = createStore({
dashboardType: DASHBOARD_TYPES.GROUP,
});
props = { vulnerability };
vm = mountComponentWithStore(Component, { store, props });
});
afterEach(() => {
vm.$destroy();
});
it('should contain project name as the namespace', () => {
expect(vm.$el.querySelectorAll('.table-mobile-content')[2].textContent).toContain(
props.vulnerability.project.full_name,
);
});
});
describe('Non-group Security Dashboard', () => {
beforeEach(() => {
store = createStore();
// eslint-disable-next-line prefer-destructuring
vulnerability = mockDataVulnerabilities[7];
props = { vulnerability };
vm = mountComponentWithStore(Component, { store, props });
});
afterEach(() => {
vm.$destroy();
});
it('should contain container image as the namespace', () => {
expect(vm.$el.querySelectorAll('.table-mobile-content')[2].textContent).toContain(
props.vulnerability.location.image,
);
});
});
}); });
describe('with a dismissed vulnerability', () => { describe('with a dismissed vulnerability', () => {
......
...@@ -168,7 +168,7 @@ ...@@ -168,7 +168,7 @@
"feedback_type": "dismissal", "feedback_type": "dismissal",
"branch": "master", "branch": "master",
"project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4", "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"destroy_vulnerability_feedback_dismissal_path": "https://example.com/feedback_dismissal_path" "destroy_vulnerability_feedback_dismissal_path": "https://example.com/feedback_dismissal_path"
}, },
"issue_feedback": null, "issue_feedback": null,
"create_vulnerability_feedback_issue_path": "https://example.com/vulnerability_feedback", "create_vulnerability_feedback_issue_path": "https://example.com/vulnerability_feedback",
...@@ -484,5 +484,57 @@ ...@@ -484,5 +484,57 @@
"url": "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first" "url": "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
} }
] ]
},
{
"id": 8,
"report_type": "container_scanning",
"name": "CVE-2018-1000001 in glibc",
"severity": "high",
"confidence": "unknown",
"scanner": {
"external_id": "clair",
"name": "Clair"
},
"identifiers": [
{
"external_type": "cve",
"external_id": "CVE-2018-1000001",
"name": "CVE-2018-1000001",
"url": "https://security-tracker.debian.org/tracker/CVE-2018-1000001"
}
],
"project_fingerprint": "af08ab5aa899af9e74318ebc23684c9aa728ab7c",
"create_vulnerability_feedback_issue_path": "/gitlab-org/sec-reports/vulnerability_feedback",
"create_vulnerability_feedback_merge_request_path": "/gitlab-org/sec-reports/vulnerability_feedback",
"create_vulnerability_feedback_dismissal_path": "/gitlab-org/sec-reports/vulnerability_feedback",
"project": {
"id": 19,
"name": "sec-reports",
"full_path": "/gitlab-org/sec-reports",
"full_name": "Gitlab Org / sec-reports"
},
"dismissal_feedback": null,
"issue_feedback": null,
"merge_request_feedback": null,
"description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.",
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2018-1000001"
}
],
"location": {
"image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff",
"operating_system": "debian:9",
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
}
},
"remediations": null,
"solution": null,
"state": "opened",
"blob_path": ""
} }
] ]
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