Commit c00bc663 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'djadmin-meta-tag-validation' into 'master'

Add new method for DAST site validation

See merge request gitlab-org/gitlab!67945
parents 6ab56bdf cd871071
---
name: dast_meta_tag_validation
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67945
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/337711
milestone: '14.2'
type: development
group: group::dynamic analysis
default_enabled: false
...@@ -16,11 +16,13 @@ import download from '~/lib/utils/downloader'; ...@@ -16,11 +16,13 @@ import download from '~/lib/utils/downloader';
import { cleanLeadingSeparator, joinPaths, stripPathTail } from '~/lib/utils/url_utility'; import { cleanLeadingSeparator, joinPaths, stripPathTail } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { import {
DAST_SITE_VALIDATION_MODAL_ID, DAST_SITE_VALIDATION_MODAL_ID,
DAST_SITE_VALIDATION_HTTP_HEADER_KEY, DAST_SITE_VALIDATION_HTTP_HEADER_KEY,
DAST_SITE_VALIDATION_METHOD_HTTP_HEADER, DAST_SITE_VALIDATION_METHOD_HTTP_HEADER,
DAST_SITE_VALIDATION_METHOD_TEXT_FILE, DAST_SITE_VALIDATION_METHOD_TEXT_FILE,
DAST_SITE_VALIDATION_METHOD_META_TAG,
DAST_SITE_VALIDATION_METHODS, DAST_SITE_VALIDATION_METHODS,
} from '../constants'; } from '../constants';
import dastSiteTokenCreateMutation from '../graphql/dast_site_token_create.mutation.graphql'; import dastSiteTokenCreateMutation from '../graphql/dast_site_token_create.mutation.graphql';
...@@ -42,6 +44,7 @@ export default { ...@@ -42,6 +44,7 @@ export default {
GlSkeletonLoader, GlSkeletonLoader,
GlTruncate, GlTruncate,
}, },
mixins: [glFeatureFlagMixin()],
props: { props: {
fullPath: { fullPath: {
type: String, type: String,
...@@ -82,7 +85,11 @@ export default { ...@@ -82,7 +85,11 @@ export default {
}; };
}, },
validationMethodOptions() { validationMethodOptions() {
return Object.values(DAST_SITE_VALIDATION_METHODS); const options = Object.values(DAST_SITE_VALIDATION_METHODS);
if (!this.glFeatures.dastMetaTagValidation) {
return options.filter(({ value }) => value !== DAST_SITE_VALIDATION_METHOD_META_TAG);
}
return options;
}, },
urlObject() { urlObject() {
try { try {
...@@ -103,6 +110,9 @@ export default { ...@@ -103,6 +110,9 @@ export default {
isHttpHeaderValidation() { isHttpHeaderValidation() {
return this.validationMethod === DAST_SITE_VALIDATION_METHOD_HTTP_HEADER; return this.validationMethod === DAST_SITE_VALIDATION_METHOD_HTTP_HEADER;
}, },
isMetaTagValidation() {
return this.validationMethod === DAST_SITE_VALIDATION_METHOD_META_TAG;
},
textFileName() { textFileName() {
return `GitLab-DAST-Site-Validation-${this.token}.txt`; return `GitLab-DAST-Site-Validation-${this.token}.txt`;
}, },
...@@ -112,6 +122,9 @@ export default { ...@@ -112,6 +122,9 @@ export default {
httpHeader() { httpHeader() {
return `${DAST_SITE_VALIDATION_HTTP_HEADER_KEY}: ${this.token}`; return `${DAST_SITE_VALIDATION_HTTP_HEADER_KEY}: ${this.token}`;
}, },
metaTag() {
return `<meta name="gitlab-dast-validation" content="${this.token}">`;
},
}, },
watch: { watch: {
targetUrl: { targetUrl: {
...@@ -253,6 +266,17 @@ export default { ...@@ -253,6 +266,17 @@ export default {
:modal-id="modalProps.id" :modal-id="modalProps.id"
/> />
</gl-form-group> </gl-form-group>
<gl-form-group
v-else-if="isMetaTagValidation"
:label="s__('DastSiteValidation|Step 2 - Add following meta tag to your site')"
>
<code class="gl-p-3 gl-bg-black gl-text-white">{{ metaTag }}</code>
<modal-copy-button
:text="metaTag"
:title="s__('DastSiteValidation|Copy Meta tag to clipboard')"
:modal-id="modalProps.id"
/>
</gl-form-group>
<gl-form-group :label="locationStepLabel" class="mw-460"> <gl-form-group :label="locationStepLabel" class="mw-460">
<gl-form-input-group> <gl-form-input-group>
<template #prepend> <template #prepend>
......
...@@ -2,6 +2,7 @@ import { s__ } from '~/locale'; ...@@ -2,6 +2,7 @@ import { s__ } from '~/locale';
export const DAST_SITE_VALIDATION_METHOD_TEXT_FILE = 'TEXT_FILE'; export const DAST_SITE_VALIDATION_METHOD_TEXT_FILE = 'TEXT_FILE';
export const DAST_SITE_VALIDATION_METHOD_HTTP_HEADER = 'HEADER'; export const DAST_SITE_VALIDATION_METHOD_HTTP_HEADER = 'HEADER';
export const DAST_SITE_VALIDATION_METHOD_META_TAG = 'META_TAG';
export const DAST_SITE_VALIDATION_METHODS = { export const DAST_SITE_VALIDATION_METHODS = {
[DAST_SITE_VALIDATION_METHOD_TEXT_FILE]: { [DAST_SITE_VALIDATION_METHOD_TEXT_FILE]: {
...@@ -18,6 +19,13 @@ export const DAST_SITE_VALIDATION_METHODS = { ...@@ -18,6 +19,13 @@ export const DAST_SITE_VALIDATION_METHODS = {
locationStepLabel: s__('DastSiteValidation|Step 3 - Confirm header location and validate'), locationStepLabel: s__('DastSiteValidation|Step 3 - Confirm header location and validate'),
}, },
}, },
[DAST_SITE_VALIDATION_METHOD_META_TAG]: {
value: DAST_SITE_VALIDATION_METHOD_META_TAG,
text: s__('DastSiteValidation|Meta tag validation'),
i18n: {
locationStepLabel: s__('DastSiteValidation|Step 3 - Confirm meta tag location and validate'),
},
},
}; };
export const DAST_SITE_VALIDATION_STATUS = { export const DAST_SITE_VALIDATION_STATUS = {
......
...@@ -8,6 +8,7 @@ module Projects ...@@ -8,6 +8,7 @@ module Projects
before_action do before_action do
authorize_read_on_demand_scans! authorize_read_on_demand_scans!
push_frontend_feature_flag(:dast_failed_site_validations, @project, default_enabled: :yaml) push_frontend_feature_flag(:dast_failed_site_validations, @project, default_enabled: :yaml)
push_frontend_feature_flag(:dast_meta_tag_validation, @project, default_enabled: :yaml)
end end
feature_category :dynamic_application_security_testing feature_category :dynamic_application_security_testing
......
...@@ -22,7 +22,7 @@ const targetUrl = 'https://example.com/'; ...@@ -22,7 +22,7 @@ const targetUrl = 'https://example.com/';
const tokenId = '1'; const tokenId = '1';
const token = 'validation-token-123'; const token = 'validation-token-123';
const validationMethods = ['text file', 'header']; const validationMethods = ['text file', 'header', 'meta tag'];
const defaultProps = { const defaultProps = {
fullPath, fullPath,
...@@ -57,6 +57,9 @@ describe('DastSiteValidationModal', () => { ...@@ -57,6 +57,9 @@ describe('DastSiteValidationModal', () => {
static: true, static: true,
visible: true, visible: true,
}, },
provide: {
glFeatures: { dastMetaTagValidation: true },
},
}, },
mountOptions, mountOptions,
{ {
...@@ -284,6 +287,57 @@ describe('DastSiteValidationModal', () => { ...@@ -284,6 +287,57 @@ describe('DastSiteValidationModal', () => {
}); });
}); });
}); });
describe('meta tag validation', () => {
beforeEach(async () => {
createFullComponent();
await waitForPromises();
enableValidationMethod('meta tag');
});
it.each([
/step 2 - add following meta tag to your site/i,
/step 3 - confirm meta tag location and validate/i,
])('shows the correct descriptions', (descriptionText) => {
expect(withinComponent().getByText(descriptionText)).not.toBe(null);
});
it('shows a code block containing the meta key with the given token', () => {
expect(
withinComponent().getByText(`<meta name="gitlab-dast-validation" content="${token}">`, {
selector: 'code',
}),
).not.toBe(null);
});
it('shows a button that copies the meta tag to the clipboard', () => {
const clipboardButton = wrapper.find(ModalCopyButton);
expect(clipboardButton.exists()).toBe(true);
expect(clipboardButton.props()).toMatchObject({
text: `<meta name="gitlab-dast-validation" content="${token}">`,
title: 'Copy Meta tag to clipboard',
});
});
});
});
describe('with the dastMetaTagValidation feature flag disabled', () => {
beforeEach(() => {
createFullComponent({
provide: {
glFeatures: {
dastMetaTagValidation: false,
},
},
});
});
it('does not render the meta tag validation method', () => {
expect(findRadioInputForValidationMethod('meta tag')).toBe(null);
});
}); });
describe.each(validationMethods)('"%s" validation submission', (validationMethod) => { describe.each(validationMethods)('"%s" validation submission', (validationMethod) => {
......
...@@ -10438,6 +10438,9 @@ msgstr "" ...@@ -10438,6 +10438,9 @@ msgstr ""
msgid "DastSiteValidation|Copy HTTP header to clipboard" msgid "DastSiteValidation|Copy HTTP header to clipboard"
msgstr "" msgstr ""
msgid "DastSiteValidation|Copy Meta tag to clipboard"
msgstr ""
msgid "DastSiteValidation|Could not create validation token. Please try again." msgid "DastSiteValidation|Could not create validation token. Please try again."
msgstr "" msgstr ""
...@@ -10450,6 +10453,9 @@ msgstr "" ...@@ -10450,6 +10453,9 @@ msgstr ""
msgid "DastSiteValidation|Header validation" msgid "DastSiteValidation|Header validation"
msgstr "" msgstr ""
msgid "DastSiteValidation|Meta tag validation"
msgstr ""
msgid "DastSiteValidation|Retry validation" msgid "DastSiteValidation|Retry validation"
msgstr "" msgstr ""
...@@ -10462,12 +10468,18 @@ msgstr "" ...@@ -10462,12 +10468,18 @@ msgstr ""
msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site" msgid "DastSiteValidation|Step 2 - Add following HTTP header to your site"
msgstr "" msgstr ""
msgid "DastSiteValidation|Step 2 - Add following meta tag to your site"
msgstr ""
msgid "DastSiteValidation|Step 2 - Add following text to the target site" msgid "DastSiteValidation|Step 2 - Add following text to the target site"
msgstr "" msgstr ""
msgid "DastSiteValidation|Step 3 - Confirm header location and validate" msgid "DastSiteValidation|Step 3 - Confirm header location and validate"
msgstr "" msgstr ""
msgid "DastSiteValidation|Step 3 - Confirm meta tag location and validate"
msgstr ""
msgid "DastSiteValidation|Step 3 - Confirm text file location and validate" msgid "DastSiteValidation|Step 3 - Confirm text file location and validate"
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