Remove security_on_demand_scans_scanner_profiles feature flag

Removed the security_on_demand_scans_scanner_profiles feature flag and
all associated logic
parent fa229d77
...@@ -671,13 +671,6 @@ To delete an existing site profile: ...@@ -671,13 +671,6 @@ To delete an existing site profile:
## Scanner profile ## Scanner profile
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/222767) in GitLab 13.4.
> - [Deployed behind a feature flag](../../feature_flags.md), enabled by default.
> - Enabled on GitLab.com.
> - Can be enabled or disabled per-project.
> - Recommended for production use.
> - For GitLab self-managed instances, GitLab administrators can [disable this feature](#enable-or-disable-dast-scanner-profiles).
A scanner profile defines the scanner settings used to run an on-demand scan: A scanner profile defines the scanner settings used to run an on-demand scan:
- **Profile name:** A name you give the scanner profile. For example, "Spider_15". - **Profile name:** A name you give the scanner profile. For example, "Spider_15".
...@@ -711,29 +704,6 @@ To delete a scanner profile: ...@@ -711,29 +704,6 @@ To delete a scanner profile:
1. Click **Manage** in the **DAST Profiles** row. 1. Click **Manage** in the **DAST Profiles** row.
1. Click **{remove}** in the scanner profile's row. 1. Click **{remove}** in the scanner profile's row.
### Enable or disable DAST scanner profiles
The scanner profile feature is ready for production use. It's deployed behind a feature flag that
is **enabled by default**. [GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) can opt to disable it.
To disable it:
```ruby
# Instance-wide
Feature.disable(:security_on_demand_scans_scanner_profiles)
# or by project
Feature.disable(:security_on_demand_scans_scanner_profiles, Project.find(<project id>))
```
To enable it:
```ruby
# Instance-wide
Feature.enable(:security_on_demand_scans_scanner_profiles)
# or by project
Feature.enable(:security_on_demand_scans_scanner_profiles, Project.find(<project ID>))
```
## On-demand scans ## On-demand scans
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218465) in GitLab 13.2. > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218465) in GitLab 13.2.
......
...@@ -6,7 +6,6 @@ import { ...@@ -6,7 +6,6 @@ import {
GlCard, GlCard,
GlForm, GlForm,
GlFormGroup, GlFormGroup,
GlIcon,
GlLink, GlLink,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
...@@ -48,7 +47,6 @@ export default { ...@@ -48,7 +47,6 @@ export default {
GlCard, GlCard,
GlForm, GlForm,
GlFormGroup, GlFormGroup,
GlIcon,
GlLink, GlLink,
GlDropdown, GlDropdown,
GlDropdownItem, GlDropdownItem,
...@@ -75,9 +73,6 @@ export default { ...@@ -75,9 +73,6 @@ export default {
Sentry.captureException(e); Sentry.captureException(e);
this.showErrors(ERROR_FETCH_SCANNER_PROFILES); this.showErrors(ERROR_FETCH_SCANNER_PROFILES);
}, },
skip() {
return !this.glFeatures.securityOnDemandScansScannerProfiles;
},
}, },
siteProfiles: { siteProfiles: {
query: dastSiteProfilesQuery, query: dastSiteProfilesQuery,
...@@ -129,9 +124,7 @@ export default { ...@@ -129,9 +124,7 @@ export default {
scannerProfiles: [], scannerProfiles: [],
siteProfiles: [], siteProfiles: [],
form: { form: {
...(this.glFeatures.securityOnDemandScansScannerProfiles dastScannerProfileId: initField(null),
? { dastScannerProfileId: initField(null) }
: {}),
dastSiteProfileId: initField(null), dastSiteProfileId: initField(null),
}, },
loading: false, loading: false,
...@@ -295,7 +288,7 @@ export default { ...@@ -295,7 +288,7 @@ export default {
{{ s__('OnDemandScans|Scanner settings') }} {{ s__('OnDemandScans|Scanner settings') }}
</h3> </h3>
</div> </div>
<div v-if="glFeatures.securityOnDemandScansScannerProfiles" class="col-5 gl-text-right"> <div class="col-5 gl-text-right">
<gl-button <gl-button
:href="scannerProfiles.length ? scannerProfilesLibraryPath : null" :href="scannerProfiles.length ? scannerProfilesLibraryPath : null"
:disabled="!scannerProfiles.length" :disabled="!scannerProfiles.length"
...@@ -310,109 +303,80 @@ export default { ...@@ -310,109 +303,80 @@ export default {
</div> </div>
</template> </template>
<template v-if="glFeatures.securityOnDemandScansScannerProfiles"> <gl-form-group v-if="scannerProfiles.length">
<gl-form-group v-if="scannerProfiles.length"> <template #label>
<template #label> {{ s__('OnDemandScans|Use existing scanner profile') }}
{{ s__('OnDemandScans|Use existing scanner profile') }} </template>
</template> <gl-dropdown
<gl-dropdown v-model="form.dastScannerProfileId.value"
v-model="form.dastScannerProfileId.value" :text="scannerProfileText"
:text="scannerProfileText" class="mw-460"
class="mw-460" data-testid="scanner-profiles-dropdown"
data-testid="scanner-profiles-dropdown" >
<gl-dropdown-item
v-for="scannerProfile in scannerProfiles"
:key="scannerProfile.id"
:is-checked="form.dastScannerProfileId.value === scannerProfile.id"
is-check-item
@click="setScannerProfile(scannerProfile)"
> >
<gl-dropdown-item {{ scannerProfile.profileName }}
v-for="scannerProfile in scannerProfiles" </gl-dropdown-item>
:key="scannerProfile.id" </gl-dropdown>
:is-checked="form.dastScannerProfileId.value === scannerProfile.id" <template v-if="selectedScannerProfile">
is-check-item <hr />
@click="setScannerProfile(scannerProfile)" <div data-testid="scanner-profile-summary">
> <div class="row">
{{ scannerProfile.profileName }} <div class="col-md-6">
</gl-dropdown-item> <div class="row">
</gl-dropdown> <div class="col-md-3">{{ s__('DastProfiles|Scan mode') }}:</div>
<template v-if="selectedScannerProfile"> <div class="col-md-9">
<hr /> <strong>{{ s__('DastProfiles|Passive') }}</strong>
<div data-testid="scanner-profile-summary">
<div class="row">
<div class="col-md-6">
<div class="row">
<div class="col-md-3">{{ s__('DastProfiles|Scan mode') }}:</div>
<div class="col-md-9">
<strong>{{ s__('DastProfiles|Passive') }}</strong>
</div>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> </div>
<div class="col-md-6"> <div class="row">
<div class="row"> <div class="col-md-6">
<div class="col-md-3">{{ s__('DastProfiles|Spider timeout') }}:</div> <div class="row">
<div class="col-md-9"> <div class="col-md-3">{{ s__('DastProfiles|Spider timeout') }}:</div>
<strong> <div class="col-md-9">
{{ n__('%d minute', '%d minutes', selectedScannerProfile.spiderTimeout) }} <strong>
</strong> {{ n__('%d minute', '%d minutes', selectedScannerProfile.spiderTimeout) }}
</div> </strong>
</div> </div>
</div> </div>
<div class="col-md-6"> </div>
<div class="row"> <div class="col-md-6">
<div class="col-md-3">{{ s__('DastProfiles|Target timeout') }}:</div> <div class="row">
<div class="col-md-9"> <div class="col-md-3">{{ s__('DastProfiles|Target timeout') }}:</div>
<strong> <div class="col-md-9">
{{ n__('%d second', '%d seconds', selectedScannerProfile.targetTimeout) }} <strong>
</strong> {{ n__('%d second', '%d seconds', selectedScannerProfile.targetTimeout) }}
</div> </strong>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </div>
</gl-form-group>
<template v-else>
<p class="gl-text-gray-700">
{{
s__(
'OnDemandScans|No profile yet. In order to create a new scan, you need to have at least one completed scanner profile.',
)
}}
</p>
<gl-button
:href="newScannerProfilePath"
variant="success"
category="secondary"
data-testid="create-scanner-profile-link"
>
{{ s__('OnDemandScans|Create a new scanner profile') }}
</gl-button>
</template> </template>
</template> </gl-form-group>
<template v-else> <template v-else>
<gl-form-group class="gl-mt-4"> <p class="gl-text-gray-700">
<template #label> {{
{{ s__('OnDemandScans|Scan mode') }} s__(
<gl-icon 'OnDemandScans|No profile yet. In order to create a new scan, you need to have at least one completed scanner profile.',
v-gl-tooltip.hover )
name="information-o" }}
class="gl-vertical-align-text-bottom gl-text-gray-600" </p>
:title="s__('OnDemandScans|Only a passive scan can be performed on demand.')" <gl-button
/> :href="newScannerProfilePath"
</template> variant="success"
{{ s__('OnDemandScans|Passive') }} category="secondary"
</gl-form-group> data-testid="create-scanner-profile-link"
>
<gl-form-group class="gl-mt-7 gl-mb-2"> {{ s__('OnDemandScans|Create a new scanner profile') }}
<template #label> </gl-button>
{{ s__('OnDemandScans|Attached branch') }}
<gl-icon
v-gl-tooltip.hover
name="information-o"
class="gl-vertical-align-text-bottom gl-text-gray-600"
:title="s__('OnDemandScans|Attached branch is where the scan job runs.')"
/>
</template>
{{ defaultBranch }}
</gl-form-group>
</template> </template>
</gl-card> </gl-card>
......
...@@ -4,7 +4,6 @@ import { GlDropdown, GlDropdownItem, GlTab, GlTabs } from '@gitlab/ui'; ...@@ -4,7 +4,6 @@ import { GlDropdown, GlDropdownItem, GlTab, GlTabs } from '@gitlab/ui';
import { camelCase, kebabCase } from 'lodash'; import { camelCase, kebabCase } from 'lodash';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { getLocationHash } from '~/lib/utils/url_utility'; import { getLocationHash } from '~/lib/utils/url_utility';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import ProfilesList from './dast_profiles_list.vue'; import ProfilesList from './dast_profiles_list.vue';
import * as cacheUtils from '../graphql/cache_utils'; import * as cacheUtils from '../graphql/cache_utils';
import { getProfileSettings } from '../settings/profiles'; import { getProfileSettings } from '../settings/profiles';
...@@ -17,7 +16,6 @@ export default { ...@@ -17,7 +16,6 @@ export default {
GlTabs, GlTabs,
ProfilesList, ProfilesList,
}, },
mixins: [glFeatureFlagMixin()],
props: { props: {
createNewProfilePaths: { createNewProfilePaths: {
type: Object, type: Object,
...@@ -37,14 +35,11 @@ export default { ...@@ -37,14 +35,11 @@ export default {
}, },
computed: { computed: {
profileSettings() { profileSettings() {
const { glFeatures, createNewProfilePaths } = this; const { createNewProfilePaths } = this;
return getProfileSettings( return getProfileSettings({
{ createNewProfilePaths,
createNewProfilePaths, });
},
glFeatures,
);
}, },
tabIndex: { tabIndex: {
get() { get() {
......
...@@ -5,76 +5,61 @@ import dastScannerProfilesDelete from 'ee/security_configuration/dast_profiles/g ...@@ -5,76 +5,61 @@ import dastScannerProfilesDelete from 'ee/security_configuration/dast_profiles/g
import { dastProfilesDeleteResponse } from 'ee/security_configuration/dast_profiles/graphql/cache_utils'; import { dastProfilesDeleteResponse } from 'ee/security_configuration/dast_profiles/graphql/cache_utils';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
const hasNoFeatureFlagOrIsEnabled = glFeatures => ([, { featureFlag }]) => { export const getProfileSettings = ({ createNewProfilePaths }) => ({
if (!featureFlag) { siteProfiles: {
return true; profileType: 'siteProfiles',
} createNewProfilePath: createNewProfilePaths.siteProfile,
graphQL: {
return Boolean(glFeatures[featureFlag]); query: dastSiteProfilesQuery,
}; deletion: {
mutation: dastSiteProfilesDelete,
export const getProfileSettings = ({ createNewProfilePaths }, glFeatures) => { optimisticResponse: dastProfilesDeleteResponse({
const settings = { mutationName: 'siteProfilesDelete',
siteProfiles: { payloadTypeName: 'DastSiteProfileDeletePayload',
profileType: 'siteProfiles', }),
createNewProfilePath: createNewProfilePaths.siteProfile,
graphQL: {
query: dastSiteProfilesQuery,
deletion: {
mutation: dastSiteProfilesDelete,
optimisticResponse: dastProfilesDeleteResponse({
mutationName: 'siteProfilesDelete',
payloadTypeName: 'DastSiteProfileDeletePayload',
}),
},
}, },
tableFields: ['profileName', 'targetUrl'], },
i18n: { tableFields: ['profileName', 'targetUrl'],
createNewLinkText: s__('DastProfiles|Site Profile'), i18n: {
tabName: s__('DastProfiles|Site Profiles'), createNewLinkText: s__('DastProfiles|Site Profile'),
errorMessages: { tabName: s__('DastProfiles|Site Profiles'),
fetchNetworkError: s__( errorMessages: {
'DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later.', fetchNetworkError: s__(
), 'DastProfiles|Could not fetch site profiles. Please refresh the page, or try again later.',
deletionNetworkError: s__( ),
'DastProfiles|Could not delete site profile. Please refresh the page, or try again later.', deletionNetworkError: s__(
), 'DastProfiles|Could not delete site profile. Please refresh the page, or try again later.',
deletionBackendError: s__('DastProfiles|Could not delete site profiles:'), ),
}, deletionBackendError: s__('DastProfiles|Could not delete site profiles:'),
}, },
}, },
scannerProfiles: { },
profileType: 'scannerProfiles', scannerProfiles: {
createNewProfilePath: createNewProfilePaths.scannerProfile, profileType: 'scannerProfiles',
graphQL: { createNewProfilePath: createNewProfilePaths.scannerProfile,
query: dastScannerProfilesQuery, graphQL: {
deletion: { query: dastScannerProfilesQuery,
mutation: dastScannerProfilesDelete, deletion: {
optimisticResponse: dastProfilesDeleteResponse({ mutation: dastScannerProfilesDelete,
mutationName: 'scannerProfilesDelete', optimisticResponse: dastProfilesDeleteResponse({
payloadTypeName: 'DastScannerProfileDeletePayload', mutationName: 'scannerProfilesDelete',
}), payloadTypeName: 'DastScannerProfileDeletePayload',
}, }),
}, },
featureFlag: 'securityOnDemandScansScannerProfiles', },
tableFields: ['profileName'], tableFields: ['profileName'],
i18n: { i18n: {
createNewLinkText: s__('DastProfiles|Scanner Profile'), createNewLinkText: s__('DastProfiles|Scanner Profile'),
tabName: s__('DastProfiles|Scanner Profiles'), tabName: s__('DastProfiles|Scanner Profiles'),
errorMessages: { errorMessages: {
fetchNetworkError: s__( fetchNetworkError: s__(
'DastProfiles|Could not fetch scanner profiles. Please refresh the page, or try again later.', 'DastProfiles|Could not fetch scanner profiles. Please refresh the page, or try again later.',
), ),
deletionNetworkError: s__( deletionNetworkError: s__(
'DastProfiles|Could not delete scanner profile. Please refresh the page, or try again later.', 'DastProfiles|Could not delete scanner profile. Please refresh the page, or try again later.',
), ),
deletionBackendError: s__('DastProfiles|Could not delete scanner profiles:'), deletionBackendError: s__('DastProfiles|Could not delete scanner profiles:'),
},
}, },
}, },
}; },
});
return Object.fromEntries(
Object.entries(settings).filter(hasNoFeatureFlagOrIsEnabled(glFeatures)),
);
};
...@@ -2,10 +2,7 @@ ...@@ -2,10 +2,7 @@
module Projects module Projects
class OnDemandScansController < Projects::ApplicationController class OnDemandScansController < Projects::ApplicationController
before_action do before_action :authorize_read_on_demand_scans!
authorize_read_on_demand_scans!
push_frontend_feature_flag(:security_on_demand_scans_scanner_profiles, project, default_enabled: true)
end
def index def index
end end
......
...@@ -4,9 +4,6 @@ module Projects ...@@ -4,9 +4,6 @@ module Projects
module Security module Security
class DastProfilesController < Projects::ApplicationController class DastProfilesController < Projects::ApplicationController
before_action :authorize_read_on_demand_scans! before_action :authorize_read_on_demand_scans!
before_action do
push_frontend_feature_flag(:security_on_demand_scans_scanner_profiles, project, default_enabled: true)
end
def show def show
end end
......
---
name: security_on_demand_scans_scanner_profiles
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39250
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235545
group: group::dynamic analysis
type: development
default_enabled: true
...@@ -67,7 +67,6 @@ describe('OnDemandScansApp', () => { ...@@ -67,7 +67,6 @@ describe('OnDemandScansApp', () => {
}, },
}, },
provide: { provide: {
glFeatures: { securityOnDemandScansScannerProfiles: true },
scannerProfilesLibraryPath, scannerProfilesLibraryPath,
siteProfilesLibraryPath, siteProfilesLibraryPath,
newScannerProfilePath, newScannerProfilePath,
...@@ -203,41 +202,6 @@ describe('OnDemandScansApp', () => { ...@@ -203,41 +202,6 @@ describe('OnDemandScansApp', () => {
}); });
}); });
describe('scanner profiles with feature flag disabled', () => {
beforeEach(() => {
createComponent({
provide: {
glFeatures: { securityOnDemandScansScannerProfiles: false },
},
});
});
it('shows static scanner settings and no scanner profiles component', () => {
expect(findScannerProfilesDropdown().exists()).toBe(false);
expect(findManageScannerProfilesButton().exists()).toBe(false);
expect(findCreateNewScannerProfileLink().exists()).toBe(false);
expect(wrapper.text()).toContain('Passive');
expect(wrapper.text()).toContain('master');
});
it('when submitting the form, GraphQL query does not include scanner data', async () => {
wrapper.vm.siteProfiles = siteProfiles;
await wrapper.vm.$nextTick();
jest
.spyOn(wrapper.vm.$apollo, 'mutate')
.mockResolvedValue({ data: { dastOnDemandScanCreate: { pipelineUrl, errors: [] } } });
findSiteProfilesDropdown().vm.$emit('input', siteProfiles[0]);
submitForm();
expect(wrapper.vm.$apollo.mutate).toHaveBeenCalledWith({
mutation: dastOnDemandScanCreate,
variables: {
dastSiteProfileId: siteProfiles[0],
fullPath: projectPath,
},
});
});
});
describe.each` describe.each`
profileType | manageProfilesButtonFinder | manageProfilesPath | createNewProfileButtonFinder | newProfilePath | dropdownFinder profileType | manageProfilesButtonFinder | manageProfilesPath | createNewProfileButtonFinder | newProfilePath | dropdownFinder
${'scanner'} | ${findManageScannerProfilesButton} | ${scannerProfilesLibraryPath} | ${findCreateNewScannerProfileLink} | ${newScannerProfilePath} | ${findScannerProfilesDropdown} ${'scanner'} | ${findManageScannerProfilesButton} | ${scannerProfilesLibraryPath} | ${findCreateNewScannerProfileLink} | ${newScannerProfilePath} | ${findScannerProfilesDropdown}
......
...@@ -36,12 +36,6 @@ describe('EE - DastProfiles', () => { ...@@ -36,12 +36,6 @@ describe('EE - DastProfiles', () => {
}, },
}; };
const defaultProvide = {
glFeatures: {
securityOnDemandScansScannerProfiles: true,
},
};
wrapper = mountFn( wrapper = mountFn(
DastProfiles, DastProfiles,
merge( merge(
...@@ -49,7 +43,6 @@ describe('EE - DastProfiles', () => { ...@@ -49,7 +43,6 @@ describe('EE - DastProfiles', () => {
{ {
propsData: defaultProps, propsData: defaultProps,
mocks: defaultMocks, mocks: defaultMocks,
provide: defaultProvide,
}, },
options, options,
), ),
...@@ -59,24 +52,6 @@ describe('EE - DastProfiles', () => { ...@@ -59,24 +52,6 @@ describe('EE - DastProfiles', () => {
const createComponent = createComponentFactory(); const createComponent = createComponentFactory();
const createFullComponent = createComponentFactory(mount); const createFullComponent = createComponentFactory(mount);
const withFeatureFlag = (featureFlagName, { enabled, disabled }) => {
it.each([true, false])(`with ${featureFlagName} enabled: "%s"`, featureFlagStatus => {
createComponent({
provide: {
glFeatures: {
[featureFlagName]: featureFlagStatus,
},
},
});
if (featureFlagStatus) {
enabled();
} else {
disabled();
}
});
};
const withinComponent = () => within(wrapper.element); const withinComponent = () => within(wrapper.element);
const getProfilesComponent = profileType => wrapper.find(`[data-testid="${profileType}List"]`); const getProfilesComponent = profileType => wrapper.find(`[data-testid="${profileType}List"]`);
const getDropdownComponent = () => wrapper.find(GlDropdown); const getDropdownComponent = () => wrapper.find(GlDropdown);
...@@ -116,17 +91,10 @@ describe('EE - DastProfiles', () => { ...@@ -116,17 +91,10 @@ describe('EE - DastProfiles', () => {
); );
}); });
describe(`shows a "Scanner Profile" dropdown item that links to ${TEST_NEW_DAST_SCANNER_PROFILE_PATH}`, () => { it(`shows a "Scanner Profile" dropdown item that links to ${TEST_NEW_DAST_SCANNER_PROFILE_PATH}`, () => {
withFeatureFlag('securityOnDemandScansScannerProfiles', { expect(getSiteProfilesDropdownItem('Scanner Profile').getAttribute('href')).toBe(
enabled: () => { TEST_NEW_DAST_SCANNER_PROFILE_PATH,
expect(getSiteProfilesDropdownItem('Scanner Profile').getAttribute('href')).toBe( );
TEST_NEW_DAST_SCANNER_PROFILE_PATH,
);
},
disabled: () => {
expect(getSiteProfilesDropdownItem('Scanner Profile')).toBe(null);
},
});
}); });
}); });
......
...@@ -17682,12 +17682,6 @@ msgstr "" ...@@ -17682,12 +17682,6 @@ msgstr ""
msgid "On track" msgid "On track"
msgstr "" msgstr ""
msgid "OnDemandScans|Attached branch"
msgstr ""
msgid "OnDemandScans|Attached branch is where the scan job runs."
msgstr ""
msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later." msgid "OnDemandScans|Could not fetch scanner profiles. Please refresh the page, or try again later."
msgstr "" msgstr ""
...@@ -17724,18 +17718,9 @@ msgstr "" ...@@ -17724,18 +17718,9 @@ msgstr ""
msgid "OnDemandScans|On-demand scans run outside the DevOps cycle and find vulnerabilities in your projects. %{learnMoreLinkStart}Learn more%{learnMoreLinkEnd}" msgid "OnDemandScans|On-demand scans run outside the DevOps cycle and find vulnerabilities in your projects. %{learnMoreLinkStart}Learn more%{learnMoreLinkEnd}"
msgstr "" msgstr ""
msgid "OnDemandScans|Only a passive scan can be performed on demand."
msgstr ""
msgid "OnDemandScans|Passive"
msgstr ""
msgid "OnDemandScans|Run scan" msgid "OnDemandScans|Run scan"
msgstr "" msgstr ""
msgid "OnDemandScans|Scan mode"
msgstr ""
msgid "OnDemandScans|Scanner profile" msgid "OnDemandScans|Scanner profile"
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