Commit e3d22298 authored by Scott Hampton's avatar Scott Hampton

Merge branch 'djadmin-update-placeholders-dast-profile' into 'master'

Update placeholders for DAST Profile sensitive fields

See merge request gitlab-org/gitlab!56496
parents 656a918a f4e5a483
......@@ -38,7 +38,6 @@ import {
ERROR_MESSAGES,
SCANNER_PROFILES_QUERY,
SITE_PROFILES_QUERY,
SITE_PROFILES_EXTENDED_QUERY,
TYPE_SITE_PROFILE,
TYPE_SCANNER_PROFILE,
} from '../settings';
......@@ -101,15 +100,11 @@ export default {
'selectedScannerProfileId',
SCANNER_PROFILES_QUERY,
),
siteProfiles() {
return createProfilesApolloOptions(
siteProfiles: createProfilesApolloOptions(
'siteProfiles',
'selectedSiteProfileId',
this.glFeatures.securityDastSiteProfilesAdditionalFields
? SITE_PROFILES_EXTENDED_QUERY
: SITE_PROFILES_QUERY,
);
},
SITE_PROFILES_QUERY,
),
},
inject: {
dastSiteValidationDocsPath: {
......@@ -233,6 +228,9 @@ export default {
selectedSiteProfileId,
};
},
hasExcludedUrls() {
return this.selectedSiteProfile.excludedUrls?.length > 0;
},
},
created() {
const params = queryToObject(window.location.search);
......@@ -499,6 +497,10 @@ export default {
:label="s__('DastProfiles|Username')"
:value="selectedSiteProfile.auth.username"
/>
<profile-selector-summary-cell
:label="s__('DastProfiles|Password')"
value="••••••••"
/>
</div>
<div class="row">
<profile-selector-summary-cell
......@@ -513,12 +515,14 @@ export default {
</template>
<div class="row">
<profile-selector-summary-cell
v-if="hasExcludedUrls"
:label="s__('DastProfiles|Excluded URLs')"
:value="selectedSiteProfile.excludedUrls.join($options.EXCLUDED_URLS_SEPARATOR)"
/>
<profile-selector-summary-cell
v-if="selectedSiteProfile.requestHeaders"
:label="s__('DastProfiles|Request headers')"
:value="selectedSiteProfile.requestHeaders"
:value="__('[Redacted]')"
/>
</div>
</template>
......
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { resolvers } from 'ee/security_configuration/dast_profiles/graphql/provider';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export default new VueApollo({
defaultClient: createDefaultClient(),
defaultClient: createDefaultClient(resolvers),
});
import dastScannerProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_scanner_profiles.query.graphql';
import dastSiteProfilesQuery from 'ee/security_configuration/dast_profiles/graphql/dast_site_profiles.query.graphql';
import dastSiteProfilesExtendedQuery from 'ee/security_configuration/dast_profiles/graphql/dast_site_profiles_extended.query.graphql';
import { s__ } from '~/locale';
export const ERROR_RUN_SCAN = 'ERROR_RUN_SCAN';
......@@ -29,10 +28,5 @@ export const SITE_PROFILES_QUERY = {
fetchError: ERROR_FETCH_SITE_PROFILES,
};
export const SITE_PROFILES_EXTENDED_QUERY = {
...SITE_PROFILES_QUERY,
fetchQuery: dastSiteProfilesExtendedQuery,
};
export const TYPE_SITE_PROFILE = 'DastSiteProfile';
export const TYPE_SCANNER_PROFILE = 'DastScannerProfile';
......@@ -17,6 +17,15 @@ query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first:
editPath
validationStatus
referencedInSecurityPolicies
auth @client {
enabled
url
usernameField
passwordField
username
}
excludedUrls @client
requestHeaders @client
}
}
}
......
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
query DastSiteProfiles($fullPath: ID!, $after: String, $before: String, $first: Int, $last: Int) {
project(fullPath: $fullPath) {
siteProfiles: dastSiteProfiles(after: $after, before: $before, first: $first, last: $last)
@connection(key: "dastSiteProfiles") {
pageInfo {
...PageInfo
}
edges {
cursor
node {
id
profileName
normalizedTargetUrl
targetUrl
editPath
validationStatus
auth @client {
enabled
url
usernameField
passwordField
username
}
excludedUrls @client
requestHeaders @client
referencedInSecurityPolicies
}
}
}
}
}
......@@ -4,6 +4,21 @@ import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export const resolvers = {
DastSiteProfile: {
auth: () => ({
__typename: 'DastSiteProfileAuth',
enabled: true,
url: 'http://test.local/users/sign_in',
usernameField: 'username',
passwordField: 'password',
username: 'root',
}),
excludedUrls: () => ['http://test.local/sign_out', 'http://test.local/send_mail'],
requestHeaders: () => 'log-identifier: dast-active-scan',
},
};
export default new VueApollo({
defaultClient: createDefaultClient({}, { assumeImmutableResults: true }),
defaultClient: createDefaultClient(resolvers, { assumeImmutableResults: true }),
});
<script>
import { GlFormGroup, GlFormInput, GlFormCheckbox } from '@gitlab/ui';
import { initFormField } from 'ee/security_configuration/utils';
import { __ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
export default {
......@@ -65,8 +64,8 @@ export default {
showValidationOrInEditMode() {
return this.showValidation || this.isEditMode;
},
sensitiveFieldPlaceholder() {
return this.isEditMode ? __('[Unchanged]') : '';
passwordFieldPlaceholder() {
return this.isEditMode ? '••••••••' : '';
},
},
watch: {
......@@ -132,7 +131,7 @@ export default {
autocomplete="off"
name="password"
type="password"
:placeholder="sensitiveFieldPlaceholder"
:placeholder="passwordFieldPlaceholder"
:required="isSensitiveFieldRequired"
:state="form.fields.password.state"
/>
......
......@@ -65,8 +65,7 @@ export default {
},
},
data() {
const { name = '', targetUrl = '', excludedUrls = [], requestHeaders = '', auth = {} } =
this.siteProfile || {};
const { name = '', targetUrl = '', excludedUrls = [], auth = {} } = this.siteProfile || {};
const form = {
state: false,
......@@ -80,7 +79,7 @@ export default {
skipValidation: true,
}),
requestHeaders: initFormField({
value: requestHeaders,
value: '',
required: false,
skipValidation: true,
}),
......@@ -108,6 +107,9 @@ export default {
isEdit() {
return Boolean(this.siteProfile?.id);
},
hasRequestHeaders() {
return Boolean(this.siteProfile?.requestHeaders);
},
i18n() {
const { isEdit } = this;
return {
......@@ -138,8 +140,10 @@ export default {
tooltip: s__(
'DastProfiles|Request header names and values. Headers are added to every request made by DAST.',
),
// eslint-disable-next-line @gitlab/require-i18n-strings
placeholder: 'Cache-control: no-cache, User-Agent: DAST/1.0',
placeholder: this.hasRequestHeaders
? __('[Redacted]')
: // eslint-disable-next-line @gitlab/require-i18n-strings
'Cache-control: no-cache, User-Agent: DAST/1.0',
},
};
},
......
......@@ -12,6 +12,7 @@ import dastSiteProfilesQuery from 'ee/security_configuration/dast_profiles/graph
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import createApolloProvider from 'helpers/mock_apollo_helper';
import { stubComponent } from 'helpers/stub_component';
import waitForPromises from 'helpers/wait_for_promises';
import { redirectTo, setUrlParams } from '~/lib/utils/url_utility';
import RefSelector from '~/ref/components/ref_selector.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
......@@ -113,7 +114,6 @@ describe('OnDemandScansForm', () => {
dastSiteProfiles: jest.fn().mockResolvedValue(responses.dastSiteProfiles()),
...handlers,
};
return createApolloProvider([
[dastScannerProfilesQuery, requestHandlers.dastScannerProfiles],
[dastSiteProfilesQuery, requestHandlers.dastSiteProfiles],
......@@ -499,13 +499,15 @@ describe('OnDemandScansForm', () => {
`('when there is a single $profileType profile', ({ query, selector, profiles }) => {
const [profile] = profiles;
beforeEach(() => {
beforeEach(async () => {
mountShallowSubject(
{},
{
[query]: jest.fn().mockResolvedValue(responses[query]([profile])),
},
);
await waitForPromises();
});
it('automatically selects the only available profile', () => {
......@@ -534,14 +536,17 @@ describe('OnDemandScansForm', () => {
it('renders all fields correctly', async () => {
await selectSiteProfile(authEnabledProfile);
const summary = subject.find(SiteProfileSelector).text();
const defaultPassword = '••••••••';
const defaultRequestHeaders = '[Redacted]';
expect(summary).toMatch(authEnabledProfile.targetUrl);
expect(summary).toMatch(authEnabledProfile.excludedUrls.join(','));
expect(summary).toMatch(authEnabledProfile.requestHeaders);
expect(summary).toMatch(authEnabledProfile.auth.url);
expect(summary).toMatch(authEnabledProfile.auth.username);
expect(summary).toMatch(authEnabledProfile.auth.usernameField);
expect(summary).toMatch(authEnabledProfile.auth.passwordField);
expect(summary).toMatch(defaultPassword);
expect(summary).toMatch(defaultRequestHeaders);
});
});
......
......@@ -64,6 +64,10 @@ export const siteProfiles = [
validationStatus: 'PASSED_VALIDATION',
auth: {
enabled: false,
url: 'https://foo.com/login',
usernameField: 'username',
passwordField: 'password',
username: 'admin',
},
excludedUrls: ['https://bar.com/logout'],
requestHeaders: 'auth: gitlab-dast',
......
......@@ -175,6 +175,33 @@ describe('DastSiteProfileForm', () => {
expect(findExcludedUrlsInput().attributes('maxlength')).toBe('2048');
expect(findRequestHeadersInput().attributes('maxlength')).toBe('2048');
});
describe('should have correct placeholders', () => {
const defaultPlaceholder = 'Cache-control: no-cache, User-Agent: DAST/1.0';
it('when creating a new profile', async () => {
expect(findRequestHeadersInput().attributes('placeholder')).toBe(defaultPlaceholder);
});
it('when updating an existing profile with no request headers set', () => {
createFullComponent({
propsData: {
siteProfile: { ...siteProfileOne, requestHeaders: '' },
},
});
expect(findRequestHeadersInput().attributes('placeholder')).toBe(defaultPlaceholder);
});
it('when updating an existing profile', () => {
createFullComponent({
propsData: {
siteProfile: siteProfileOne,
},
});
expect(findRequestHeadersInput().attributes('placeholder')).toBe('[Redacted]');
expect(findByNameAttribute('password').attributes('placeholder')).toBe('••••••••');
});
});
});
describe.each`
......
......@@ -35160,7 +35160,7 @@ msgstr ""
msgid "[No reason]"
msgstr ""
msgid "[Unchanged]"
msgid "[Redacted]"
msgstr ""
msgid "`end_time` should not exceed one month after `start_time`"
......
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