Commit 53d17209 authored by Dheeraj Joshi's avatar Dheeraj Joshi Committed by Kushal Pandya

Add code snippet generation for DAST Configuration

parent 1d599b61
......@@ -2,10 +2,16 @@ import { helpPagePath } from '~/helpers/help_page_helper';
export const CODE_SNIPPET_SOURCE_URL_PARAM = 'code_snippet_copied_from';
export const CODE_SNIPPET_SOURCE_API_FUZZING = 'api_fuzzing';
export const CODE_SNIPPET_SOURCES = [CODE_SNIPPET_SOURCE_API_FUZZING];
export const CODE_SNIPPET_SOURCE_DAST = 'dast';
export const CODE_SNIPPET_SOURCES = [CODE_SNIPPET_SOURCE_API_FUZZING, CODE_SNIPPET_SOURCE_DAST];
export const CODE_SNIPPET_SOURCE_SETTINGS = {
[CODE_SNIPPET_SOURCE_API_FUZZING]: {
datasetKey: 'apiFuzzingConfigurationPath',
docsPath: helpPagePath('user/application_security/api_fuzzing/index'),
},
[CODE_SNIPPET_SOURCE_DAST]: {
datasetKey: 'dastConfigurationPath',
docsPath: helpPagePath('user/application_security/dast/index'),
},
};
<script>
import { GlModal } from '@gitlab/ui';
import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
import Clipboard from 'clipboard';
import { getBaseURL, setUrlParams, redirectTo } from '~/lib/utils/url_utility';
import { sprintf, s__ } from '~/locale';
import { sprintf, s__, __ } from '~/locale';
import { CODE_SNIPPET_SOURCE_URL_PARAM } from '~/pipeline_editor/components/code_snippet_alert/constants';
import { CONFIGURATION_SNIPPET_MODAL_ID } from './constants';
......@@ -10,6 +10,16 @@ export default {
CONFIGURATION_SNIPPET_MODAL_ID,
components: {
GlModal,
GlSprintf,
GlLink,
},
i18n: {
helpText: s__(
'This code snippet contains everything reflected in the configuration form. Copy and paste it into %{linkStart}.gitlab-ci.yml%{linkEnd} file and save your changes. Future %{scanType} scans will use these settings.',
),
primaryText: s__('SecurityConfiguration|Copy code and open .gitlab-ci.yml file'),
secondaryText: s__('SecurityConfiguration|Copy code only'),
cancelText: __('Cancel'),
},
props: {
ciYamlEditUrl: {
......@@ -70,15 +80,15 @@ export default {
<gl-modal
ref="modal"
:action-primary="{
text: s__('SecurityConfiguration|Copy code and open .gitlab-ci.yml file'),
text: $options.i18n.primaryText,
attributes: [{ variant: 'confirm' }, { id: 'copy-yaml-snippet-and-edit-button' }],
}"
:action-secondary="{
text: s__('SecurityConfiguration|Copy code only'),
text: $options.i18n.secondaryText,
attributes: [{ variant: 'default' }, { id: 'copy-yaml-snippet-button' }],
}"
:action-cancel="{
text: __('Cancel'),
text: $options.i18n.cancelText,
}"
:modal-id="$options.CONFIGURATION_SNIPPET_MODAL_ID"
:title="modalTitle"
......@@ -86,6 +96,19 @@ export default {
@primary="copySnippet"
@secondary="copySnippet(false)"
>
<p class="gl-text-gray-500" data-testid="configuration-modal-help-text">
<gl-sprintf :message="$options.i18n.helpText">
<template #link="{ content }">
<gl-link :href="ciYamlEditUrl" target="_blank">
{{ content }}
</gl-link>
</template>
<template #scanType>
{{ scanType }}
</template>
</gl-sprintf>
</p>
<pre><code data-testid="configuration-modal-yaml-snippet" v-text="yaml"></code></pre>
</gl-modal>
</template>
<script>
import { GlLink, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { GlLink, GlSprintf, GlButton, GlForm } from '@gitlab/ui';
import ConfigurationSnippetModal from 'ee/security_configuration/components/configuration_snippet_modal.vue';
import { CONFIGURATION_SNIPPET_MODAL_ID } from 'ee/security_configuration/components/constants';
import { s__, __ } from '~/locale';
import { CODE_SNIPPET_SOURCE_DAST } from '~/pipeline_editor/components/code_snippet_alert/constants';
import { DAST_HELP_PATH } from '~/security_configuration/components/constants';
import {
DAST_YAML_CONFIGURATION_TEMPLATE as template,
DAST_SCANNER_PROFILE_PLACEHOLDER,
DAST_SITE_PROFILE_PLACEHOLDER,
} from '../constants';
export default {
DAST_HELP_PATH,
CONFIGURATION_SNIPPET_MODAL_ID,
CODE_SNIPPET_SOURCE_DAST,
components: {
GlLink,
GlSprintf,
GlButton,
GlForm,
ConfigurationSnippetModal,
},
inject: ['gitlabCiYamlEditPath', 'securityConfigurationPath'],
i18n: {
helpText: s__(`
DastConfig|Customize DAST settings to suit your requirements. Configuration changes made here override those provided by GitLab and are excluded from updates. For details of more advanced configuration options, see the %{docsLinkStart}GitLab DAST documentation%{docsLinkEnd}.`),
submitButtonText: s__('DastConfig|Generate code snippet'),
cancelText: __('Cancel'),
},
data() {
return {
// eslint-disable-next-line @gitlab/require-i18n-strings
selectedScannerProfileName: 'My DAST Scanner Profile',
// eslint-disable-next-line @gitlab/require-i18n-strings
selectedSiteProfileName: 'My DAST Site Profile',
isLoading: false,
};
},
computed: {
configurationYaml() {
return template
.replace(DAST_SITE_PROFILE_PLACEHOLDER, this.selectedSiteProfileName)
.replace(DAST_SCANNER_PROFILE_PLACEHOLDER, this.selectedScannerProfileName);
},
isSubmitDisabled() {
return !this.selectedScannerProfileName || !this.selectedSiteProfileName;
},
},
methods: {
onSubmit() {
this.$refs[CONFIGURATION_SNIPPET_MODAL_ID].show();
},
},
DAST_HELP_PATH,
};
</script>
<template>
<section class="gl-mt-5">
<p>
<gl-sprintf :message="$options.i18n.helpText">
<template #docsLink="{ content }">
<gl-link :href="$options.DAST_HELP_PATH" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</section>
<gl-form @submit.prevent="onSubmit">
<section class="gl-mt-5">
<p>
<gl-sprintf :message="$options.i18n.helpText">
<template #docsLink="{ content }">
<gl-link :href="$options.DAST_HELP_PATH" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</section>
<gl-button
:disabled="isSubmitDisabled"
:loading="isLoading"
type="submit"
variant="confirm"
class="js-no-auto-disable"
data-testid="dast-configuration-submit-button"
>{{ $options.i18n.submitButtonText }}</gl-button
>
<gl-button
:disabled="isLoading"
:href="securityConfigurationPath"
data-testid="dast-configuration-cancel-button"
>{{ $options.i18n.cancelText }}</gl-button
>
<configuration-snippet-modal
:ref="$options.CONFIGURATION_SNIPPET_MODAL_ID"
:ci-yaml-edit-url="gitlabCiYamlEditPath"
:yaml="configurationYaml"
:redirect-param="$options.CODE_SNIPPET_SOURCE_DAST"
scan-type="DAST"
/>
</gl-form>
</template>
export const DAST_SCANNER_PROFILE_PLACEHOLDER = '#DAST_SCANNER_PROFILE_NAME';
export const DAST_SITE_PROFILE_PLACEHOLDER = '#DAST_SITE_PROFILE_NAME';
export const DAST_YAML_CONFIGURATION_TEMPLATE = `# Add \`dast\` to your \`stages:\` configuration
stages:
- dast
# Include the DAST template
include:
- template: DAST.gitlab-ci.yml
# Your selected site and scanner profiles:
dast:
stage: dast
dast_configuration:
site_profile: "${DAST_SITE_PROFILE_PLACEHOLDER}"
scanner_profile: "${DAST_SCANNER_PROFILE_PLACEHOLDER}"
`;
......@@ -10,7 +10,8 @@ module EE
return super unless project.licensed_feature_available?(:api_fuzzing)
super.merge(
"api-fuzzing-configuration-path" => project_security_configuration_api_fuzzing_path(project)
"api-fuzzing-configuration-path" => project_security_configuration_api_fuzzing_path(project),
"dast-configuration-path" => project_security_configuration_dast_path(project)
)
end
end
......
import { GlModal } from '@gitlab/ui';
import { GlModal, GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import Clipboard from 'clipboard';
import { merge } from 'lodash';
......@@ -31,6 +31,7 @@ describe('EE - SecurityConfigurationSnippetModal', () => {
const findModal = () => wrapper.find(GlModal);
const findYamlSnippet = () => wrapper.findByTestId('configuration-modal-yaml-snippet');
const helpText = () => wrapper.findByTestId('configuration-modal-help-text');
const createWrapper = (options) => {
wrapper = extendedWrapper(
......@@ -48,6 +49,9 @@ describe('EE - SecurityConfigurationSnippetModal', () => {
static: true,
visible: true,
},
stubs: {
GlSprintf,
},
},
options,
),
......@@ -67,6 +71,12 @@ describe('EE - SecurityConfigurationSnippetModal', () => {
expect(findYamlSnippet().text()).toBe(configurationYaml);
});
it('renders help text correctly', () => {
expect(helpText().exists()).toBe(true);
expect(helpText().text()).not.toBe('');
expect(helpText().html()).toContain(gitlabCiYamlEditPath);
});
it('on primary event, text is copied to the clipbard and user is redirected to CI editor', async () => {
findModal().vm.$emit('primary');
......
import { GlSprintf } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import { mount } from '@vue/test-utils';
import ConfigurationSnippetModal from 'ee/security_configuration/components/configuration_snippet_modal.vue';
import { CONFIGURATION_SNIPPET_MODAL_ID } from 'ee/security_configuration/components/constants';
import ConfigurationForm from 'ee/security_configuration/dast/components/configuration_form.vue';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { CODE_SNIPPET_SOURCE_DAST } from '~/pipeline_editor/components/code_snippet_alert/constants';
import { DAST_HELP_PATH } from '~/security_configuration/components/constants';
const securityConfigurationPath = '/security/configuration';
const gitlabCiYamlEditPath = '/ci/editor';
const template = `# Add \`dast\` to your \`stages:\` configuration
stages:
- dast
# Include the DAST template
include:
- template: DAST.gitlab-ci.yml
# Your selected site and scanner profiles:
dast:
stage: dast
dast_configuration:
site_profile: "My DAST Site Profile"
scanner_profile: "My DAST Scanner Profile"
`;
describe('EE - DAST Configuration Form', () => {
let wrapper;
const findCancelButton = () => wrapper.findByTestId('dast-configuration-cancel-button');
const findConfigurationSnippetModal = () => wrapper.findComponent(ConfigurationSnippetModal);
const createComponent = () => {
wrapper = shallowMount(ConfigurationForm, {
stubs: {
GlSprintf,
},
});
wrapper = extendedWrapper(
mount(ConfigurationForm, {
provide: {
securityConfigurationPath,
gitlabCiYamlEditPath,
},
stubs: {
GlSprintf,
},
}),
);
};
beforeEach(() => {
......@@ -29,4 +60,25 @@ describe('EE - DAST Configuration Form', () => {
it('includes a link to DAST Configuration documentation', () => {
expect(wrapper.html()).toContain(DAST_HELP_PATH);
});
describe('form', () => {
it('submit button should open the model with correct props', () => {
jest.spyOn(wrapper.vm.$refs[CONFIGURATION_SNIPPET_MODAL_ID], 'show');
wrapper.find('form').trigger('submit');
expect(wrapper.vm.$refs[CONFIGURATION_SNIPPET_MODAL_ID].show).toHaveBeenCalled();
expect(findConfigurationSnippetModal().props()).toEqual({
ciYamlEditUrl: gitlabCiYamlEditPath,
yaml: template,
redirectParam: CODE_SNIPPET_SOURCE_DAST,
scanType: 'DAST',
});
});
it('cancel button points to Security Configuration page', () => {
expect(findCancelButton().attributes('href')).toBe('/security/configuration');
});
});
});
......@@ -25,12 +25,14 @@ RSpec.describe EE::Ci::PipelineEditorHelper do
it 'returns ee specific values' do
expect(pipeline_editor_data.keys).to include('api-fuzzing-configuration-path')
expect(pipeline_editor_data.keys).to include('dast-configuration-path')
end
end
context 'without licensed feature' do
it 'does not return the API fuzzing path' do
expect(pipeline_editor_data.keys).not_to include('api-fuzzing-configuration-path')
expect(pipeline_editor_data.keys).not_to include('dast-configuration-path')
end
end
end
......
......@@ -10042,6 +10042,9 @@ msgstr ""
msgid "DastConfig|DAST Settings"
msgstr ""
msgid "DastConfig|Generate code snippet"
msgstr ""
msgid "DastConfig|Scan Configuration"
msgstr ""
......@@ -33123,6 +33126,9 @@ msgstr ""
msgid "This chart could not be displayed"
msgstr ""
msgid "This code snippet contains everything reflected in the configuration form. Copy and paste it into %{linkStart}.gitlab-ci.yml%{linkEnd} file and save your changes. Future %{scanType} scans will use these settings."
msgstr ""
msgid "This comment changed after you started editing it. Review the %{startTag}updated comment%{endTag} to ensure information is not lost."
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