Skip to content
Snippets Groups Projects
Commit 89f0485e authored by Alexander Turinske's avatar Alexander Turinske :gay_pride_flag: Committed by Coung Ngo
Browse files

Update dast mixin profile requests

- pass fullPath to child component that also uses mixin
  to prevent double request with fullPath null
- simplify loading computed property

Changelog: changed
EE: true
parent 9772b6c6
No related branches found
No related tags found
1 merge request!121810Fix DAST security policy bug
......@@ -119,7 +119,7 @@ export default () => ({
return [ERROR_FETCH_SCANNER_PROFILES, ERROR_FETCH_SITE_PROFILES].includes(this.errorType);
},
isLoadingProfiles() {
return ['scannerProfiles', 'siteProfiles'].some((name) => this.$apollo.queries[name].loading);
return this.$apollo.loading;
},
isScannerProfile() {
return this.profileType === SCANNER_TYPE;
......
......@@ -26,9 +26,11 @@ export default {
mixins: [dastProfileConfiguratorMixin()],
provide() {
return {
projectPath: this.fullPath,
// rename namespacePath to projectPath for dastProfilesDrawer
projectPath: this.namespacePath,
};
},
inject: ['namespacePath'],
data() {
return {
activeProfile: undefined,
......@@ -52,28 +54,36 @@ export default {
},
},
watch: {
scannerProfiles() {
this.hasDastProfileError();
},
siteProfiles() {
this.hasDastProfileError();
},
selectedScannerProfile: 'updateProfiles',
selectedSiteProfile: 'updateProfiles',
},
mounted() {
if (
this.savedScannerProfileName &&
!this.doesProfileExist(this.scannerProfiles, this.savedScannerProfileName)
) {
this.$emit('error');
}
if (
this.savedSiteProfileName &&
!this.doesProfileExist(this.siteProfiles, this.savedSiteProfileName)
) {
this.$emit('error');
}
},
methods: {
doesProfileExist(profiles = [], savedProfileName) {
return profiles.some(({ profileName }) => profileName === savedProfileName);
},
hasDastProfileError() {
if (this.isLoadingProfiles) {
return;
}
const savedScannerProfileDoesNotExist =
this.savedScannerProfileName &&
!this.doesProfileExist(this.scannerProfiles, this.savedScannerProfileName);
const savedSiteProfileDoesNotExist =
this.savedSiteProfileName &&
!this.doesProfileExist(this.siteProfiles, this.savedSiteProfileName);
if (savedScannerProfileDoesNotExist || savedSiteProfileDoesNotExist) {
this.$emit('error');
}
},
updateProfiles() {
this.$emit('profiles-selected', {
scannerProfile: this.selectedScannerProfile?.profileName,
......@@ -121,6 +131,7 @@ export default {
<dast-profiles-drawer
:active-profile="activeProfile"
:full-path="namespacePath"
:open="isSideDrawerOpen"
:is-loading="isLoadingProfiles"
:profiles="selectedProfiles"
......
......@@ -89,6 +89,7 @@ export default {
: DEFAULT_SCAN_EXECUTION_POLICY;
const { policy, hasParsingError } = createPolicyObject(yamlEditorValue);
const parsingError = hasParsingError ? this.$options.i18n.PARSING_ERROR_MESSAGE : '';
return {
isCreatingMR: false,
......@@ -96,7 +97,7 @@ export default {
newlyCreatedPolicyProject: null,
policy,
hasParsingError,
parsingError: this.$options.i18n.PARSING_ERROR_MESSAGE,
parsingError,
yamlEditorValue,
mode: EDITOR_MODE_RULE,
documentationPath: setUrlFragment(
......@@ -203,7 +204,7 @@ export default {
this.yamlEditorValue = manifest;
this.hasParsingError = hasParsingError;
this.parsingError = this.$options.i18n.PARSING_ERROR_MESSAGE;
this.parsingError = hasParsingError ? this.$options.i18n.PARSING_ERROR_MESSAGE : '';
this.policy = policy;
},
updateYamlEditorValue(policy) {
......
import { GlSprintf, GlTruncate } from '@gitlab/ui';
import siteProfilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql.basic.json';
import scannerProfilesFixtures from 'test_fixtures/graphql/security_configuration/dast_profiles/graphql/dast_scanner_profiles.query.graphql.basic.json';
import { createMockApolloProvider } from 'ee_jest/security_configuration/dast_profiles/graphql/create_mock_apollo_provider';
import waitForPromises from 'helpers/wait_for_promises';
import DastProfilesDrawer from 'ee/security_configuration/dast_profiles/dast_profiles_drawer/dast_profiles_drawer.vue';
import DastProfilesDrawerHeader from 'ee/security_configuration/dast_profiles/dast_profiles_drawer/dast_profiles_drawer_header.vue';
import ProjectDastProfileSelector from 'ee/security_orchestration/components/policy_editor/scan_execution_policy/project_dast_profile_selector.vue';
import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createMockApolloProvider } from 'ee_jest/security_configuration/dast_profiles/graphql/create_mock_apollo_provider';
import { SCANNER_TYPE, SITE_TYPE } from 'ee/on_demand_scans/constants';
describe('ProjectDastProfileSelector', () => {
let wrapper;
const createComponent = (mountFn) => (props = {}, handlers = []) => {
const createComponent = (mountFn) => ({ props = {}, handlers = [] } = {}) => {
wrapper = mountFn(ProjectDastProfileSelector, {
propsData: {
...props,
},
apolloProvider: createMockApolloProvider(handlers),
provide: {
namespacePath: 'path/to/project',
},
stubs: {
GlSprintf,
},
......@@ -31,63 +35,85 @@ describe('ProjectDastProfileSelector', () => {
const findSiteProfileTrigger = () => wrapper.findByTestId('site-profile-trigger');
const findTriggerText = (trigger) => trigger.findComponent(GlTruncate);
it('renders scanner and site profiles triggers', () => {
createShallowComponent();
const scannerTrigger = findScannerProfileTrigger();
const siteTrigger = findSiteProfileTrigger();
describe('loading', () => {
it('renders scanner and site profiles triggers', () => {
createShallowComponent();
expect(scannerTrigger.exists()).toBe(true);
expect(siteTrigger.exists()).toBe(true);
const scannerTrigger = findScannerProfileTrigger();
const siteTrigger = findSiteProfileTrigger();
expect(findTriggerText(scannerTrigger).props('text')).toBe('Select scanner profile');
expect(findTriggerText(siteTrigger).props('text')).toBe('Select site profile');
expect(scannerTrigger.exists()).toBe(true);
expect(siteTrigger.exists()).toBe(true);
expect(scannerTrigger.props('loading')).toBe(true);
expect(siteTrigger.props('loading')).toBe(true);
});
});
it.each`
findTrigger | profileType | drawerHeader
${findScannerProfileTrigger} | ${'scanner'} | ${'Scanner profile library'}
${findSiteProfileTrigger} | ${'site'} | ${'Site profile library'}
`(
'opens a drawer with correct profile type',
async ({ findTrigger, profileType, drawerHeader }) => {
createFullComponent();
expect(findDrawerComponent().props('open')).toBe(false);
findTrigger().vm.$emit('click');
describe('default', () => {
it('renders scanner and site profiles triggers', async () => {
createShallowComponent();
await waitForPromises();
expect(findDrawerHeaderComponent().props('profileType')).toBe(profileType);
expect(findDrawerHeaderComponent().text()).toContain(drawerHeader);
expect(findDrawerComponent().props('open')).toBe(true);
},
);
it.each`
findTrigger | profileType | selectedProfile | selectedProfileName
${findScannerProfileTrigger} | ${SCANNER_TYPE} | ${'gid://gitlab/DastScannerProfile/2'} | ${'Passive scanner'}
${findSiteProfileTrigger} | ${SITE_TYPE} | ${'gid://gitlab/DastSiteProfile/1'} | ${'Non-validated'}
`(
'selects profile',
async ({ findTrigger, profileType, selectedProfile, selectedProfileName }) => {
createFullComponent();
findDrawerComponent().vm.$emit('select-profile', {
profile: { id: selectedProfile },
profileType,
});
const scannerTrigger = findScannerProfileTrigger();
const siteTrigger = findSiteProfileTrigger();
await waitForPromises();
expect(findTriggerText(findTrigger()).props('text')).toBe(selectedProfileName);
expect(wrapper.emitted('profiles-selected')).toHaveLength(2);
},
);
expect(scannerTrigger.exists()).toBe(true);
expect(siteTrigger.exists()).toBe(true);
expect(findTriggerText(scannerTrigger).props('text')).toBe('Select scanner profile');
expect(findTriggerText(siteTrigger).props('text')).toBe('Select site profile');
expect(scannerTrigger.props('loading')).toBe(false);
expect(siteTrigger.props('loading')).toBe(false);
});
describe('saved scanner profiles', () => {
it.each`
findTrigger | profileType | drawerHeader
${findScannerProfileTrigger} | ${'scanner'} | ${'Scanner profile library'}
${findSiteProfileTrigger} | ${'site'} | ${'Site profile library'}
`(
'opens a drawer with correct profile type',
async ({ findTrigger, profileType, drawerHeader }) => {
createFullComponent();
expect(findDrawerComponent().props('open')).toBe(false);
findTrigger().vm.$emit('click');
await waitForPromises();
expect(findDrawerHeaderComponent().props('profileType')).toBe(profileType);
expect(findDrawerHeaderComponent().text()).toContain(drawerHeader);
expect(findDrawerComponent().props('open')).toBe(true);
},
);
it.each`
findTrigger | profileType | selectedProfile | selectedProfileName
${findScannerProfileTrigger} | ${SCANNER_TYPE} | ${'gid://gitlab/DastScannerProfile/2'} | ${'Passive scanner'}
${findSiteProfileTrigger} | ${SITE_TYPE} | ${'gid://gitlab/DastSiteProfile/1'} | ${'Non-validated'}
`(
'selects profile',
async ({ findTrigger, profileType, selectedProfile, selectedProfileName }) => {
createShallowComponent();
findDrawerComponent().vm.$emit('select-profile', {
profile: { id: selectedProfile },
profileType,
});
await waitForPromises();
expect(findTriggerText(findTrigger()).props('text')).toBe(selectedProfileName);
expect(wrapper.emitted('profiles-selected')).toHaveLength(2);
},
);
});
describe('existing profiles', () => {
it('selects existing scanners', async () => {
createShallowComponent({
savedScannerProfileName: 'Passive scanner',
savedSiteProfileName: 'Non-validated',
props: {
savedScannerProfileName: 'Passive scanner',
savedSiteProfileName: 'Non-validated',
},
});
await waitForPromises();
......@@ -97,13 +123,41 @@ describe('ProjectDastProfileSelector', () => {
});
describe('error handling', () => {
it('should emit error if saved profile does not exist', async () => {
it('does not emit an error if the profiles are loading', () => {
createShallowComponent();
expect(wrapper.emitted('error')).toBeUndefined();
});
it('does not emit an error if no profiles are selected', async () => {
createShallowComponent();
expect(wrapper.emitted('error')).toBeUndefined();
await waitForPromises();
expect(wrapper.emitted('error')).toBeUndefined();
});
it('does not emit an error if the profiles selected profiles are valid', async () => {
const validSiteProfile = siteProfilesFixtures.data.project.siteProfiles.nodes[0].profileName;
const validScannerProfile =
scannerProfilesFixtures.data.project.scannerProfiles.nodes[0].profileName;
createShallowComponent({
savedScannerProfileName: 'Non-existing Passive scanner',
savedSiteProfileName: 'Non-existing Non-validated',
props: {
savedScannerProfileName: validScannerProfile,
savedSiteProfileName: validSiteProfile,
},
});
await waitForPromises();
expect(wrapper.emitted('error')).toBeUndefined();
});
it('emits an error if saved profile is invalid', async () => {
createShallowComponent({
props: {
savedScannerProfileName: 'invalidScannerProfileName',
savedSiteProfileName: 'invalidSiteProfileName',
},
});
expect(wrapper.emitted('error')).toBeUndefined();
await waitForPromises();
expect(wrapper.emitted('error')).toHaveLength(2);
});
});
......
......@@ -100,6 +100,19 @@ describe('ScanExecutionPolicyEditor', () => {
const findPolicyRuleBuilder = () => wrapper.findComponent(PolicyRuleBuilder);
const findAllPolicyRuleBuilders = () => wrapper.findAllComponents(PolicyRuleBuilder);
describe('default', () => {
beforeEach(() => {
factory();
});
it('should render correctly', () => {
expect(findPolicyEditorLayout().props()).toMatchObject({
hasParsingError: false,
parsingError: '',
});
});
});
describe('saving a policy', () => {
it.each`
status | action | event | factoryFn | yamlEditorValue | currentlyAssignedPolicyProject
......@@ -152,14 +165,20 @@ describe('ScanExecutionPolicyEditor', () => {
const newManifest = `name: test
enabled: true`;
expect(findPolicyEditorLayout().props('yamlEditorValue')).toBe(DEFAULT_SCAN_EXECUTION_POLICY);
expect(findPolicyEditorLayout().props('policy')).toMatchObject(
fromYaml({ manifest: DEFAULT_SCAN_EXECUTION_POLICY }),
);
expect(findPolicyEditorLayout().props()).toMatchObject({
hasParsingError: false,
parsingError: '',
policy: fromYaml({ manifest: DEFAULT_SCAN_EXECUTION_POLICY }),
yamlEditorValue: DEFAULT_SCAN_EXECUTION_POLICY,
});
findPolicyEditorLayout().vm.$emit('update-yaml', newManifest);
await nextTick();
expect(findPolicyEditorLayout().props('yamlEditorValue')).toBe(newManifest);
expect(findPolicyEditorLayout().props('policy')).toMatchObject({ enabled: true });
expect(findPolicyEditorLayout().props()).toMatchObject({
hasParsingError: false,
parsingError: '',
policy: expect.objectContaining({ enabled: true }),
yamlEditorValue: newManifest,
});
});
it.each`
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment