Commit 085324df authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '6709-group-security-dashboard-api-integration-fe-ee' into 'master'

Security Dashboard API hookup

See merge request gitlab-org/gitlab-ee!7793
parents c4399be0 586f688a
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import { s__ } from '~/locale';
import Tabs from '~/vue_shared/components/tabs/tabs'; import Tabs from '~/vue_shared/components/tabs/tabs';
import Tab from '~/vue_shared/components/tabs/tab.vue'; import Tab from '~/vue_shared/components/tabs/tab.vue';
import SecurityDashboardTable from './security_dashboard_table.vue'; import SecurityDashboardTable from './security_dashboard_table.vue';
import VulnerabilityCountList from './vulnerability_count_list.vue'; import VulnerabilityCountList from './vulnerability_count_list.vue';
import SvgBlankState from '~/pipelines/components/blank_state.vue';
import Icon from '~/vue_shared/components/icon.vue';
import popover from '~/vue_shared/directives/popover';
export default { export default {
name: 'SecurityDashboardApp', name: 'SecurityDashboardApp',
directives: {
popover,
},
components: { components: {
Tabs, Icon,
Tab,
SecurityDashboardTable, SecurityDashboardTable,
SvgBlankState,
Tab,
Tabs,
VulnerabilityCountList, VulnerabilityCountList,
}, },
props: {
dashboardDocumentation: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
errorStateSvgPath: {
type: String,
required: true,
},
vulnerabilitiesEndpoint: {
type: String,
required: true,
},
vulnerabilitiesCountEndpoint: {
type: String,
required: true,
},
},
computed: { computed: {
...mapGetters('vulnerabilities', ['vulnerabilitiesCountByReportType']), ...mapGetters('vulnerabilities', ['vulnerabilitiesCountByReportType']),
...mapState('vulnerabilities', ['hasError']),
sastCount() { sastCount() {
return this.vulnerabilitiesCountByReportType('sast'); return this.vulnerabilitiesCountByReportType('sast');
}, },
popoverOptions() {
return {
trigger: 'click',
placement: 'right',
title: s__(
'Security Reports|At this time, the security dashboard only supports SAST. More analyzers are coming soon.',
),
content: `
<a
title="${s__('Security Reports|Security Dashboard Roadmap')}"
href="${this.dashboardDocumentation}"
target="_blank"
rel="noopener
noreferrer"
>
<span class="vertical-align-middle">${s__(
'Security Reports|Security Dashboard Roadmap',
)}</span>
${gl.utils.spriteIcon('external-link', 's16 vertical-align-middle')}
</a>
`,
html: true,
};
},
}, },
created() { created() {
this.setVulnerabilitiesEndpoint(this.vulnerabilitiesEndpoint);
this.setVulnerabilitiesCountEndpoint(this.vulnerabilitiesCountEndpoint);
this.fetchVulnerabilitiesCount(); this.fetchVulnerabilitiesCount();
}, },
methods: { methods: {
...mapActions('vulnerabilities', ['fetchVulnerabilitiesCount']), ...mapActions('vulnerabilities', [
'setVulnerabilitiesCountEndpoint',
'setVulnerabilitiesEndpoint',
'fetchVulnerabilitiesCount',
]),
}, },
}; };
</script> </script>
<template> <template>
<div> <div>
<svg-blank-state
v-if="hasError"
:svg-path="errorStateSvgPath"
:message="s__(`Security Reports|There was an error fetching the dashboard.
Please try again in a few moments or contact your support team.`)"
/>
<div v-else>
<vulnerability-count-list /> <vulnerability-count-list />
<tabs stop-propagation> <tabs stop-propagation>
<tab active> <tab active>
<template slot="title"> <template slot="title">
{{ __('SAST') }} <span>{{ __('SAST') }}</span>
<span <span
v-if="sastCount" v-if="sastCount"
class="badge badge-pill"> class="badge badge-pill"
>
{{ sastCount }} {{ sastCount }}
</span> </span>
<span
v-popover="popoverOptions"
class="text-muted ml-1"
>
<icon
name="question"
class="vertical-align-middle"
/>
</span>
</template> </template>
<security-dashboard-table/> <security-dashboard-table
:empty-state-svg-path="emptyStateSvgPath"
/>
</tab> </tab>
</tabs> </tabs>
</div> </div>
</div>
</template> </template>
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { SkeletonLoading } from '@gitlab-org/gitlab-ui'; import { SkeletonLoading } from '@gitlab-org/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 SecurityDashboardActionButtons from './security_dashboard_action_buttons.vue'; import SecurityDashboardActionButtons from './security_dashboard_action_buttons.vue';
import VulnerabilityIssueLink from './vulnerability_issue_link.vue';
export default { export default {
name: 'SecurityDashboardTableRow', name: 'SecurityDashboardTableRow',
...@@ -9,6 +10,7 @@ export default { ...@@ -9,6 +10,7 @@ export default {
SeverityBadge, SeverityBadge,
SecurityDashboardActionButtons, SecurityDashboardActionButtons,
SkeletonLoading, SkeletonLoading,
VulnerabilityIssueLink,
}, },
props: { props: {
vulnerability: { vulnerability: {
...@@ -31,7 +33,13 @@ export default { ...@@ -31,7 +33,13 @@ export default {
}, },
projectNamespace() { projectNamespace() {
const { project } = this.vulnerability; const { project } = this.vulnerability;
return project && project.name_with_namespace ? project.name_with_namespace : null; return project && project.full_name ? project.full_name : null;
},
isDismissed() {
return this.vulnerability.dismissal_feedback;
},
hasIssue() {
return this.vulnerability.issue_feedback;
}, },
}, },
}; };
...@@ -65,7 +73,13 @@ export default { ...@@ -65,7 +73,13 @@ export default {
:lines="2" :lines="2"
/> />
<div v-else> <div v-else>
<span>{{ vulnerability.description }}</span> <strike v-if="isDismissed">{{ vulnerability.name }}</strike>
<span v-else>{{ vulnerability.name }}</span>
<vulnerability-issue-link
v-if="hasIssue"
:issue="vulnerability.issue_feedback"
:project-name="vulnerability.project.name"
/>
<br /> <br />
<span <span
v-if="projectNamespace" v-if="projectNamespace"
...@@ -84,7 +98,8 @@ export default { ...@@ -84,7 +98,8 @@ export default {
{{ s__('Reports|Confidence') }} {{ s__('Reports|Confidence') }}
</div> </div>
<div class="table-mobile-content text-capitalize"> <div class="table-mobile-content text-capitalize">
{{ confidence }} <strike v-if="isDismissed">{{ confidence }}</strike>
<span v-else>{{ confidence }}</span>
</div> </div>
</div> </div>
......
<script>
import Icon from '~/vue_shared/components/icon.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
export default {
name: 'VulnerabilityIssueLink',
components: {
Icon,
},
directives: {
Tooltip,
},
props: {
issue: {
type: Object,
required: true,
},
projectName: {
type: String,
required: true,
},
},
computed: {
linkText() {
return `${this.projectName}#${this.issue.issue_id}`;
},
},
};
</script>
<template>
<div class="d-inline">
<icon
v-tooltip
name="issues"
css-classes="text-success vertical-align-middle"
:title="s__('Security Dashboard|Issue Created')"
/>
<a
:href="issue.issue_url"
>{{ linkText }}</a>
</div>
</template>
...@@ -12,7 +12,15 @@ export default () => { ...@@ -12,7 +12,15 @@ export default () => {
GroupSecurityDashboardApp, GroupSecurityDashboardApp,
}, },
render(createElement) { render(createElement) {
return createElement('group-security-dashboard-app'); return createElement('group-security-dashboard-app', {
props: {
dashboardDocumentation: el.dataset.dashboardDocumentation,
errorStateSvgPath: el.dataset.errorStateSvgPath,
emptyStateSvgPath: el.dataset.emptyStateSvgPath,
vulnerabilitiesEndpoint: el.dataset.vulnerabilitiesEndpoint,
vulnerabilitiesCountEndpoint: el.dataset.vulnerabilitiesSummaryEndpoint,
},
});
}, },
}); });
}; };
...@@ -2,6 +2,14 @@ import axios from '~/lib/utils/axios_utils'; ...@@ -2,6 +2,14 @@ import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
export const setVulnerabilitiesEndpoint = ({ commit }, endpoint) => {
commit(types.SET_VULNERABILITIES_ENDPOINT, endpoint);
};
export const setVulnerabilitiesCountEndpoint = ({ commit }, endpoint) => {
commit(types.SET_VULNERABILITIES_COUNT_ENDPOINT, endpoint);
};
export const fetchVulnerabilitiesCount = ({ state, dispatch }) => { export const fetchVulnerabilitiesCount = ({ state, dispatch }) => {
dispatch('requestVulnerabilitiesCount'); dispatch('requestVulnerabilitiesCount');
......
[ [
{ {
"id": "wzexrctyu", "id": 1,
"report_type": "sast", "report_type": "sast",
"name": "Cipher with no integrity", "name": "Insecure variable usage",
"description": "Insecure variable usage", "severity": "critical",
"severity": "medium",
"confidence": "high", "confidence": "high",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"scanner": { "scanner": {
"external_id": "find_sec_bugs", "external_id": "find_sec_bugs",
"name": "Find Security Bugs" "name": "Find Security Bugs"
}, },
"identifiers": [
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
},
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
}
],
"project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"project": {
"id": 1,
"name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / security-products / binaries"
},
"dismissal_feedback": null,
"issue_feedback": null,
"description": "The cipher does not provide data integrity update 1",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"location": { "location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java", "file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29, "start_line": 29,
...@@ -18,71 +41,119 @@ ...@@ -18,71 +41,119 @@
"class": "com.gitlab.security_products.tests.App", "class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher" "method": "insecureCypher"
}, },
"links": [
{
"name": "Cipher does not check for integrity first?",
"url": "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
]
},
{
"id": 2,
"report_type": "sast",
"name": "Insecure variable usage",
"severity": "critical",
"confidence": "high",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"identifiers": [ "identifiers": [
{ {
"external_type": "find_sec_bugs_type", "external_type": "CVE",
"external_id": "CIPHER_INTEGRITY", "external_id": "CVE-2018-1234",
"name": "Find Security Bugs-CIPHER_INTEGRITY", "name": "CVE-2018-1234",
"primary": true, "url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
"url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
}, },
{ {
"external_type": "cwe", "external_type": "CVE",
"external_id": "353", "external_id": "CVE-2018-1234",
"name": "CWE-353", "name": "CVE-2018-1234",
"primary": false, "url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
"url": "https://cwe.mitre.org/data/definitions/353.html"
} }
], ],
"project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"project": {
"id": 1,
"name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / quality / staging"
},
"dismissal_feedback": null,
"issue_feedback": null,
"description": "The cipher does not provide data integrity update 1",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29,
"end_line": 29,
"class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher"
},
"links": [ "links": [
{ {
"name": "Cipher does not check for integrity first?", "name": "Cipher does not check for integrity first?",
"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": 3,
"report_type": "sast",
"name": "Insecure variable usage",
"severity": "medium",
"confidence": "",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"identifiers": [
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
},
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
}
], ],
"project_fingerprint": "a42cbe949d1471a4310e3bd6fc16619ad2691f3f", "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"vulnerability_feedback_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common/vulnerability_feedback",
"project": { "project": {
"name_with_namespace": "GitLab.org / security-products / binaries", "id": 1,
"web_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common" "name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / security-products / licence-management"
}, },
"dismissal_feedback": { "dismissal_feedback": {
"id": 19, "id": 1,
"project_id": 6126012, "project_id": 1,
"author": { "author": {
"id": 30915, "id": 6,
"name": "Philippe Lafoucrière", "name": "John Doe7",
"username": "plafoucriere", "username": "user6",
"state": "active", "state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/79f933bb7e06a2400302f5b30f28689e?s=80\u0026d=identicon", "avatar_url": "https://www.gravatar.com/avatar/3de3cc5a52553af613b6c457da6c219a?s=80&d=identicon",
"web_url": "https://gitlab.com/plafoucriere", "web_url": "http://localhost/user6",
"status_tooltip_html": null, "status_tooltip_html": null,
"path": "/plafoucriere" "path": "/user6"
}, },
"issue_id": null, "issue_id": null,
"pipeline": { "pipeline": {
"id": 23035216, "id": 2,
"path": "/gitlab-org/security-products/analyzers/common/pipelines/23035216" "path": "/namespace5/project5/pipelines/2"
}, },
"category": "sast", "category": "sast",
"feedback_type": "dismissal", "feedback_type": "dismissal",
"branch": "gitlab-ee-5043_enrich_sast_reports", "branch": "master",
"project_fingerprint": "1235d9f927a0dee633570a53c0773249e0a734a8" "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4"
}, },
"issue_feedback": null "issue_feedback": null,
},
{
"id": "jawodjbwkdjbawkdjba",
"report_type": "sast",
"name": "Cipher with no integrity",
"description": "The cipher does not provide data integrity update 1", "description": "The cipher does not provide data integrity update 1",
"severity": "unknown",
"confidence": "",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.", "solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"location": { "location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java", "file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29, "start_line": 29,
...@@ -90,71 +161,71 @@ ...@@ -90,71 +161,71 @@
"class": "com.gitlab.security_products.tests.App", "class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher" "method": "insecureCypher"
}, },
"identifiers": [
{
"external_type": "find_sec_bugs_type",
"external_id": "CIPHER_INTEGRITY",
"name": "Find Security Bugs-CIPHER_INTEGRITY",
"primary": true,
"url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
},
{
"external_type": "cwe",
"external_id": "353",
"name": "CWE-353",
"primary": false,
"url": "https://cwe.mitre.org/data/definitions/353.html"
}
],
"links": [ "links": [
{ {
"name": "Cipher does not check for integrity first?", "name": "Cipher does not check for integrity first?",
"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": 4,
"report_type": "sast",
"name": "Insecure variable usage",
"severity": "high",
"confidence": "low",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"identifiers": [
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
},
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
}
], ],
"project_fingerprint": "a42cbe949d1471a4310e3bd6fc16619ad2691f3f", "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"vulnerability_feedback_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common/vulnerability_feedback",
"project": { "project": {
"name_with_namespace": "GitLab.org / Security Products / Analyzers / Common", "id": 1,
"web_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common" "name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / security-products / codequality"
}, },
"dismissal_feedback": { "dismissal_feedback": null,
"id": 19, "issue_feedback": {
"project_id": 6126012, "id": 2,
"project_id": 1,
"author": { "author": {
"id": 30915, "id": 8,
"name": "Philippe Lafoucrière", "name": "John Doe9",
"username": "plafoucriere", "username": "user8",
"state": "active", "state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/79f933bb7e06a2400302f5b30f28689e?s=80\u0026d=identicon", "avatar_url": "https://www.gravatar.com/avatar/51798cfc94af924ac2dffb7083baa6f4?s=80&d=identicon",
"web_url": "https://gitlab.com/plafoucriere", "web_url": "http://localhost/user8",
"status_tooltip_html": null, "status_tooltip_html": null,
"path": "/plafoucriere" "path": "/user8"
}, },
"issue_id": null, "issue_id": 1,
"pipeline": { "pipeline": {
"id": 23035216, "id": 3,
"path": "/gitlab-org/security-products/analyzers/common/pipelines/23035216" "path": "/namespace6/project6/pipelines/3"
}, },
"issue_url": "http://localhost/namespace1/project1/issues/1",
"category": "sast", "category": "sast",
"feedback_type": "dismissal", "feedback_type": "issue",
"branch": "gitlab-ee-5043_enrich_sast_reports", "branch": "master",
"project_fingerprint": "1235d9f927a0dee633570a53c0773249e0a734a8" "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4"
}, },
"issue_feedback": null
},
{
"id": "wertvbvtc",
"report_type": "sast",
"name": "Cipher with no integrity",
"description": "The cipher does not provide data integrity update 1", "description": "The cipher does not provide data integrity update 1",
"severity": "low",
"confidence": "high",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.", "solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"location": { "location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java", "file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29, "start_line": 29,
...@@ -162,71 +233,93 @@ ...@@ -162,71 +233,93 @@
"class": "com.gitlab.security_products.tests.App", "class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher" "method": "insecureCypher"
}, },
"identifiers": [
{
"external_type": "find_sec_bugs_type",
"external_id": "CIPHER_INTEGRITY",
"name": "Find Security Bugs-CIPHER_INTEGRITY",
"primary": true,
"url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
},
{
"external_type": "cwe",
"external_id": "353",
"name": "CWE-353",
"primary": false,
"url": "https://cwe.mitre.org/data/definitions/353.html"
}
],
"links": [ "links": [
{ {
"name": "Cipher does not check for integrity first?", "name": "Cipher does not check for integrity first?",
"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": 5,
"report_type": "sast",
"name": "Remote command execution due to flaw in the include params attribute of URL and Anchor tags for org.apache.struts/struts2core",
"severity": "low",
"confidence": "",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"identifiers": [
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
},
{
"external_type": "CVE",
"external_id": "CVE-2018-1234",
"name": "CVE-2018-1234",
"url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
}
], ],
"project_fingerprint": "a42cbe949d1471a4310e3bd6fc16619ad2691f3f", "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"vulnerability_feedback_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common/vulnerability_feedback",
"project": { "project": {
"name_with_namespace": "GitLab.org / Security Products / Analyzers / Common", "id": 1,
"web_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common" "name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / security-products / staging"
}, },
"dismissal_feedback": { "dismissal_feedback": {
"id": 19, "id": 1,
"project_id": 6126012, "project_id": 1,
"author": { "author": {
"id": 30915, "id": 6,
"name": "Philippe Lafoucrière", "name": "John Doe7",
"username": "plafoucriere", "username": "user6",
"state": "active", "state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/79f933bb7e06a2400302f5b30f28689e?s=80\u0026d=identicon", "avatar_url": "https://www.gravatar.com/avatar/3de3cc5a52553af613b6c457da6c219a?s=80&d=identicon",
"web_url": "https://gitlab.com/plafoucriere", "web_url": "http://localhost/user6",
"status_tooltip_html": null, "status_tooltip_html": null,
"path": "/plafoucriere" "path": "/user6"
}, },
"issue_id": null, "issue_id": null,
"pipeline": { "pipeline": {
"id": 23035216, "id": 2,
"path": "/gitlab-org/security-products/analyzers/common/pipelines/23035216" "path": "/namespace5/project5/pipelines/2"
}, },
"category": "sast", "category": "sast",
"feedback_type": "dismissal", "feedback_type": "dismissal",
"branch": "gitlab-ee-5043_enrich_sast_reports", "branch": "master",
"project_fingerprint": "1235d9f927a0dee633570a53c0773249e0a734a8" "project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4"
},
"issue_feedback": {
"id": 2,
"project_id": 1,
"author": {
"id": 8,
"name": "John Doe9",
"username": "user8",
"state": "active",
"avatar_url": "https://www.gravatar.com/avatar/51798cfc94af924ac2dffb7083baa6f4?s=80&d=identicon",
"web_url": "http://localhost/user8",
"status_tooltip_html": null,
"path": "/user8"
}, },
"issue_feedback": null "issue_id": 1,
"pipeline": {
"id": 3,
"path": "/namespace6/project6/pipelines/3"
},
"issue_url": "http://localhost/namespace1/project1/issues/1",
"category": "sast",
"feedback_type": "issue",
"branch": "master",
"project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4"
}, },
{
"id": "iouygdfei",
"report_type": "sast",
"name": "Cipher with no integrity",
"description": "The cipher does not provide data integrity update 1", "description": "The cipher does not provide data integrity update 1",
"severity": "critical",
"confidence": "ignore",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.", "solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"location": { "location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java", "file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29, "start_line": 29,
...@@ -234,57 +327,60 @@ ...@@ -234,57 +327,60 @@
"class": "com.gitlab.security_products.tests.App", "class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher" "method": "insecureCypher"
}, },
"links": [
{
"name": "Cipher does not check for integrity first?",
"url": "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
]
},
{
"id": 6,
"report_type": "sast",
"name": "Doorkeeper Gem does not revoke token for public clients",
"severity": "unknown",
"confidence": "",
"scanner": {
"external_id": "find_sec_bugs",
"name": "Find Security Bugs"
},
"identifiers": [ "identifiers": [
{ {
"external_type": "find_sec_bugs_type", "external_type": "CVE",
"external_id": "CIPHER_INTEGRITY", "external_id": "CVE-2018-1234",
"name": "Find Security Bugs-CIPHER_INTEGRITY", "name": "CVE-2018-1234",
"primary": true, "url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
"url": "https://find-sec-bugs.github.io/bugs.htm#CIPHER_INTEGRITY"
}, },
{ {
"external_type": "cwe", "external_type": "CVE",
"external_id": "353", "external_id": "CVE-2018-1234",
"name": "CWE-353", "name": "CVE-2018-1234",
"primary": false, "url": "http://cve.mitre.org/cgi-bin/cvename.cgi?name=2018-1234"
"url": "https://cwe.mitre.org/data/definitions/353.html"
} }
], ],
"project_fingerprint": "4e5b6966dd100170b4b1ad599c7058cce91b57b4",
"project": {
"id": 1,
"name": "project1",
"full_path": "/namespace1/project1",
"full_name": "Gitab.org / security-products / binaries"
},
"dismissal_feedback": null,
"issue_feedback": null,
"description": "The cipher does not provide data integrity update 1",
"solution": "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"location": {
"file": "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line": 29,
"end_line": 29,
"class": "com.gitlab.security_products.tests.App",
"method": "insecureCypher"
},
"links": [ "links": [
{ {
"name": "Cipher does not check for integrity first?", "name": "Cipher does not check for integrity first?",
"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"
} }
], ]
"project_fingerprint": "a42cbe949d1471a4310e3bd6fc16619ad2691f3f",
"vulnerability_feedback_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common/vulnerability_feedback",
"project": {
"name_with_namespace": "GitLab.org / Security Products / Analyzers / Common",
"web_url": "https://gitlab.com/gitlab-org/security-products/analyzers/common"
},
"dismissal_feedback": {
"id": 19,
"project_id": 6126012,
"author": {
"id": 30915,
"name": "Philippe Lafoucrière",
"username": "plafoucriere",
"state": "active",
"avatar_url": "https://secure.gravatar.com/avatar/79f933bb7e06a2400302f5b30f28689e?s=80\u0026d=identicon",
"web_url": "https://gitlab.com/plafoucriere",
"status_tooltip_html": null,
"path": "/plafoucriere"
},
"issue_id": null,
"pipeline": {
"id": 23035216,
"path": "/gitlab-org/security-products/analyzers/common/pipelines/23035216"
},
"category": "sast",
"feedback_type": "dismissal",
"branch": "gitlab-ee-5043_enrich_sast_reports",
"project_fingerprint": "1235d9f927a0dee633570a53c0773249e0a734a8"
},
"issue_feedback": null
} }
] ]
\ No newline at end of file
export const SET_VULNERABILITIES_ENDPOINT = 'SET_VULNERABILITIES_ENDPOINT';
export const REQUEST_VULNERABILITIES = 'REQUEST_VULNERABILITIES'; export const REQUEST_VULNERABILITIES = 'REQUEST_VULNERABILITIES';
export const RECEIVE_VULNERABILITIES_SUCCESS = 'RECEIVE_VULNERABILITIES_SUCCESS'; export const RECEIVE_VULNERABILITIES_SUCCESS = 'RECEIVE_VULNERABILITIES_SUCCESS';
export const RECEIVE_VULNERABILITIES_ERROR = 'RECEIVE_VULNERABILITIES_ERROR'; export const RECEIVE_VULNERABILITIES_ERROR = 'RECEIVE_VULNERABILITIES_ERROR';
export const SET_VULNERABILITIES_COUNT_ENDPOINT = 'SET_VULNERABILITIES_COUNT_ENDPOINT';
export const REQUEST_VULNERABILITIES_COUNT = 'REQUEST_VULNERABILITIES_COUNT'; export const REQUEST_VULNERABILITIES_COUNT = 'REQUEST_VULNERABILITIES_COUNT';
export const RECEIVE_VULNERABILITIES_COUNT_SUCCESS = 'RECEIVE_VULNERABILITIES_COUNT_SUCCESS'; export const RECEIVE_VULNERABILITIES_COUNT_SUCCESS = 'RECEIVE_VULNERABILITIES_COUNT_SUCCESS';
export const RECEIVE_VULNERABILITIES_COUNT_ERROR = 'RECEIVE_VULNERABILITIES_COUNT_ERROR'; export const RECEIVE_VULNERABILITIES_COUNT_ERROR = 'RECEIVE_VULNERABILITIES_COUNT_ERROR';
import * as types from './mutation_types'; import * as types from './mutation_types';
export default { export default {
[types.SET_VULNERABILITIES_ENDPOINT](state, payload) {
state.vulnerabilitiesEndpoint = payload;
},
[types.REQUEST_VULNERABILITIES](state) { [types.REQUEST_VULNERABILITIES](state) {
state.isLoadingVulnerabilities = true; state.isLoadingVulnerabilities = true;
state.hasError = false;
}, },
[types.RECEIVE_VULNERABILITIES_SUCCESS](state, payload) { [types.RECEIVE_VULNERABILITIES_SUCCESS](state, payload) {
state.isLoadingVulnerabilities = false; state.isLoadingVulnerabilities = false;
state.errorLoadingVulnerabilities = false;
state.pageInfo = payload.pageInfo; state.pageInfo = payload.pageInfo;
state.vulnerabilities = payload.vulnerabilities; state.vulnerabilities = payload.vulnerabilities;
}, },
[types.RECEIVE_VULNERABILITIES_ERROR](state) { [types.RECEIVE_VULNERABILITIES_ERROR](state) {
state.isLoadingVulnerabilities = false; state.isLoadingVulnerabilities = false;
state.errorLoadingVulnerabilities = true; state.hasError = true;
},
[types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, payload) {
state.vulnerabilitiesCountEndpoint = payload;
}, },
[types.REQUEST_VULNERABILITIES_COUNT](state) { [types.REQUEST_VULNERABILITIES_COUNT](state) {
state.isLoadingVulnerabilitiesCount = true; state.isLoadingVulnerabilitiesCount = true;
state.hasError = false;
}, },
[types.RECEIVE_VULNERABILITIES_COUNT_SUCCESS](state, payload) { [types.RECEIVE_VULNERABILITIES_COUNT_SUCCESS](state, payload) {
state.isLoadingVulnerabilitiesCount = false; state.isLoadingVulnerabilitiesCount = false;
state.errorLoadingVulnerabilities = false;
state.vulnerabilitiesCount = payload; state.vulnerabilitiesCount = payload;
}, },
[types.RECEIVE_VULNERABILITIES_COUNT_ERROR](state) { [types.RECEIVE_VULNERABILITIES_COUNT_ERROR](state) {
state.isLoadingVulnerabilitiesCount = false; state.isLoadingVulnerabilitiesCount = false;
state.errorLoadingVulnerabilities = true; state.hasError = true;
}, },
}; };
export default () => ({ export default () => ({
isLoadingVulnerabilities: false, hasError: false,
isLoadingVulnerabilitiesCount: false, isLoadingVulnerabilities: true,
isLoadingVulnerabilitiesCount: true,
pageInfo: {}, pageInfo: {},
vulnerabilities: [], vulnerabilities: [],
vulnerabilitiesCount: {}, vulnerabilitiesCount: {},
errorLoadingVulnerabilities: false, vulnerabilitiesCountEndpoint: null,
vulnerabilitiesEndpoint: null,
}); });
- breadcrumb_title _("Security Dashboard") - breadcrumb_title _("Security Dashboard")
- page_title _("Security Dashboard")
#js-group-security-dashboard #js-group-security-dashboard{ data: { vulnerabilities_endpoint: group_security_vulnerabilities_path(@group),
vulnerabilities_summary_endpoint: summary_group_security_vulnerabilities_path(@group),
dashboard_documentation: help_page_path('user/group/security_dashboard'),
empty_state_svg_path: image_path('illustrations/security-dashboard-empty-state.svg'),
error_state_svg_path: image_path('illustrations/security-dashboard-api-error-empty-state.svg') } }
---
title: Connects the Group Security Dashboard API and Frontend
merge_request: 7793
author:
type: other
...@@ -36,9 +36,9 @@ describe('Security Dashboard Table Row', () => { ...@@ -36,9 +36,9 @@ describe('Security Dashboard Table Row', () => {
beforeEach(() => { beforeEach(() => {
const vulnerability = { const vulnerability = {
severity: 'high', severity: 'high',
description: 'Test vulnerability', name: 'Test vulnerability',
confidence: 'medium', confidence: 'medium',
project: { name_with_namespace: 'project name' }, project: { full_name: 'project name' },
}; };
props = { vulnerability }; props = { vulnerability };
...@@ -55,15 +55,15 @@ describe('Security Dashboard Table Row', () => { ...@@ -55,15 +55,15 @@ describe('Security Dashboard Table Row', () => {
); );
}); });
it('should render the description', () => { it('should render the name', () => {
expect(vm.$el.querySelectorAll('.table-mobile-content')[1].textContent).toContain( expect(vm.$el.querySelectorAll('.table-mobile-content')[1].textContent).toContain(
props.vulnerability.description, props.vulnerability.name,
); );
}); });
it('should render the project namespace', () => { it('should render the project namespace', () => {
expect(vm.$el.querySelectorAll('.table-mobile-content')[1].textContent).toContain( expect(vm.$el.querySelectorAll('.table-mobile-content')[1].textContent).toContain(
props.vulnerability.project.name_with_namespace, props.vulnerability.project.full_name,
); );
}); });
......
import Vue from 'vue';
import component from 'ee/security_dashboard/components/vulnerability_issue_link.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Vulnerability Issue Link component', () => {
const Component = Vue.extend(component);
let vm;
let props;
beforeEach(() => {
const issue = {
issue_id: 1,
issue_url: 'https://gitlab.com',
};
const projectName = 'Project Name';
props = { issue, projectName };
vm = mountComponent(Component, props);
});
afterEach(() => {
vm.$destroy();
});
it('should render the severity label', () => {
expect(vm.$el.textContent).toContain(`${props.projectName}#${props.issue.issue_id}`);
});
it('should link to the issue', () => {
const link = vm.$el.querySelector('a');
expect(link.href).toMatch(props.issue.issue_url);
});
});
...@@ -234,4 +234,46 @@ describe('vulnerabilities actions', () => { ...@@ -234,4 +234,46 @@ describe('vulnerabilities actions', () => {
); );
}); });
}); });
describe('setVulnerabilitiesEndpoint', () => {
it('should commit the correct mutuation', done => {
const state = initialState;
const endpoint = 'fakepath.json';
testAction(
actions.setVulnerabilitiesEndpoint,
endpoint,
state,
[
{
type: types.SET_VULNERABILITIES_ENDPOINT,
payload: endpoint,
},
],
[],
done,
);
});
});
describe('setVulnerabilitiesCountEndpoint', () => {
it('should commit the correct mutuation', done => {
const state = initialState;
const endpoint = 'fakepath.json';
testAction(
actions.setVulnerabilitiesCountEndpoint,
endpoint,
state,
[
{
type: types.SET_VULNERABILITIES_COUNT_ENDPOINT,
payload: endpoint,
},
],
[],
done,
);
});
});
}); });
...@@ -3,14 +3,35 @@ import * as types from 'ee/security_dashboard/store/modules/vulnerabilities/muta ...@@ -3,14 +3,35 @@ import * as types from 'ee/security_dashboard/store/modules/vulnerabilities/muta
import mutations from 'ee/security_dashboard/store/modules/vulnerabilities/mutations'; import mutations from 'ee/security_dashboard/store/modules/vulnerabilities/mutations';
describe('vulnerabilities module mutations', () => { describe('vulnerabilities module mutations', () => {
describe('REQUEST_VULNERABILITIES', () => { describe('SET_VULNERABILITIES_ENDPOINT', () => {
it('should set `isLoadingVulnerabilities` to `true`', () => { it('should set `vulnerabilitiesEndpoint` to `fakepath.json`', () => {
const state = initialState; const state = initialState;
const endpoint = 'fakepath.json';
mutations[types.SET_VULNERABILITIES_ENDPOINT](state, endpoint);
expect(state.vulnerabilitiesEndpoint).toEqual(endpoint);
});
});
describe('REQUEST_VULNERABILITIES', () => {
let state;
beforeEach(() => {
state = {
...initialState,
hasError: true,
};
mutations[types.REQUEST_VULNERABILITIES](state); mutations[types.REQUEST_VULNERABILITIES](state);
});
it('should set `isLoadingVulnerabilities` to `true`', () => {
expect(state.isLoadingVulnerabilities).toBeTruthy(); expect(state.isLoadingVulnerabilities).toBeTruthy();
}); });
it('should set `hasError` to `false`', () => {
expect(state.hasError).toBeFalsy();
});
}); });
describe('RECEIVE_VULNERABILITIES_SUCCESS', () => { describe('RECEIVE_VULNERABILITIES_SUCCESS', () => {
...@@ -30,10 +51,6 @@ describe('vulnerabilities module mutations', () => { ...@@ -30,10 +51,6 @@ describe('vulnerabilities module mutations', () => {
expect(state.isLoadingVulnerabilities).toBeFalsy(); expect(state.isLoadingVulnerabilities).toBeFalsy();
}); });
it('should set `errorLoadingData` to `false`', () => {
expect(state.errorLoadingData).toBeFalsy();
});
it('should set `pageInfo`', () => { it('should set `pageInfo`', () => {
expect(state.pageInfo).toBe(payload.pageInfo); expect(state.pageInfo).toBe(payload.pageInfo);
}); });
...@@ -53,14 +70,35 @@ describe('vulnerabilities module mutations', () => { ...@@ -53,14 +70,35 @@ describe('vulnerabilities module mutations', () => {
}); });
}); });
describe('REQUEST_VULNERABILITIES_COUNT', () => { describe('SET_VULNERABILITIES_COUNT_ENDPOINT', () => {
it('should set `isLoadingVulnerabilitiesCount` to `true`', () => { it('should set `vulnerabilitiesCountEndpoint` to `fakepath.json`', () => {
const state = initialState; const state = initialState;
const endpoint = 'fakepath.json';
mutations[types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, endpoint);
expect(state.vulnerabilitiesCountEndpoint).toEqual(endpoint);
});
});
describe('REQUEST_VULNERABILITIES_COUNT', () => {
let state;
beforeEach(() => {
state = {
...initialState,
hasError: true,
};
mutations[types.REQUEST_VULNERABILITIES_COUNT](state); mutations[types.REQUEST_VULNERABILITIES_COUNT](state);
});
it('should set `isLoadingVulnerabilitiesCount` to `true`', () => {
expect(state.isLoadingVulnerabilitiesCount).toBeTruthy(); expect(state.isLoadingVulnerabilitiesCount).toBeTruthy();
}); });
it('should set `hasError` to `false`', () => {
expect(state.hasError).toBeFalsy();
});
}); });
describe('RECEIVE_VULNERABILITIES_COUNT_SUCCESS', () => { describe('RECEIVE_VULNERABILITIES_COUNT_SUCCESS', () => {
...@@ -77,10 +115,6 @@ describe('vulnerabilities module mutations', () => { ...@@ -77,10 +115,6 @@ describe('vulnerabilities module mutations', () => {
expect(state.isLoadingVulnerabilitiesCount).toBeFalsy(); expect(state.isLoadingVulnerabilitiesCount).toBeFalsy();
}); });
it('should set `errorLoadingData` to `false`', () => {
expect(state.errorLoadingData).toBeFalsy();
});
it('should set `vulnerabilitiesCount`', () => { it('should set `vulnerabilitiesCount`', () => {
expect(state.vulnerabilitiesCount).toBe(payload); expect(state.vulnerabilitiesCount).toBe(payload);
}); });
......
...@@ -6916,6 +6916,18 @@ msgstr "" ...@@ -6916,6 +6916,18 @@ msgstr ""
msgid "Security Dashboard" msgid "Security Dashboard"
msgstr "" msgstr ""
msgid "Security Dashboard|Issue Created"
msgstr ""
msgid "Security Reports|At this time, the security dashboard only supports SAST. More analyzers are coming soon."
msgstr ""
msgid "Security Reports|Security Dashboard Roadmap"
msgstr ""
msgid "Security Reports|There was an error fetching the dashboard. Please try again in a few moments or contact your support team."
msgstr ""
msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities." msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
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