Commit e2f971cd authored by Dheeraj Joshi's avatar Dheeraj Joshi Committed by Kushal Pandya

Pipeline Security: Render markdown content for Vulnerability description

parent c7da9ac4
<script> <script>
import { GlFriendlyWrap, GlLink, GlBadge } from '@gitlab/ui'; import { GlFriendlyWrap, GlLink, GlBadge, GlSafeHtmlDirective } from '@gitlab/ui';
import { REPORT_TYPES } from 'ee/security_dashboard/store/constants'; import { REPORT_TYPES } from 'ee/security_dashboard/store/constants';
import FalsePositiveAlert from 'ee/vulnerabilities/components/false_positive_alert.vue'; import FalsePositiveAlert from 'ee/vulnerabilities/components/false_positive_alert.vue';
import GenericReportSection from 'ee/vulnerabilities/components/generic_report/report_section.vue'; import GenericReportSection from 'ee/vulnerabilities/components/generic_report/report_section.vue';
...@@ -24,6 +24,9 @@ export default { ...@@ -24,6 +24,9 @@ export default {
GlBadge, GlBadge,
FalsePositiveAlert, FalsePositiveAlert,
}, },
directives: {
SafeHtml: GlSafeHtmlDirective,
},
props: { vulnerability: { type: Object, required: true } }, props: { vulnerability: { type: Object, required: true } },
computed: { computed: {
url() { url() {
...@@ -126,6 +129,9 @@ export default { ...@@ -126,6 +129,9 @@ export default {
falsePositive() { falsePositive() {
return this.vulnerability.false_positive; return this.vulnerability.false_positive;
}, },
hasDescription() {
return this.vulnerability.description_html || this.vulnerability.description;
},
hasRequest() { hasRequest() {
return Boolean(this.constructedRequest); return Boolean(this.constructedRequest);
}, },
...@@ -178,11 +184,13 @@ export default { ...@@ -178,11 +184,13 @@ export default {
<vulnerability-detail v-if="vulnerability.state" :label="s__('Vulnerability|Status')"> <vulnerability-detail v-if="vulnerability.state" :label="s__('Vulnerability|Status')">
<gl-badge variant="warning" class="text-capitalize">{{ vulnerability.state }}</gl-badge> <gl-badge variant="warning" class="text-capitalize">{{ vulnerability.state }}</gl-badge>
</vulnerability-detail> </vulnerability-detail>
<vulnerability-detail <vulnerability-detail v-if="hasDescription" :label="s__('Vulnerability|Description')">
v-if="vulnerability.description" <p
:label="s__('Vulnerability|Description')" v-if="vulnerability.description_html"
> v-safe-html="vulnerability.description_html"
<gl-friendly-wrap :text="vulnerability.description" /> data-testid="description"
></p>
<p v-else data-testid="description">{{ vulnerability.description }}</p>
</vulnerability-detail> </vulnerability-detail>
<vulnerability-detail v-if="vulnerability.project" :label="s__('Vulnerability|Project')"> <vulnerability-detail v-if="vulnerability.project" :label="s__('Vulnerability|Project')">
<gl-link ref="projectLink" :href="vulnerability.project.full_path" target="_blank"> <gl-link ref="projectLink" :href="vulnerability.project.full_path" target="_blank">
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class Vulnerabilities::FindingEntity < Grape::Entity class Vulnerabilities::FindingEntity < Grape::Entity
include RequestAwareEntity include RequestAwareEntity
include VulnerabilitiesHelper include VulnerabilitiesHelper
include MarkupHelper
expose :id, :report_type, :name, :severity, :confidence expose :id, :report_type, :name, :severity, :confidence
expose :scanner, using: Vulnerabilities::ScannerEntity expose :scanner, using: Vulnerabilities::ScannerEntity
...@@ -32,6 +33,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity ...@@ -32,6 +33,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity
expose :metadata, merge: true, if: ->(occurrence, _) { occurrence.raw_metadata } do expose :metadata, merge: true, if: ->(occurrence, _) { occurrence.raw_metadata } do
expose :description expose :description
expose :description_html do |model|
markdown(model.description)
end
expose :links expose :links
expose :location expose :location
expose :remediations expose :remediations
......
...@@ -21,10 +21,11 @@ exports[`VulnerabilityDetails component pin test renders correctly 1`] = ` ...@@ -21,10 +21,11 @@ exports[`VulnerabilityDetails component pin test renders correctly 1`] = `
<vulnerability-detail-stub <vulnerability-detail-stub
label="Description" label="Description"
> >
<gl-friendly-wrap-stub <p
symbols="/" data-testid="description"
text="The serialize-javascript npm package is vulnerable to Cross-site Scripting (XSS). It does not properly mitigate against unsafe characters in serialized regular expressions. If serialized data of regular expression objects are used in an environment other than Node.js, it is affected by this vulnerability." >
/> The serialize-javascript npm package is vulnerable to Cross-site Scripting (XSS). It does not properly mitigate against unsafe characters in serialized regular expressions. If serialized data of regular expression objects are used in an environment other than Node.js, it is affected by this vulnerability.
</p>
</vulnerability-detail-stub> </vulnerability-detail-stub>
<vulnerability-detail-stub <vulnerability-detail-stub
......
import { GlLink, GlBadge } from '@gitlab/ui'; import { GlLink, GlBadge } from '@gitlab/ui';
import { mount, shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { cloneDeep } from 'lodash'; import { cloneDeep } from 'lodash';
import { EMPTY_BODY_MESSAGE } from 'ee/vue_shared/security_reports/components/constants'; import { EMPTY_BODY_MESSAGE } from 'ee/vue_shared/security_reports/components/constants';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue'; import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
...@@ -7,6 +7,7 @@ import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vuln ...@@ -7,6 +7,7 @@ import VulnerabilityDetails from 'ee/vue_shared/security_reports/components/vuln
import FalsePositiveAlert from 'ee/vulnerabilities/components/false_positive_alert.vue'; import FalsePositiveAlert from 'ee/vulnerabilities/components/false_positive_alert.vue';
import GenericReportSection from 'ee/vulnerabilities/components/generic_report/report_section.vue'; import GenericReportSection from 'ee/vulnerabilities/components/generic_report/report_section.vue';
import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants'; import { SUPPORTING_MESSAGE_TYPES } from 'ee/vulnerabilities/constants';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { TEST_HOST } from 'helpers/test_constants'; import { TEST_HOST } from 'helpers/test_constants';
import { mockFindings } from '../mock_data'; import { mockFindings } from '../mock_data';
...@@ -19,7 +20,7 @@ describe('VulnerabilityDetails component', () => { ...@@ -19,7 +20,7 @@ describe('VulnerabilityDetails component', () => {
let wrapper; let wrapper;
const componentFactory = (vulnerability, provide = {}) => { const componentFactory = (vulnerability, provide = {}) => {
wrapper = mount(VulnerabilityDetails, { wrapper = mountExtended(VulnerabilityDetails, {
propsData: { vulnerability }, propsData: { vulnerability },
provide, provide,
}); });
...@@ -44,6 +45,7 @@ describe('VulnerabilityDetails component', () => { ...@@ -44,6 +45,7 @@ describe('VulnerabilityDetails component', () => {
const findStacktraceSnippet = () => wrapper.find({ ref: 'stacktraceSnippet' }); const findStacktraceSnippet = () => wrapper.find({ ref: 'stacktraceSnippet' });
const findGenericReportSection = () => wrapper.findComponent(GenericReportSection); const findGenericReportSection = () => wrapper.findComponent(GenericReportSection);
const findAlert = () => wrapper.findComponent(FalsePositiveAlert); const findAlert = () => wrapper.findComponent(FalsePositiveAlert);
const findDescription = () => wrapper.findByTestId('description');
const USER_NOT_FOUND_MESSAGE = '{"message":"User not found."}'; const USER_NOT_FOUND_MESSAGE = '{"message":"User not found."}';
...@@ -61,6 +63,22 @@ describe('VulnerabilityDetails component', () => { ...@@ -61,6 +63,22 @@ describe('VulnerabilityDetails component', () => {
expect(findAlert().exists()).toBe(true); expect(findAlert().exists()).toBe(true);
}); });
it('renders description text when markdown is not present', () => {
const vulnerability = makeVulnerability();
componentFactory(vulnerability);
const section = findDescription();
expect(section.text()).toBe(vulnerability.description);
});
it('renders description html when markdown is present', () => {
const vulnerability = makeVulnerability({ description_html: '<code>test</code>' });
componentFactory(vulnerability);
const section = findDescription();
expect(section.html()).toContain(vulnerability.description_html);
});
it('renders severity with a badge', () => { it('renders severity with a badge', () => {
const vulnerability = makeVulnerability({ severity: 'critical' }); const vulnerability = makeVulnerability({ severity: 'critical' });
componentFactory(vulnerability); componentFactory(vulnerability);
......
...@@ -306,7 +306,7 @@ RSpec.describe VulnerabilitiesHelper do ...@@ -306,7 +306,7 @@ RSpec.describe VulnerabilitiesHelper do
it 'returns finding information' do it 'returns finding information' do
expect(subject.to_h).to match( expect(subject.to_h).to match(
description: finding.description, description: finding.description,
description_html: anything, description_html: match(%r<p data-sourcepos.*?\<\/p>),
identifiers: kind_of(Array), identifiers: kind_of(Array),
issue_feedback: anything, issue_feedback: anything,
links: finding.links, links: finding.links,
......
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