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 { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
document.addEventListener('DOMContentLoaded', () => {
initSecurityDashboard();
initSecurityDashboard(DASHBOARD_TYPES.GROUP);
});
<script>
import { mapActions } from 'vuex';
import { mapState, mapActions } from 'vuex';
import { GlButton, GlSkeletonLoading } from '@gitlab/ui';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import Icon from '~/vue_shared/components/icon.vue';
import VulnerabilityActionButtons from './vulnerability_action_buttons.vue';
import VulnerabilityIssueLink from './vulnerability_issue_link.vue';
import { DASHBOARD_TYPES } from '../store/constants';
export default {
name: 'SecurityDashboardTableRow',
......@@ -35,9 +36,12 @@ export default {
severity() {
return this.vulnerability.severity || ' ';
},
projectFullName() {
const { project } = this.vulnerability;
return project && project.full_name;
vulnerabilityNamepace() {
const { project, location } = this.vulnerability;
if (this.dashboardType === DASHBOARD_TYPES.GROUP) {
return project && project.full_name;
}
return location && (location.image || location.file || location.path);
},
isDismissed() {
return Boolean(this.vulnerability.dismissal_feedback);
......@@ -55,6 +59,7 @@ export default {
const path = this.vulnerability.create_vulnerability_feedback_issue_path;
return Boolean(path) && !this.hasIssue;
},
...mapState(['dashboardType']),
},
methods: {
...mapActions('vulnerabilities', ['openModal']),
......@@ -105,8 +110,8 @@ export default {
:project-name="vulnerability.project.name"
/>
<br />
<span v-if="projectFullName" class="vulnerability-namespace">
{{ projectFullName }}
<span v-if="vulnerabilityNamepace" class="vulnerability-namespace">
{{ vulnerabilityNamepace }}
</span>
</template>
</div>
......
......@@ -6,7 +6,7 @@ import createRouter from './store/router';
import projectsPlugin from './store/plugins/projects';
import syncWithRouter from './store/plugins/sync_with_router';
export default function() {
export default function(dashboardType) {
const el = document.getElementById('js-group-security-dashboard');
const { isUnavailable, dashboardDocumentation, emptyStateSvgPath } = el.dataset;
......@@ -25,7 +25,10 @@ export default function() {
}
const router = createRouter();
const store = createStore({ plugins: [projectsPlugin, syncWithRouter(router)] });
const store = createStore({
dashboardType,
plugins: [projectsPlugin, syncWithRouter(router)],
});
return new Vue({
el,
store,
......
......@@ -28,3 +28,10 @@ export const REPORT_TYPES = {
dependency_scanning: s__('ciReport|Dependency Scanning'),
sast: s__('ciReport|SAST'),
};
export const DASHBOARD_TYPES = {
PROJECT: 'project',
PIPELINE: 'pipeline',
GROUP: 'group',
INSTANCE: 'instance',
};
import Vue from 'vue';
import Vuex from 'vuex';
import { DASHBOARD_TYPES } from './constants';
import mediator from './plugins/mediator';
import filters from './modules/filters/index';
......@@ -8,8 +10,11 @@ import vulnerableProjects from './modules/vulnerable_projects/index';
Vue.use(Vuex);
export default ({ plugins = [] } = {}) =>
export default ({ dashboardType = DASHBOARD_TYPES.PROJECT, plugins = [] } = {}) =>
new Vuex.Store({
state: () => ({
dashboardType,
}),
modules: {
vulnerableProjects,
filters,
......
......@@ -163,7 +163,7 @@ exports[`DependenciesTableRow component when a dependency with vulnerabilities i
size="16"
/>
7 vulnerabilities
8 vulnerabilities
</gl-button-stub>
</div>
......
......@@ -3,11 +3,12 @@ import component from 'ee/security_dashboard/components/security_dashboard_table
import createStore from 'ee/security_dashboard/store';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import mockDataVulnerabilities from '../store/vulnerabilities/data/mock_data_vulnerabilities.json';
import { DASHBOARD_TYPES } from 'ee/security_dashboard/store/constants';
describe('Security Dashboard Table Row', () => {
let vm;
let props;
const store = createStore();
let store = createStore();
const Component = Vue.extend(component);
describe('when loading', () => {
......@@ -40,7 +41,7 @@ describe('Security Dashboard Table Row', () => {
});
describe('when loaded', () => {
const vulnerability = mockDataVulnerabilities[0];
let vulnerability = mockDataVulnerabilities[0];
beforeEach(() => {
props = { vulnerability };
......@@ -76,7 +77,7 @@ describe('Security Dashboard Table Row', () => {
it('should render the project namespace', () => {
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', () => {
});
});
});
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', () => {
......
......@@ -168,7 +168,7 @@
"feedback_type": "dismissal",
"branch": "master",
"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,
"create_vulnerability_feedback_issue_path": "https://example.com/vulnerability_feedback",
......@@ -484,5 +484,57 @@
"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