Remove validation from DAST site profile form

DAST site validation has been rethought and will not be part of the DAST
site profile form like it was originally designed to be. This is the
first step in a series of changes to move the validation to a dedicated
modal accessible from the DAST profiles library.

Here, we're simply removing all of the validation logic form the DAST
site profile form to clean things up.
parent a1780610
<script>
import { isEqual } from 'lodash';
import {
GlAlert,
GlButton,
GlCollapse,
GlForm,
GlFormGroup,
GlFormInput,
GlModal,
GlToggle,
} from '@gitlab/ui';
import { GlAlert, GlButton, GlForm, GlFormGroup, GlFormInput, GlModal } from '@gitlab/ui';
import { initFormField } from 'ee/security_configuration/utils';
import * as Sentry from '~/sentry/wrapper';
import { __, s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import { serializeFormObject } from '~/lib/utils/forms';
import { fetchPolicies } from '~/lib/graphql';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import validation from '~/vue_shared/directives/validation';
import DastSiteValidation from './dast_site_validation.vue';
import dastSiteProfileCreateMutation from '../graphql/dast_site_profile_create.mutation.graphql';
import dastSiteProfileUpdateMutation from '../graphql/dast_site_profile_update.mutation.graphql';
import dastSiteTokenCreateMutation from '../graphql/dast_site_token_create.mutation.graphql';
import dastSiteValidationQuery from '../graphql/dast_site_validation.query.graphql';
import { DAST_SITE_VALIDATION_STATUS, DAST_SITE_VALIDATION_POLL_INTERVAL } from '../constants';
const { PENDING, INPROGRESS, PASSED, FAILED } = DAST_SITE_VALIDATION_STATUS;
export default {
name: 'DastSiteProfileForm',
components: {
GlAlert,
GlButton,
GlCollapse,
GlForm,
GlFormGroup,
GlFormInput,
GlModal,
GlToggle,
DastSiteValidation,
},
directives: {
validation: validation(),
},
mixins: [glFeatureFlagsMixin()],
props: {
fullPath: {
type: String,
......@@ -72,18 +51,12 @@ export default {
};
return {
fetchValidationTimeout: null,
form,
initialFormValues: serializeFormObject(form.fields),
isFetchingValidationStatus: false,
isValidatingSite: false,
isLoading: false,
hasAlert: false,
tokenId: null,
token: null,
isSiteValidationActive: false,
isSiteValidationTouched: false,
validationStatus: null,
errorMessage: '',
errors: [],
};
......@@ -92,9 +65,6 @@ export default {
isEdit() {
return Boolean(this.siteProfile?.id);
},
isSiteValidationDisabled() {
return !this.form.fields.targetUrl.state || this.validationStatusMatches(INPROGRESS);
},
i18n() {
const { isEdit } = this;
return {
......@@ -111,163 +81,18 @@ export default {
okTitle: __('Discard'),
cancelTitle: __('Cancel'),
},
siteValidation: {
validationStatusFetchError: s__(
'DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later.',
),
createTokenError: s__(
'DastProfiles|Could not create site validation token. Please refresh the page, or try again later.',
),
},
};
},
formTouched() {
return !isEqual(serializeFormObject(this.form.fields), this.initialFormValues);
},
isSubmitDisabled() {
return (
this.validationStatusMatches(INPROGRESS) ||
(this.isSiteValidationActive && !this.validationStatusMatches(PASSED))
);
},
showValidationSection() {
return (
this.isSiteValidationActive &&
!this.isValidatingSite &&
![INPROGRESS, PASSED].some(this.validationStatusMatches)
);
},
siteValidationStatusDescription() {
const descriptions = {
[PENDING]: { text: s__('DastProfiles|Site must be validated to run an active scan.') },
[INPROGRESS]: {
text: s__('DastProfiles|Validation is in progress...'),
},
[PASSED]: {
text: s__(
'DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site.',
),
cssClass: 'gl-text-green-500',
},
[FAILED]: {
text: s__('DastProfiles|Validation failed. Please try again.'),
cssClass: 'gl-text-red-500',
dismissed: this.isSiteValidationTouched,
},
};
const defaultDescription = descriptions[PENDING];
const currentStatusDescription = descriptions[this.validationStatus];
return currentStatusDescription && !currentStatusDescription.dismissed
? currentStatusDescription
: defaultDescription;
},
},
async mounted() {
if (this.isEdit) {
this.form.showValidation = true;
if (this.glFeatures.securityOnDemandScansSiteValidation) {
await this.fetchValidationStatus();
this.isSiteValidationActive = this.validationStatusMatches(PASSED);
}
}
},
destroyed() {
clearTimeout(this.fetchValidationTimeout);
this.fetchValidationTimeout = null;
},
methods: {
async validateSite(validate) {
this.isSiteValidationActive = validate;
this.isSiteValidationTouched = true;
this.tokenId = null;
this.token = null;
if (!validate) {
this.validationStatus = null;
} else {
try {
this.isValidatingSite = true;
await this.fetchValidationStatus();
if (![PASSED, INPROGRESS].some(this.validationStatusMatches)) {
await this.createValidationToken();
}
} catch (exception) {
this.captureException(exception);
this.isSiteValidationActive = false;
} finally {
this.isValidatingSite = false;
}
}
},
validationStatusMatches(status) {
return this.validationStatus === status;
},
async fetchValidationStatus() {
this.isFetchingValidationStatus = true;
try {
const {
data: {
project: { dastSiteValidation },
},
} = await this.$apollo.query({
query: dastSiteValidationQuery,
variables: {
fullPath: this.fullPath,
targetUrl: this.form.fields.targetUrl.value,
},
fetchPolicy: fetchPolicies.NETWORK_ONLY,
});
this.validationStatus = dastSiteValidation?.status || null;
if (this.validationStatusMatches(INPROGRESS)) {
await new Promise(resolve => {
this.fetchValidationTimeout = setTimeout(resolve, DAST_SITE_VALIDATION_POLL_INTERVAL);
});
await this.fetchValidationStatus();
}
} catch (exception) {
this.showErrors({
message: this.i18n.siteValidation.validationStatusFetchError,
});
throw new Error(exception);
} finally {
this.isFetchingValidationStatus = false;
}
},
async createValidationToken() {
const errorMessage = this.i18n.siteValidation.createTokenError;
try {
const {
data: {
dastSiteTokenCreate: { id, token, errors = [] },
},
} = await this.$apollo.mutate({
mutation: dastSiteTokenCreateMutation,
variables: {
fullPath: this.fullPath,
targetUrl: this.form.fields.targetUrl.value,
},
});
if (errors.length) {
this.showErrors({ message: errorMessage, errors });
} else {
this.tokenId = id;
this.token = token;
}
} catch (exception) {
this.showErrors({ message: errorMessage });
throw new Error(exception);
}
},
onSubmit() {
this.form.showValidation = true;
......@@ -317,9 +142,6 @@ export default {
this.$refs[this.$options.modalId].show();
}
},
onValidationSuccess() {
this.validationStatus = PASSED;
},
discard() {
redirectTo(this.profilesLibraryPath);
},
......@@ -381,11 +203,6 @@ export default {
<gl-form-group
data-testid="target-url-input-group"
:invalid-feedback="form.fields.targetUrl.feedback"
:description="
isSiteValidationActive && !isValidatingSite
? s__('DastProfiles|Validation must be turned off to change the target URL')
: null
"
:label="s__('DastProfiles|Target URL')"
>
<gl-form-input
......@@ -397,44 +214,9 @@ export default {
required
type="url"
:state="form.fields.targetUrl.state"
:disabled="isSiteValidationActive"
/>
</gl-form-group>
<template v-if="glFeatures.securityOnDemandScansSiteValidation">
<gl-form-group :label="s__('DastProfiles|Validate target site')">
<template #description>
<p
v-if="siteValidationStatusDescription.text"
class="gl-mt-3"
:class="siteValidationStatusDescription.cssClass"
data-testid="siteValidationStatusDescription"
>
{{ siteValidationStatusDescription.text }}
</p>
</template>
<gl-toggle
data-testid="dast-site-validation-toggle"
:value="isSiteValidationActive"
:disabled="isSiteValidationDisabled"
:is-loading="
!isSiteValidationDisabled && (isFetchingValidationStatus || isValidatingSite)
"
@change="validateSite"
/>
</gl-form-group>
<gl-collapse :visible="showValidationSection">
<dast-site-validation
:full-path="fullPath"
:token-id="tokenId"
:token="token"
:target-url="form.fields.targetUrl.value"
@success="onValidationSuccess"
/>
</gl-collapse>
</template>
<hr />
<div class="gl-mt-6 gl-pt-6">
......@@ -443,7 +225,6 @@ export default {
variant="success"
class="js-no-auto-disable"
data-testid="dast-site-profile-form-submit-button"
:disabled="isSubmitDisabled"
:loading="isLoading"
>
{{ s__('DastProfiles|Save profile') }}
......
......@@ -8401,9 +8401,6 @@ msgstr ""
msgid "DastProfiles|Copy HTTP header to clipboard"
msgstr ""
msgid "DastProfiles|Could not create site validation token. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not create the scanner profile. Please try again."
msgstr ""
......@@ -8428,9 +8425,6 @@ msgstr ""
msgid "DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not retrieve site validation status. Please refresh the page, or try again later."
msgstr ""
msgid "DastProfiles|Could not update the scanner profile. Please try again."
msgstr ""
......@@ -8545,9 +8539,6 @@ msgstr ""
msgid "DastProfiles|Site is not validated yet, please follow the steps."
msgstr ""
msgid "DastProfiles|Site must be validated to run an active scan."
msgstr ""
msgid "DastProfiles|Spider timeout"
msgstr ""
......@@ -8593,27 +8584,12 @@ msgstr ""
msgid "DastProfiles|Validate"
msgstr ""
msgid "DastProfiles|Validate target site"
msgstr ""
msgid "DastProfiles|Validating..."
msgstr ""
msgid "DastProfiles|Validation failed, please make sure that you follow the steps above with the chosen method."
msgstr ""
msgid "DastProfiles|Validation failed. Please try again."
msgstr ""
msgid "DastProfiles|Validation is in progress..."
msgstr ""
msgid "DastProfiles|Validation must be turned off to change the target URL"
msgstr ""
msgid "DastProfiles|Validation succeeded. Both active and passive scans can be run against the target site."
msgstr ""
msgid "Data is still calculating..."
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