Commit 22cb9cd3 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch...

Merge branch '217368-dast-vulnerabilities-show-more-information-about-the-request-standalone-page' into 'master'

Add request/response to standalone vulnerability page

See merge request gitlab-org/gitlab!34811
parents 2fdae334 fadaf922
......@@ -19,10 +19,10 @@ export default {
<li :data-testid="valueName">
<gl-sprintf :message="sprintfMessage">
<template #label="{ content }">
<strong>{{ content }}</strong>
<strong data-testid="label">{{ content }}</strong>
</template>
<template #[valueName]>
<slot></slot>
<span data-testid="value"><slot></slot></span>
</template>
</gl-sprintf>
</li>
......
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import CodeBlock from '~/vue_shared/components/code_block.vue';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import { __ } from '~/locale';
import DetailItem from './detail_item.vue';
export default {
name: 'VulnerabilityDetails',
components: { GlLink, SeverityBadge, DetailItem, GlSprintf },
components: { CodeBlock, GlLink, SeverityBadge, DetailItem, GlSprintf },
props: {
vulnerability: {
type: Object,
......@@ -48,6 +50,49 @@ export default {
properties: {},
};
},
requestData() {
const { request: { method, url, headers = [] } = {} } = this.vulnerability;
return [
{
label: __('%{labelStart}Method:%{labelEnd} %{method}'),
content: method,
},
{
label: __('%{labelStart}URL:%{labelEnd} %{url}'),
content: url,
},
{
label: __('%{labelStart}Headers:%{labelEnd} %{headers}'),
content: this.getHeadersAsCodeBlockLines(headers),
isCode: true,
},
].filter(x => x.content);
},
responseData() {
const {
response: { status_code: statusCode, reason_phrase: reasonPhrase, headers = [] } = {},
} = this.vulnerability;
return [
{
label: __('%{labelStart}Status:%{labelEnd} %{status}'),
content: statusCode && reasonPhrase ? `${statusCode} ${reasonPhrase}` : '',
},
{
label: __('%{labelStart}Headers:%{labelEnd} %{headers}'),
content: this.getHeadersAsCodeBlockLines(headers),
isCode: true,
},
].filter(x => x.content);
},
},
methods: {
getHeadersAsCodeBlockLines(headers) {
return Array.isArray(headers)
? headers.map(({ name, value }) => `${name}: ${value}`).join('\n')
: '';
},
},
};
</script>
......@@ -156,5 +201,37 @@ export default {
</li>
</ul>
</template>
<section v-if="requestData.length" data-testid="request">
<h3>{{ s__('Vulnerability|Request') }}</h3>
<ul>
<detail-item
v-for="{ label, isCode, content } in requestData"
:key="label"
:sprintf-message="label"
>
<code-block v-if="isCode" class="mt-1" :code="content" max-height="225px" />
<template v-else>
{{ content }}
</template>
</detail-item>
</ul>
</section>
<section v-if="responseData.length" data-testid="response">
<h3>{{ s__('Vulnerability|Response') }}</h3>
<ul>
<detail-item
v-for="{ label, isCode, content } in responseData"
:key="label"
:sprintf-message="label"
>
<code-block v-if="isCode" class="mt-1" :code="content" max-height="225px" />
<template v-else>
{{ content }}
</template>
</detail-item>
</ul>
</section>
</div>
</template>
......@@ -53,7 +53,9 @@ module VulnerabilitiesHelper
:remediations,
:evidence,
:scanner,
:solution
:solution,
:request,
:response
)
if data[:location]['file']
......
---
title: Add request/response to standalone vulnerability page
merge_request: 34811
author:
type: added
import { mount } from '@vue/test-utils';
import { getAllByRole, getByTestId } from '@testing-library/dom';
import { GlLink } from '@gitlab/ui';
import SeverityBadge from 'ee/vue_shared/security_reports/components/severity_badge.vue';
import VulnerabilityDetails from 'ee/vulnerabilities/components/details.vue';
......@@ -169,4 +170,56 @@ describe('Vulnerability Details', () => {
expect(link().attributes('href')).toBe('//link');
});
});
describe('http data', () => {
const TEST_HEADERS = [{ name: 'Name1', value: 'Value1' }, { name: 'Name2', value: 'Value2' }];
const TEST_URL = 'http://foo.bar/test';
const EXPECT_HEADERS = {
label: 'Headers:',
content: 'Name1: Value1\nName2: Value2',
isCode: true,
};
const getTextContent = el => el.textContent.trim();
const getLabel = el => getTextContent(getByTestId(el, 'label'));
const getContent = el => getTextContent(getByTestId(el, 'value'));
const getSectionData = testId => {
const section = getById(testId).element;
if (!section) {
return null;
}
return getAllByRole(section, 'listitem').map(li => ({
label: getLabel(li),
content: getContent(li),
...(li.querySelector('code') ? { isCode: true } : {}),
}));
};
it.each`
request | expectedData
${{}} | ${null}
${{ headers: TEST_HEADERS }} | ${[EXPECT_HEADERS]}
${{ headers: TEST_HEADERS, method: 'GET' }} | ${[{ label: 'Method:', content: 'GET' }, EXPECT_HEADERS]}
${{ headers: TEST_HEADERS, method: 'GET', url: TEST_URL }} | ${[{ label: 'Method:', content: 'GET' }, { label: 'URL:', content: TEST_URL }, EXPECT_HEADERS]}
${{ url: TEST_URL }} | ${[{ label: 'URL:', content: TEST_URL }]}
${{ method: 'GET' }} | ${[{ label: 'Method:', content: 'GET' }]}
`('shows request data for $request', ({ request, expectedData }) => {
createWrapper({ request });
expect(getSectionData('request')).toEqual(expectedData);
});
it.each`
response | expectedData
${{}} | ${null}
${{ headers: TEST_HEADERS }} | ${[EXPECT_HEADERS]}
${{ headers: TEST_HEADERS, status_code: 200 }} | ${[EXPECT_HEADERS]}
${{ headers: TEST_HEADERS, status_code: 200, reason_phrase: 'OK' }} | ${[{ label: 'Status:', content: '200 OK' }, EXPECT_HEADERS]}
${{ status_code: 400, reason_phrase: 'Something bad' }} | ${[{ label: 'Status:', content: '400 Something bad' }]}
`('shows response data for $response', ({ response, expectedData }) => {
createWrapper({ response });
expect(getSectionData('response')).toEqual(expectedData);
});
});
});
......@@ -115,7 +115,9 @@ RSpec.describe VulnerabilitiesHelper do
remediations: finding.remediations,
solution: kind_of(String),
evidence: kind_of(String),
scanner: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder)
scanner: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
request: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
response: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder)
)
expect(subject[:location]['blob_path']).to match(kind_of(String))
......
......@@ -409,6 +409,9 @@ msgstr ""
msgid "%{labelStart}File:%{labelEnd} %{file}"
msgstr ""
msgid "%{labelStart}Headers:%{labelEnd} %{headers}"
msgstr ""
msgid "%{labelStart}Image:%{labelEnd} %{image}"
msgstr ""
......@@ -427,6 +430,12 @@ msgstr ""
msgid "%{labelStart}Severity:%{labelEnd} %{severity}"
msgstr ""
msgid "%{labelStart}Status:%{labelEnd} %{status}"
msgstr ""
msgid "%{labelStart}URL:%{labelEnd} %{url}"
msgstr ""
msgid "%{label_for_message} unavailable"
msgstr ""
......@@ -25683,6 +25692,12 @@ msgstr ""
msgid "Vulnerability|Project"
msgstr ""
msgid "Vulnerability|Request"
msgstr ""
msgid "Vulnerability|Response"
msgstr ""
msgid "Vulnerability|Scanner"
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