Commit aa2d6c24 authored by Phil Hughes's avatar Phil Hughes

Merge branch '4310-security-reports-step-1' into 'master'

Break security issues report component into smaller ones

See merge request gitlab-org/gitlab-ee!5038
parents cac6a815 0a742530
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import ReportSection from 'ee/vue_shared/security_reports/components/report_section.vue'; import ReportSection from 'ee/vue_shared/security_reports/components/report_section.vue';
import securityMixin from 'ee/vue_shared/security_reports/mixins/security_report_mixin'; import securityMixin from 'ee/vue_shared/security_reports/mixins/security_report_mixin';
import LoadingIcon from '~/vue_shared/components/loading_icon.vue'; import LoadingIcon from '~/vue_shared/components/loading_icon.vue';
import { SAST } from 'ee/vue_shared/security_reports/helpers/constants';
export default { export default {
name: 'SecurityReportTab', name: 'SecurityReportTab',
...@@ -9,9 +10,8 @@ ...@@ -9,9 +10,8 @@
LoadingIcon, LoadingIcon,
ReportSection, ReportSection,
}, },
mixins: [ mixins: [securityMixin],
securityMixin, sast: SAST,
],
props: { props: {
securityReports: { securityReports: {
type: Object, type: Object,
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<div class="pipeline-tab-content"> <div class="pipeline-tab-content">
<report-section <report-section
class="js-sast-widget" class="js-sast-widget"
type="security" :type="$options.sast"
:status="checkReportStatus(securityReports.sast.isLoading, securityReports.sast.hasError)" :status="checkReportStatus(securityReports.sast.isLoading, securityReports.sast.hasError)"
:loading-text="translateText('security').loading" :loading-text="translateText('security').loading"
:error-text="translateText('security').error" :error-text="translateText('security').error"
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
:unresolved-issues="securityReports.sast.newIssues" :unresolved-issues="securityReports.sast.newIssues"
:resolved-issues="securityReports.sast.resolvedIssues" :resolved-issues="securityReports.sast.resolvedIssues"
:all-issues="securityReports.sast.allIssues" :all-issues="securityReports.sast.allIssues"
:has-priority="true"
:is-collapsible="false" :is-collapsible="false"
/> />
</div> </div>
......
<script>
/**
* Renders Code quality body text
* Fixed: [name] in [link]:[line]
*/
import ReportLink from 'ee/vue_shared/security_reports/components/report_link.vue';
export default {
name: 'CodequalityIssueBody',
components: {
ReportLink,
},
props: {
isStatusSuccess: {
type: Boolean,
required: true,
},
issue: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="isStatusSuccess">{{ s__('ciReport|Fixed:') }}</template>
{{ issue.name }}
</div>
<report-link
v-if="issue.path"
:issue="issue"
/>
</div>
</template>
<script>
/**
* Renders Perfomance issue body text
* [name] :[score] [symbol] [delta] in [link]
*/
import ReportLink from 'ee/vue_shared/security_reports/components/report_link.vue';
export default {
name: 'PerformanceIssueBody',
components: {
ReportLink,
},
props: {
issue: {
type: Object,
required: true,
},
},
methods: {
formatScore(value) {
if (Math.floor(value) !== value) {
return parseFloat(value).toFixed(2);
}
return value;
},
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
{{ issue.name }}<template v-if="issue.score">:
<strong>{{ formatScore(issue.score) }}</strong></template>
<template v-if="issue.delta != null">
({{ issue.delta >= 0 ? '+' : '' }}{{ formatScore(issue.delta) }})
</template>
</div>
<report-link
v-if="issue.path"
:issue="issue"
/>
</div>
</template>
...@@ -4,6 +4,11 @@ import WidgetApprovals from './components/approvals/mr_widget_approvals'; ...@@ -4,6 +4,11 @@ import WidgetApprovals from './components/approvals/mr_widget_approvals';
import GeoSecondaryNode from './components/states/mr_widget_secondary_geo_node'; import GeoSecondaryNode from './components/states/mr_widget_secondary_geo_node';
import ReportSection from '../vue_shared/security_reports/components/report_section.vue'; import ReportSection from '../vue_shared/security_reports/components/report_section.vue';
import securityMixin from '../vue_shared/security_reports/mixins/security_report_mixin'; import securityMixin from '../vue_shared/security_reports/mixins/security_report_mixin';
import {
SAST,
DAST,
SAST_CONTAINER,
} from '../vue_shared/security_reports/helpers/constants';
export default { export default {
extends: CEWidgetOptions, extends: CEWidgetOptions,
...@@ -15,6 +20,9 @@ export default { ...@@ -15,6 +20,9 @@ export default {
mixins: [ mixins: [
securityMixin, securityMixin,
], ],
dast: DAST,
sast: SAST,
sastContainer: SAST_CONTAINER,
data() { data() {
return { return {
isLoadingCodequality: false, isLoadingCodequality: false,
...@@ -318,7 +326,7 @@ export default { ...@@ -318,7 +326,7 @@ export default {
:success-text="codequalityText" :success-text="codequalityText"
:unresolved-issues="mr.codeclimateMetrics.newIssues" :unresolved-issues="mr.codeclimateMetrics.newIssues"
:resolved-issues="mr.codeclimateMetrics.resolvedIssues" :resolved-issues="mr.codeclimateMetrics.resolvedIssues"
/> />
<report-section <report-section
class="js-performance-widget" class="js-performance-widget"
v-if="shouldRenderPerformance" v-if="shouldRenderPerformance"
...@@ -330,11 +338,11 @@ export default { ...@@ -330,11 +338,11 @@ export default {
:unresolved-issues="mr.performanceMetrics.degraded" :unresolved-issues="mr.performanceMetrics.degraded"
:resolved-issues="mr.performanceMetrics.improved" :resolved-issues="mr.performanceMetrics.improved"
:neutral-issues="mr.performanceMetrics.neutral" :neutral-issues="mr.performanceMetrics.neutral"
/> />
<report-section <report-section
class="js-sast-widget" class="js-sast-widget"
v-if="shouldRenderSecurityReport" v-if="shouldRenderSecurityReport"
type="security" :type="$options.sast"
:status="securityStatus" :status="securityStatus"
:loading-text="translateText('security').loading" :loading-text="translateText('security').loading"
:error-text="translateText('security').error" :error-text="translateText('security').error"
...@@ -342,12 +350,11 @@ export default { ...@@ -342,12 +350,11 @@ export default {
:unresolved-issues="mr.securityReport.newIssues" :unresolved-issues="mr.securityReport.newIssues"
:resolved-issues="mr.securityReport.resolvedIssues" :resolved-issues="mr.securityReport.resolvedIssues"
:all-issues="mr.securityReport.allIssues" :all-issues="mr.securityReport.allIssues"
:has-priority="true"
/> />
<report-section <report-section
class="js-docker-widget" class="js-docker-widget"
v-if="shouldRenderDockerReport" v-if="shouldRenderDockerReport"
type="docker" :type="$options.sastContainer"
:status="dockerStatus" :status="dockerStatus"
:loading-text="translateText('sast:container').loading" :loading-text="translateText('sast:container').loading"
:error-text="translateText('sast:container').error" :error-text="translateText('sast:container').error"
...@@ -355,19 +362,17 @@ export default { ...@@ -355,19 +362,17 @@ export default {
:unresolved-issues="mr.dockerReport.unapproved" :unresolved-issues="mr.dockerReport.unapproved"
:neutral-issues="mr.dockerReport.approved" :neutral-issues="mr.dockerReport.approved"
:info-text="sastContainerInformationText()" :info-text="sastContainerInformationText()"
:has-priority="true" />
/>
<report-section <report-section
class="js-dast-widget" class="js-dast-widget"
v-if="shouldRenderDastReport" v-if="shouldRenderDastReport"
type="dast" :type="$options.dast"
:status="dastStatus" :status="dastStatus"
:loading-text="translateText('DAST').loading" :loading-text="translateText('DAST').loading"
:error-text="translateText('DAST').error" :error-text="translateText('DAST').error"
:success-text="getDastText" :success-text="getDastText"
:unresolved-issues="mr.dastReport" :unresolved-issues="mr.dastReport"
:has-priority="true" />
/>
<div class="mr-widget-section"> <div class="mr-widget-section">
<component <component
:is="componentName" :is="componentName"
......
<script>
/**
* Renders DAST body text
* [priority]: [name]
*/
export default {
name: 'SastIssueBody',
props: {
issue: {
type: Object,
required: true,
},
issueIndex: {
type: Number,
required: true,
},
modalTargetId: {
type: String,
required: true,
},
},
methods: {
openDastModal() {
this.$emit('openDastModal', this.issue, this.issueIndex);
},
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="issue.priority">{{ issue.priority }}:</template>
<button
type="button"
@click="openDastModal()"
data-toggle="modal"
class="js-modal-dast btn-link btn-blank text-left break-link"
:data-target="modalTargetId"
>
{{ issue.name }}
</button>
</div>
</div>
</template>
<script> <script>
import $ from 'jquery'; import $ from 'jquery';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import Modal from '~/vue_shared/components/gl_modal.vue'; import Modal from '~/vue_shared/components/gl_modal.vue';
import ExpandButton from '~/vue_shared/components/expand_button.vue'; import ExpandButton from '~/vue_shared/components/expand_button.vue';
import PerformanceIssue from 'ee/vue_merge_request_widget/components/performance_issue_body.vue';
import CodequalityIssue from 'ee/vue_merge_request_widget/components/codequality_issue_body.vue';
import SastIssue from './sast_issue_body.vue';
import SastContainerIssue from './sast_container_issue_body.vue';
import DastIssue from './dast_issue_body.vue';
import { SAST, DAST, SAST_CONTAINER } from '../helpers/constants';
const modalDefaultData = { const modalDefaultData = {
modalId: 'modal-mrwidget-issue', modalId: 'modal-mrwidget-issue',
...@@ -19,6 +25,11 @@ ...@@ -19,6 +25,11 @@
Modal, Modal,
Icon, Icon,
ExpandButton, ExpandButton,
SastIssue,
SastContainerIssue,
DastIssue,
PerformanceIssue,
CodequalityIssue,
}, },
props: { props: {
issues: { issues: {
...@@ -35,19 +46,11 @@ ...@@ -35,19 +46,11 @@
type: String, type: String,
required: true, required: true,
}, },
hasPriority: {
type: Boolean,
required: false,
default: false,
},
}, },
data() { data() {
return modalDefaultData; return modalDefaultData;
}, },
computed: { computed: {
fixedLabel() {
return s__('ciReport|Fixed:');
},
iconName() { iconName() {
if (this.isStatusFailed) { if (this.isStatusFailed) {
return 'status_failed_borderless'; return 'status_failed_borderless';
...@@ -66,20 +69,20 @@ ...@@ -66,20 +69,20 @@
isStatusNeutral() { isStatusNeutral() {
return this.status === 'neutral'; return this.status === 'neutral';
}, },
isTypeQuality() { isTypeCodequality() {
return this.type === 'codequality'; return this.type === 'codequality';
}, },
isTypePerformance() { isTypePerformance() {
return this.type === 'performance'; return this.type === 'performance';
}, },
isTypeSecurity() { isTypeSast() {
return this.type === 'security'; return this.type === SAST;
}, },
isTypeDocker() { isTypeSastContainer() {
return this.type === 'docker'; return this.type === SAST_CONTAINER;
}, },
isTypeDast() { isTypeDast() {
return this.type === 'dast'; return this.type === DAST;
}, },
}, },
mounted() { mounted() {
...@@ -88,21 +91,12 @@ ...@@ -88,21 +91,12 @@
}); });
}, },
methods: { methods: {
shouldRenderPriority(issue) {
return this.hasPriority && issue.priority;
},
getmodalId(index) { getmodalId(index) {
return `modal-mrwidget-issue-${index}`; return `modal-mrwidget-issue-${index}`;
}, },
modalIdTarget(index) { modalIdTarget(index) {
return `#${this.getmodalId(index)}`; return `#${this.getmodalId(index)}`;
}, },
formatScore(value) {
if (Math.floor(value) !== value) {
return parseFloat(value).toFixed(2);
}
return value;
},
openDastModal(issue, index) { openDastModal(issue, index) {
this.modalId = this.getmodalId(index); this.modalId = this.getmodalId(index);
this.modalTitle = `${issue.priority}: ${issue.name}`; this.modalTitle = `${issue.priority}: ${issue.name}`;
...@@ -145,61 +139,35 @@ ...@@ -145,61 +139,35 @@
:size="32" :size="32"
/> />
</div> </div>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="isStatusSuccess && isTypeQuality">{{ fixedLabel }}</template>
<template v-if="shouldRenderPriority(issue)">{{ issue.priority }}:</template>
<template v-if="isTypeDocker"> <sast-issue
<a v-if="isTypeSast"
v-if="issue.nameLink" :issue="issue"
:href="issue.nameLink" />
target="_blank"
rel="noopener noreferrer nofollow"
>{{ issue.name }}</a>
<template v-else>
{{ issue.name }}
</template>
</template>
<template v-else-if="isTypeDast">
<button
type="button"
@click="openDastModal(issue, index)"
data-toggle="modal"
class="js-modal-dast btn-link btn-blank text-left break-link"
:data-target="modalTargetId"
>
{{ issue.name }}
</button>
</template>
<template v-else>
{{ issue.name }}<template v-if="issue.score">:
<strong>{{ formatScore(issue.score) }}</strong></template>
</template>
<template v-if="isTypePerformance && issue.delta != null"> <dast-issue
({{ issue.delta >= 0 ? '+' : '' }}{{ formatScore(issue.delta) }}) v-else-if="isTypeDast"
</template> :issue="issue"
</div> :issue-index="index"
<div class="report-block-list-issue-description-link"> :modal-target-id="modalTargetId"
<template v-if="issue.path"> @openDastModal="openDastModal"
in />
<a <sast-container-issue
v-if="issue.urlPath" v-else-if="isTypeSastContainer"
:href="issue.urlPath" :issue="issue"
target="_blank" />
rel="noopener noreferrer nofollow"
class="break-link" <codequality-issue
> v-else-if="isTypeCodequality"
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template> :is-status-success="isStatusSuccess"
</a> :issue="issue"
<template v-else> />
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</template> <performance-issue
</template> v-else-if="isTypePerformance"
</div> :issue="issue"
</div> />
</li> </li>
</ul> </ul>
......
<script>
export default {
name: 'ReportIssueLink',
props: {
issue: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="report-block-list-issue-description-link">
in
<a
v-if="issue.urlPath"
:href="issue.urlPath"
target="_blank"
rel="noopener noreferrer nofollow"
class="break-link"
>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</a>
<template v-else>
{{ issue.path }}<template v-if="issue.line">:{{ issue.line }}</template>
</template>
</div>
</template>
...@@ -178,7 +178,7 @@ ...@@ -178,7 +178,7 @@
> >
<p <p
v-if="type === 'docker' && infoText" v-if="infoText"
v-html="infoText" v-html="infoText"
class="js-mr-code-quality-info prepend-left-10 report-block-info" class="js-mr-code-quality-info prepend-left-10 report-block-info"
> >
......
<script>
/**
* Renders SAST CONTAINER body text
* [priority]: [name|link] in [link]:[line]
*/
import ReportLink from './report_link.vue';
export default {
name: 'SastContainerIssueBody',
components: {
ReportLink,
},
props: {
issue: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="issue.priority">{{ issue.priority }}:</template>
<a
v-if="issue.nameLink"
:href="issue.nameLink"
target="_blank"
rel="noopener noreferrer nofollow"
>
{{ issue.name }}
</a>
<template v-else>
{{ issue.name }}
</template>
</div>
<report-link
v-if="issue.path"
:issue="issue"
/>
</div>
</template>
<script>
/**
* Renders SAST body text
* [priority]: [name] in [link] : [line]
*/
import ReportLink from './report_link.vue';
export default {
name: 'SastIssueBody',
components: {
ReportLink,
},
props: {
issue: {
type: Object,
required: true,
},
},
};
</script>
<template>
<div class="report-block-list-issue-description prepend-top-5 append-bottom-5">
<div class="report-block-list-issue-description-text append-right-5">
<template v-if="issue.priority">{{ issue.priority }}:</template>
{{ issue.name }}
</div>
<report-link
v-if="issue.path"
:issue="issue"
/>
</div>
</template>
export const SAST = 'SAST';
export const DAST = 'DAST';
export const SAST_CONTAINER = 'SAST_CONTAINER';
import Vue from 'vue';
import component from 'ee/vue_merge_request_widget/components/codequality_issue_body.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('sast issue body', () => {
let vm;
const Component = Vue.extend(component);
const codequalityIssue = {
name:
'rubygem-rest-client: session fixation vulnerability via Set-Cookie headers in 30x redirection responses',
path: 'Gemfile.lock',
severity: 'normal',
type: 'Issue',
urlPath: '/Gemfile.lock#L22',
};
afterEach(() => {
vm.$destroy();
});
describe('with success', () => {
it('renders fixed label', () => {
vm = mountComponent(Component, {
issue: codequalityIssue,
isStatusSuccess: true,
});
expect(vm.$el.textContent.trim()).toContain('Fixed');
});
});
describe('without success', () => {
it('renders fixed label', () => {
vm = mountComponent(Component, {
issue: codequalityIssue,
isStatusSuccess: false,
});
expect(vm.$el.textContent.trim()).not.toContain('Fixed');
});
});
describe('name', () => {
it('renders name', () => {
vm = mountComponent(Component, {
issue: codequalityIssue,
isStatusSuccess: false,
});
expect(vm.$el.textContent.trim()).toContain(codequalityIssue.name);
});
});
describe('path', () => {
it('renders name', () => {
vm = mountComponent(Component, {
issue: codequalityIssue,
isStatusSuccess: false,
});
expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(
codequalityIssue.urlPath,
);
expect(vm.$el.querySelector('a').textContent.trim()).toEqual(
codequalityIssue.path,
);
});
});
});
import Vue from 'vue';
import component from 'ee/vue_merge_request_widget/components/performance_issue_body.vue';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('performance issue body', () => {
let vm;
const Component = Vue.extend(component);
const performanceIssue = {
delta: 0.1999999999998181,
name: 'Transfer Size (KB)',
path: '/',
score: 4974.8,
};
afterEach(() => {
vm.$destroy();
});
beforeEach(() => {
vm = mountComponent(Component, {
issue: performanceIssue,
});
});
it('renders issue name', () => {
expect(vm.$el.textContent.trim()).toContain(performanceIssue.name);
});
it('renders issue score formatted', () => {
expect(vm.$el.textContent.trim()).toContain('4974.80');
});
it('renders issue delta formatted', () => {
expect(vm.$el.textContent.trim()).toContain('(+0.20)');
});
});
import Vue from 'vue';
import component from 'ee/vue_shared/security_reports/components/dast_issue_body.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('dast issue body', () => {
let vm;
const Component = Vue.extend(component);
const dastIssue = {
alert: 'X-Content-Type-Options Header Missing',
confidence: '2',
count: '17',
cweid: '16',
desc:
'<p>The Anti-MIME-Sniffing header X-Content-Type-Options was not set to "nosniff". </p>',
name: 'X-Content-Type-Options Header Missing',
parsedDescription:
' The Anti-MIME-Sniffing header X-Content-Type-Options was not set to "nosniff". ',
priority: 'Low (Medium)',
reference:
'<p>http://msdn.microsoft.com/en-us/library/ie/gg622941%28v=vs.85%29.aspx</p><p>https://www.owasp.org/index.php/List_of_useful_HTTP_headers</p>',
riskcode: '1',
riskdesc: 'Low (Medium)',
};
afterEach(() => {
vm.$destroy();
});
describe('with priority', () => {
it('renders priority key', () => {
vm = mountComponent(Component, {
issue: dastIssue,
issueIndex: 1,
modalTargetId: '#modal-mrwidget-issue',
});
expect(vm.$el.textContent.trim()).toContain(dastIssue.priority);
});
});
describe('without priority', () => {
it('does not rendere priority key', () => {
const issueCopy = Object.assign({}, dastIssue);
delete issueCopy.priority;
vm = mountComponent(Component, {
issue: issueCopy,
issueIndex: 1,
modalTargetId: '#modal-mrwidget-issue',
});
expect(vm.$el.textContent.trim()).not.toContain(dastIssue.priority);
});
});
describe('issue name', () => {
beforeEach(() => {
vm = mountComponent(Component, {
issue: dastIssue,
issueIndex: 1,
modalTargetId: '#modal-mrwidget-issue',
});
});
it('renders issue name', () => {
expect(vm.$el.textContent.trim()).toContain(dastIssue.name);
});
it('renders button to open modal box', () => {
const button = vm.$el.querySelector('.js-modal-dast');
expect(button.getAttribute('data-toggle')).toEqual('modal');
expect(button.getAttribute('data-target')).toEqual('#modal-mrwidget-issue');
});
it('emits event when button is clicked', () => {
spyOn(vm, '$emit');
vm.$el.querySelector('.js-modal-dast').click();
expect(vm.$emit).toHaveBeenCalledWith('openDastModal', dastIssue, 1);
});
});
});
...@@ -67,26 +67,21 @@ describe('Report issues', () => { ...@@ -67,26 +67,21 @@ describe('Report issues', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ReportIssues, { vm = mountComponent(ReportIssues, {
issues: sastParsedIssues, issues: sastParsedIssues,
type: 'security', type: 'SAST',
status: 'failed', status: 'failed',
hasPriority: true,
}); });
}); });
it('should render a list of unresolved issues', () => { it('should render a list of unresolved issues', () => {
expect(vm.$el.querySelectorAll('.report-block-list li').length).toEqual(sastParsedIssues.length); expect(vm.$el.querySelectorAll('.report-block-list li').length).toEqual(sastParsedIssues.length);
}); });
it('should render priority', () => {
expect(vm.$el.querySelector('.report-block-list li').textContent).toContain(sastParsedIssues[0].priority);
});
}); });
describe('with location', () => { describe('with location', () => {
it('should render location', () => { it('should render location', () => {
vm = mountComponent(ReportIssues, { vm = mountComponent(ReportIssues, {
issues: sastParsedIssues, issues: sastParsedIssues,
type: 'security', type: 'SAST',
status: 'failed', status: 'failed',
}); });
...@@ -101,7 +96,7 @@ describe('Report issues', () => { ...@@ -101,7 +96,7 @@ describe('Report issues', () => {
issues: [{ issues: [{
name: 'foo', name: 'foo',
}], }],
type: 'security', type: 'SAST',
status: 'failed', status: 'failed',
}); });
...@@ -114,9 +109,8 @@ describe('Report issues', () => { ...@@ -114,9 +109,8 @@ describe('Report issues', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ReportIssues, { vm = mountComponent(ReportIssues, {
issues: dockerReportParsed.unapproved, issues: dockerReportParsed.unapproved,
type: 'docker', type: 'SAST_CONTAINER',
status: 'failed', status: 'failed',
hasPriority: true,
}); });
}); });
...@@ -149,9 +143,8 @@ describe('Report issues', () => { ...@@ -149,9 +143,8 @@ describe('Report issues', () => {
beforeEach(() => { beforeEach(() => {
vm = mountComponent(ReportIssues, { vm = mountComponent(ReportIssues, {
issues: parsedDast, issues: parsedDast,
type: 'dast', type: 'DAST',
status: 'failed', status: 'failed',
hasPriority: true,
}); });
}); });
......
import Vue from 'vue';
import component from 'ee/vue_shared/security_reports/components/report_link.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('report link', () => {
let vm;
const Component = Vue.extend(component);
afterEach(() => {
vm.$destroy();
});
describe('With url', () => {
it('renders link', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
urlPath: '/Gemfile.lock',
},
});
expect(vm.$el.textContent.trim()).toContain('in');
expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('/Gemfile.lock');
expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Gemfile.lock');
});
});
describe('Without url', () => {
it('does not render link', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
},
});
expect(vm.$el.querySelector('a')).toBeNull();
expect(vm.$el.textContent.trim()).toContain('in');
expect(vm.$el.textContent.trim()).toContain('Gemfile.lock');
});
});
describe('with line', () => {
it('renders line number', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
urlPath:
'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
line: 22,
},
});
expect(vm.$el.querySelector('a').textContent.trim()).toContain('Gemfile.lock:22');
});
});
describe('without line', () => {
it('does not render line number', () => {
vm = mountComponent(Component, {
issue: {
path: 'Gemfile.lock',
urlPath:
'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
},
});
expect(vm.$el.querySelector('a').textContent.trim()).not.toContain(':22');
});
});
});
...@@ -104,7 +104,7 @@ describe('Report section', () => { ...@@ -104,7 +104,7 @@ describe('Report section', () => {
vm = mountComponent(ReportSection, { vm = mountComponent(ReportSection, {
status: 'success', status: 'success',
successText: 'SAST improved on 1 security vulnerability and degraded on 1 security vulnerability', successText: 'SAST improved on 1 security vulnerability and degraded on 1 security vulnerability',
type: 'security', type: 'SAST',
errorText: 'Failed to load security report', errorText: 'Failed to load security report',
hasPriority: true, hasPriority: true,
loadingText: 'Loading security report', loadingText: 'Loading security report',
......
import Vue from 'vue';
import component from 'ee/vue_shared/security_reports/components/sast_container_issue_body.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('sast container issue body', () => {
let vm;
const Component = Vue.extend(component);
const sastContainerIssue = {
name: 'CVE-2017-11671',
nameLink: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11671',
namespace: 'debian:8',
path: 'debian:8',
priority: 'Low',
severity: 'Low',
vulnerability: 'CVE-2017-11671',
};
afterEach(() => {
vm.$destroy();
});
describe('with priority', () => {
it('renders priority key', () => {
vm = mountComponent(Component, {
issue: sastContainerIssue,
});
expect(vm.$el.textContent.trim()).toContain(sastContainerIssue.priority);
});
});
describe('without priority', () => {
it('does not rendere priority key', () => {
const issueCopy = Object.assign({}, sastContainerIssue);
delete issueCopy.priority;
vm = mountComponent(Component, {
issue: issueCopy,
});
expect(vm.$el.textContent.trim()).not.toContain(sastContainerIssue.priority);
});
});
describe('with name link', () => {
it('renders name link', () => {
vm = mountComponent(Component, {
issue: sastContainerIssue,
});
expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(sastContainerIssue.nameLink);
expect(vm.$el.querySelector('a').textContent.trim()).toEqual(sastContainerIssue.name);
});
});
describe('without name link', () => {
it('does not render name link', () => {
const issueCopy = Object.assign({}, sastContainerIssue);
delete issueCopy.nameLink;
vm = mountComponent(Component, {
issue: issueCopy,
});
expect(vm.$el.querySelector('a')).toBeNull();
expect(vm.$el.textContent.trim()).toContain(sastContainerIssue.name);
});
});
describe('path', () => {
it('renders path', () => {
vm = mountComponent(Component, {
issue: sastContainerIssue,
});
expect(vm.$el.textContent.trim()).toContain(sastContainerIssue.path);
});
});
});
import Vue from 'vue';
import component from 'ee/vue_shared/security_reports/components/sast_issue_body.vue';
import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('sast issue body', () => {
let vm;
const Component = Vue.extend(component);
const sastIssue = {
cve: 'CVE-2016-9999',
file: 'Gemfile.lock',
message: 'Test Information Leak Vulnerability in Action View',
name: 'Test Information Leak Vulnerability in Action View',
path: 'Gemfile.lock',
solution:
'upgrade to >= 5.0.0.beta1.1, >= 4.2.5.1, ~> 4.2.5, >= 4.1.14.1, ~> 4.1.14, ~> 3.2.22.1',
tool: 'bundler_audit',
url:
'https://groups.google.com/forum/#!topic/rubyonrails-security/335P1DcLG00',
urlPath: '/Gemfile.lock',
priority: 'Low',
};
afterEach(() => {
vm.$destroy();
});
describe('with priority', () => {
it('renders priority key', () => {
vm = mountComponent(Component, {
issue: sastIssue,
});
expect(vm.$el.textContent.trim()).toContain(sastIssue.priority);
});
});
describe('without priority', () => {
it('does not rendere priority key', () => {
const issueCopy = Object.assign({}, sastIssue);
delete issueCopy.priority;
vm = mountComponent(Component, {
issue: issueCopy,
});
expect(vm.$el.textContent.trim()).not.toContain(
sastIssue.priority,
);
});
});
describe('name', () => {
it('renders name', () => {
vm = mountComponent(Component, {
issue: sastIssue,
});
expect(vm.$el.textContent.trim()).toContain(
sastIssue.name,
);
});
});
describe('path', () => {
it('renders name', () => {
vm = mountComponent(Component, {
issue: sastIssue,
});
expect(vm.$el.querySelector('a').getAttribute('href')).toEqual(
sastIssue.urlPath,
);
expect(vm.$el.querySelector('a').textContent.trim()).toEqual(
sastIssue.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