Commit 4050e772 authored by Mark Florian's avatar Mark Florian

Merge branch 'djadmin-exclude-url-schema-update' into 'master'

Update schema for excluded urls in DAST site profile form

See merge request gitlab-org/gitlab!56450
parents 1cfc8982 8feb7756
......@@ -17,6 +17,7 @@ import {
SCAN_TYPE_LABEL,
SCAN_TYPE,
} from 'ee/security_configuration/dast_scanner_profiles/constants';
import { EXCLUDED_URLS_SEPARATOR } from 'ee/security_configuration/dast_site_profiles_form/constants';
import { DAST_SITE_VALIDATION_STATUS } from 'ee/security_configuration/dast_site_validation/constants';
import { initFormField } from 'ee/security_configuration/utils';
import { convertToGraphQLId } from '~/graphql_shared/utils';
......@@ -319,6 +320,7 @@ export default {
},
},
ON_DEMAND_SCANS_STORAGE_KEY,
EXCLUDED_URLS_SEPARATOR,
};
</script>
......@@ -512,7 +514,7 @@ export default {
<div class="row">
<profile-selector-summary-cell
:label="s__('DastProfiles|Excluded URLs')"
:value="selectedSiteProfile.excludedUrls"
:value="selectedSiteProfile.excludedUrls.join($options.EXCLUDED_URLS_SEPARATOR)"
/>
<profile-selector-summary-cell
:label="s__('DastProfiles|Request headers')"
......
......@@ -18,13 +18,15 @@ import { __, s__, n__, sprintf } from '~/locale';
import validation from '~/vue_shared/directives/validation';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import tooltipIcon from '../../dast_scanner_profiles/components/tooltip_icon.vue';
import {
MAX_CHAR_LIMIT_EXCLUDED_URLS,
MAX_CHAR_LIMIT_REQUEST_HEADERS,
EXCLUDED_URLS_SEPARATOR,
} from '../constants';
import dastSiteProfileCreateMutation from '../graphql/dast_site_profile_create.mutation.graphql';
import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.mutation.graphql';
import DastSiteAuthSection from './dast_site_auth_section.vue';
const MAX_CHAR_LIMIT_EXCLUDED_URLS = 2048;
const MAX_CHAR_LIMIT_REQUEST_HEADERS = 2048;
export default {
name: 'DastSiteProfileForm',
components: {
......@@ -63,7 +65,7 @@ export default {
},
},
data() {
const { name = '', targetUrl = '', excludedUrls = '', requestHeaders = '', auth = {} } =
const { name = '', targetUrl = '', excludedUrls = [], requestHeaders = '', auth = {} } =
this.siteProfile || {};
const form = {
......@@ -72,7 +74,11 @@ export default {
fields: {
profileName: initFormField({ value: name }),
targetUrl: initFormField({ value: targetUrl }),
excludedUrls: initFormField({ value: excludedUrls, required: false, skipValidation: true }),
excludedUrls: initFormField({
value: excludedUrls.join(EXCLUDED_URLS_SEPARATOR),
required: false,
skipValidation: true,
}),
requestHeaders: initFormField({
value: requestHeaders,
required: false,
......@@ -143,6 +149,11 @@ export default {
isPolicyProfile() {
return Boolean(this.siteProfile?.referencedInSecurityPolicies?.length);
},
parsedExcludedUrls() {
return this.form.fields.excludedUrls.value
.split(EXCLUDED_URLS_SEPARATOR)
.map((url) => url.trim());
},
},
async mounted() {
if (this.isEdit) {
......@@ -165,13 +176,20 @@ export default {
this.hideErrors();
const { errorMessage } = this.i18n;
const { profileName, targetUrl, ...additionalFields } = serializeFormObject(this.form.fields);
const variables = {
input: {
fullPath: this.fullPath,
...(this.isEdit ? { id: this.siteProfile.id } : {}),
...serializeFormObject(this.form.fields),
profileName,
targetUrl,
...(this.glFeatures.securityDastSiteProfilesAdditionalFields && {
...additionalFields,
auth: serializeFormObject(this.authSection.fields),
...(additionalFields.excludedUrls && {
excludedUrls: this.parsedExcludedUrls,
}),
}),
},
};
......
export const MAX_CHAR_LIMIT_EXCLUDED_URLS = 2048;
export const MAX_CHAR_LIMIT_REQUEST_HEADERS = 2048;
export const EXCLUDED_URLS_SEPARATOR = ',';
......@@ -6,6 +6,6 @@
.js-dast-site-profile-form{ data: { full_path: @project.path_with_namespace,
profiles_library_path: project_security_configuration_dast_profiles_path(@project, anchor: 'site-profiles'),
site_profile: { id: @site_profile.to_global_id.to_s, name: @site_profile.name, target_url: @site_profile.dast_site.url,
excluded_urls: 'https://example.com/logout', request_headers: 'new-header',
excluded_urls: ['https://example.com/logout', 'https://example.com/send_mail'], request_headers: 'new-header',
auth: { enabled: true, url: 'https://example.com', username: 'admin', usernameField: 'username', passwordField: 'password' }, referenced_in_security_policies: @site_profile.referenced_in_security_policies}.to_json,
on_demand_scans_path: new_project_on_demand_scan_path(@project) } }
......@@ -536,7 +536,7 @@ describe('OnDemandScansForm', () => {
const summary = subject.find(SiteProfileSelector).text();
expect(summary).toMatch(authEnabledProfile.targetUrl);
expect(summary).toMatch(authEnabledProfile.excludedUrls);
expect(summary).toMatch(authEnabledProfile.excludedUrls.join(','));
expect(summary).toMatch(authEnabledProfile.requestHeaders);
expect(summary).toMatch(authEnabledProfile.auth.url);
expect(summary).toMatch(authEnabledProfile.auth.username);
......
......@@ -51,7 +51,7 @@ export const siteProfiles = [
username: 'admin',
password: 'password',
},
excludedUrls: 'https://foo.com/logout,https://foo.com/send_mail',
excludedUrls: ['https://foo.com/logout', 'https://foo.com/send_mail'],
requestHeaders: 'log-identifier: dast-active-scan',
referencedInSecurityPolicies: [],
},
......@@ -65,7 +65,7 @@ export const siteProfiles = [
auth: {
enabled: false,
},
excludedUrls: 'https://bar.com/logout',
excludedUrls: ['https://bar.com/logout'],
requestHeaders: 'auth: gitlab-dast',
referencedInSecurityPolicies: [],
},
......@@ -81,6 +81,6 @@ export const policySiteProfile = {
auth: {
enabled: false,
},
excludedUrls: 'https://bar.com/logout',
excludedUrls: ['https://bar.com/logout'],
referencedInSecurityPolicies: ['some_policy'],
};
......@@ -24,7 +24,7 @@ const profilesLibraryPath = `${TEST_HOST}/${fullPath}/-/security/configuration/d
const onDemandScansPath = `${TEST_HOST}/${fullPath}/-/on_demand_scans`;
const profileName = 'My DAST site profile';
const targetUrl = 'http://example.com';
const excludedUrls = 'http://example.com/logout';
const excludedUrls = 'https://foo.com/logout, https://foo.com/send_mail';
const requestHeaders = 'my-new-header=something';
const defaultProps = {
......@@ -224,10 +224,10 @@ describe('DastSiteProfileForm', () => {
input: {
profileName,
targetUrl,
excludedUrls,
requestHeaders,
fullPath,
auth: siteProfileOne.auth,
excludedUrls: siteProfileOne.excludedUrls,
...mutationVars,
},
});
......@@ -319,21 +319,55 @@ describe('DastSiteProfileForm', () => {
});
describe('when feature flag is off', () => {
beforeEach(() => {
createFullComponent({
provide: {
glFeatures: {
securityDastSiteProfilesAdditionalFields: false,
},
const mountOpts = {
provide: {
glFeatures: {
securityDastSiteProfilesAdditionalFields: false,
},
});
});
},
};
const fillAndSubmitForm = async () => {
await setFieldValue(findProfileNameInput(), profileName);
await setFieldValue(findTargetUrlInput(), targetUrl);
submitForm();
};
it('should not render additional fields', () => {
createFullComponent(mountOpts);
expect(findAuthSection().exists()).toBe(false);
expect(findExcludedUrlsInput().exists()).toBe(false);
expect(findRequestHeadersInput().exists()).toBe(false);
});
describe.each`
title | siteProfile | mutationVars | mutationKind
${'New site profile'} | ${null} | ${{}} | ${'dastSiteProfileCreate'}
${'Edit site profile'} | ${siteProfileOne} | ${{ id: siteProfileOne.id }} | ${'dastSiteProfileUpdate'}
`('$title', ({ siteProfile, mutationVars, mutationKind }) => {
beforeEach(() => {
createFullComponent({
propsData: {
siteProfile,
},
...mountOpts,
});
fillAndSubmitForm();
});
it('form submission triggers correct GraphQL mutation', async () => {
await fillAndSubmitForm();
expect(requestHandlers[mutationKind]).toHaveBeenCalledWith({
input: {
profileName,
targetUrl,
fullPath,
...mutationVars,
},
});
});
});
});
describe('when profile does not come from a policy', () => {
......
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