Commit 03a09778 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '299126-api-fuzzing-configuration-form' into 'master'

Create API fuzzing configuration form

See merge request gitlab-org/gitlab!52540
parents de936d35 e8f23734
......@@ -2,3 +2,4 @@
filenames:
- ee/app/assets/javascripts/on_demand_scans/graphql/dast_scan_create.mutation.graphql
- ee/app/assets/javascripts/oncall_schedules/graphql/mutations/update_oncall_schedule_rotation.mutation.graphql
- ee/app/assets/javascripts/security_configuration/api_fuzzing/graphql/api_fuzzing_ci_configuration.query.graphql
import { initApiFuzzingConfiguration } from 'ee/security_configuration/api_fuzzing/api_fuzzing_configuration_init';
initApiFuzzingConfiguration();
import Vue from 'vue';
import { parseBoolean } from '~/lib/utils/common_utils';
import { apolloProvider } from './graphql/provider';
import ApiFuzzingApp from './components/app.vue';
export const initApiFuzzingConfiguration = () => {
const el = document.querySelector('.js-api-fuzzing-configuration');
if (!el) {
return undefined;
}
const {
fullPath,
apiFuzzingDocumentationPath,
apiFuzzingAuthenticationDocumentationPath,
ciVariablesDocumentationPath,
projectCiSettingsPath,
} = el.dataset;
const canSetProjectCiVariables = parseBoolean(el.dataset.canSetProjectCiVariables);
return new Vue({
el,
apolloProvider,
provide: {
fullPath,
apiFuzzingDocumentationPath,
apiFuzzingAuthenticationDocumentationPath,
ciVariablesDocumentationPath,
projectCiSettingsPath,
canSetProjectCiVariables,
},
render(createElement) {
return createElement(ApiFuzzingApp);
},
});
};
<script>
import { GlAlert, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import apiFuzzingCiConfigurationQuery from '../graphql/api_fuzzing_ci_configuration.query.graphql';
import ConfigurationForm from './configuration_form.vue';
export default {
components: {
GlAlert,
GlLink,
GlLoadingIcon,
GlSprintf,
ConfigurationForm,
},
inject: {
fullPath: {
from: 'fullPath',
},
apiFuzzingDocumentationPath: {
from: 'apiFuzzingDocumentationPath',
},
},
apollo: {
apiFuzzingCiConfiguration: {
query: apiFuzzingCiConfigurationQuery,
variables() {
return {
fullPath: this.fullPath,
};
},
update({ project: { apiFuzzingCiConfiguration } }) {
return apiFuzzingCiConfiguration;
},
},
},
i18n: {
title: s__('APIFuzzing|API Fuzzing Configuration'),
helpText: s__(`
APIFuzzing|Customize common API fuzzing settings to suit your requirements.
For details of more advanced configuration options, see the
%{docsLinkStart}GitLab API Fuzzing documentation%{docsLinkEnd}.`),
notice: s__(`
APIFuzzing|Use this tool to generate API fuzzing configuration YAML to copy into your
.gitlab-ci.yml file. This tool does not reflect or update your .gitlab-ci.yml file automatically.
`),
},
};
</script>
<template>
<article>
<header class="gl-mt-5 gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid">
<h4>
{{ s__('APIFuzzing|API Fuzzing Configuration') }}
</h4>
<p>
<gl-sprintf :message="$options.i18n.helpText">
<template #docsLink="{ content }">
<gl-link :href="apiFuzzingDocumentationPath" target="_blank">{{ content }}</gl-link>
</template>
</gl-sprintf>
</p>
</header>
<gl-alert :dismissible="false" class="gl-mb-5">
{{ $options.i18n.notice }}
</gl-alert>
<gl-loading-icon v-if="$apollo.loading" size="lg" />
<configuration-form v-else :api-fuzzing-ci-configuration="apiFuzzingCiConfiguration" />
</article>
</template>
<script>
import {
GlAccordion,
GlAccordionItem,
GlAlert,
GlButton,
GlFormGroup,
GlFormText,
GlFormCheckbox,
GlLink,
GlSprintf,
} from '@gitlab/ui';
import { __, s__ } from '~/locale';
import { SCAN_MODES } from '../constants';
import DropdownInput from '../../components/dropdown_input.vue';
import DynamicFields from '../../components/dynamic_fields.vue';
import FormInput from '../../components/form_input.vue';
export default {
components: {
GlAccordion,
GlAccordionItem,
GlAlert,
GlButton,
GlFormGroup,
GlFormText,
GlFormCheckbox,
GlLink,
GlSprintf,
DropdownInput,
DynamicFields,
FormInput,
},
inject: {
apiFuzzingAuthenticationDocumentationPath: {
from: 'apiFuzzingAuthenticationDocumentationPath',
},
ciVariablesDocumentationPath: {
from: 'ciVariablesDocumentationPath',
},
projectCiSettingsPath: {
from: 'projectCiSettingsPath',
},
canSetProjectCiVariables: {
from: 'canSetProjectCiVariables',
},
},
props: {
apiFuzzingCiConfiguration: {
type: Object,
required: true,
},
},
data() {
return {
targetUrl: {
field: 'targetUrl',
label: s__('APIFuzzing|Target URL'),
description: s__('APIFuzzing|Base URL of API fuzzing target.'),
placeholder: __('Ex: Example.com'),
value: '',
},
scanMode: {
field: 'scanMode',
label: s__('APIFuzzing|Scan mode'),
description: s__('APIFuzzing|There are two ways to perform scans.'),
value: '',
defaultText: s__('APIFuzzing|Choose a method'),
options: this.apiFuzzingCiConfiguration.scanModes.map((value) => ({
value,
text: SCAN_MODES[value].scanModeLabel,
})),
},
apiSpecificationFile: {
field: 'apiSpecificationFile',
value: '',
},
authenticationEnabled: false,
authenticationSettings: [
{
type: 'string',
field: 'username',
label: s__('APIFuzzing|Username for basic authentication'),
description: s__(
'APIFuzzing|Instead of entering the username directly, enter the key of the CI variable set to the username.',
),
placeholder: s__('APIFuzzing|Ex: $TestUsername'),
value: '',
},
{
type: 'string',
field: 'password',
label: s__('APIFuzzing|Password for basic authentication'),
description: s__(
'APIFuzzing|Instead of entering the password directly, enter the key of the CI variable set to the password.',
),
placeholder: s__('APIFuzzing|Ex: $TestPassword'),
value: '',
},
],
scanProfile: {
field: 'scanProfile',
label: s__('APIFuzzing|Scan profile'),
description: 'Pre-defined profiles by GitLab.',
value: '',
defaultText: s__('APIFuzzing|Choose a profile'),
options: this.apiFuzzingCiConfiguration.scanProfiles.map(
({ name: value, description: text }) => ({
value,
text,
}),
),
},
};
},
computed: {
authAlertI18n() {
return this.canSetProjectCiVariables
? {
title: s__('APIFuzzing|Make sure your credentials are secured'),
text: s__(
`APIFuzzing|To prevent a security leak, authentication info must be added as a
%{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. As a user with maintainer access
rights, you can manage CI variables in the
%{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area.`,
),
}
: {
title: s__("APIFuzzing|You may need a maintainer's help to secure your credentials."),
text: s__(
`APIFuzzing|To prevent a security leak, authentication info must be added as a
%{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. A user with maintainer
access rights can manage CI variables in the
%{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area. We detected that you are not
a maintainer. Commit your changes and assign them to a maintainer to update the
credentials before merging.`,
),
};
},
scanProfileYaml() {
return this.apiFuzzingCiConfiguration.scanProfiles.find(
({ name }) => name === this.scanProfile.value,
)?.yaml;
},
},
methods: {
onSubmit() {},
},
SCAN_MODES,
};
</script>
<template>
<form @submit.prevent="onSubmit">
<form-input v-model="targetUrl.value" v-bind="targetUrl" class="gl-mb-7" />
<dropdown-input v-model="scanMode.value" v-bind="scanMode" />
<form-input
v-if="scanMode.value"
v-model="apiSpecificationFile.value"
v-bind="{ ...apiSpecificationFile, ...$options.SCAN_MODES[scanMode.value] }"
/>
<gl-form-group class="gl-my-7">
<template #label>
{{ __('Authentication') }}
<gl-form-text class="gl-mt-3">
<gl-sprintf
:message="
s__(
'APIFuzzing|Authentication is handled by providing HTTP basic authentication token as a header or cookie. %{linkStart}More information%{linkEnd}.',
)
"
>
<template #link="{ content }">
<a :href="apiFuzzingAuthenticationDocumentationPath">
{{ content }}
</a>
</template>
</gl-sprintf>
</gl-form-text>
</template>
<gl-form-checkbox
v-model="authenticationEnabled"
data-testid="api-fuzzing-enable-authentication-checkbox"
>
{{ s__('APIFuzzing|Enable authentication') }}
</gl-form-checkbox>
</gl-form-group>
<template v-if="authenticationEnabled">
<gl-alert
:title="authAlertI18n.title"
:dismissible="false"
variant="warning"
class="gl-mb-5"
data-testid="api-fuzzing-authentication-notice"
>
<gl-sprintf :message="authAlertI18n.text">
<template #ciVariablesLink="{ content }">
<gl-link :href="ciVariablesDocumentationPath" target="_blank">
{{ content }}
</gl-link>
</template>
<template #ciSettingsLink="{ content }">
<gl-link :href="projectCiSettingsPath" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</gl-alert>
<dynamic-fields v-model="authenticationSettings" />
</template>
<dropdown-input v-model="scanProfile.value" v-bind="scanProfile" />
<template v-if="scanProfileYaml">
<gl-accordion>
<gl-accordion-item :title="s__('APIFuzzing|Show code snippet for the profile')">
<pre data-testid="api-fuzzing-scan-profile-yaml-viewer">{{ scanProfileYaml }}</pre>
</gl-accordion-item>
</gl-accordion>
</template>
<hr />
<gl-button type="submit" variant="confirm">{{
s__('APIFuzzing|Generate code snippet')
}}</gl-button>
<gl-button>{{ __('Cancel') }}</gl-button>
</form>
</template>
import { __, s__ } from '~/locale';
export const SCAN_MODES = {
HAR: {
scanModeLabel: __('HAR (HTTP Archive)'),
label: __('HAR file path'),
placeholder: s__('APIFuzzing|Ex: Project_Test/File/example_fuzz.har'),
description: s__(
"APIFuzzing|HAR files may contain sensitive information such as authentication tokens, API keys, and session cookies. We recommend that you review the HAR files' contents before adding them to a repository.",
),
},
OPENAPI: {
scanModeLabel: __('Open API'),
label: __('Open API specification file path'),
placeholder: s__('APIFuzzing|Ex: Project_Test/File/example_fuzz.json'),
description: s__(
'APIFuzzing|We recommend that you review the JSON specifications file before adding it to a repository.',
),
},
};
query apiFuzzingCiConfiguration($fullPath: ID!) {
project(fullPath: $fullPath) {
apiFuzzingCiConfiguration {
scanModes
scanProfiles {
name
description
yaml
}
}
}
}
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
export const apolloProvider = new VueApollo({ defaultClient: createDefaultClient() });
......@@ -51,6 +51,11 @@ export default {
required: false,
default: false,
},
placeholder: {
type: String,
required: false,
default: '',
},
},
computed: {
showCustomValueMessage() {
......@@ -83,6 +88,7 @@ export default {
:size="inputSize"
:value="value"
:disabled="disabled"
:placeholder="placeholder"
@input="$emit('input', $event)"
/>
......
# frozen_string_literal: true
module Projects::Security::ApiFuzzingConfigurationHelper
def api_fuzzing_configuration_data(project)
{
full_path: project.full_path,
api_fuzzing_documentation_path: help_page_path('user/application_security/api_fuzzing/index'),
api_fuzzing_authentication_documentation_path: help_page_path('user/application_security/api_fuzzing/index', anchor: 'authentication'),
ci_variables_documentation_path: help_page_path('ci/variables/README'),
project_ci_settings_path: project_settings_ci_cd_path(project),
can_set_project_ci_variables: can?(current_user, :admin_pipeline, project).to_s
}
end
end
......@@ -2,4 +2,4 @@
- breadcrumb_title _("API Fuzzing Configuration")
- page_title _("API Fuzzing Configuration")
%h1= "API fuzzing configuration"
.js-api-fuzzing-configuration{ data: api_fuzzing_configuration_data(@project) }
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { shallowMount } from '@vue/test-utils';
import { merge } from 'lodash';
import { GlAlert, GlLink, GlLoadingIcon, GlSprintf } from '@gitlab/ui';
import { stripTypenames } from 'helpers/graphql_helpers';
import createMockApollo from 'helpers/mock_apollo_helper';
import apiFuzzingCiConfigurationQuery from 'ee/security_configuration/api_fuzzing/graphql/api_fuzzing_ci_configuration.query.graphql';
import App from 'ee/security_configuration/api_fuzzing/components/app.vue';
import ConfigurationForm from 'ee/security_configuration/api_fuzzing/components/configuration_form.vue';
import { apiFuzzingConfigurationQueryResponse } from '../mock_data';
Vue.use(VueApollo);
describe('EE - ApiFuzzingConfigurationApp', () => {
let wrapper;
const projectFullPath = 'namespace/project';
const pendingHandler = jest.fn(() => new Promise(() => {}));
const successHandler = jest.fn(async () => apiFuzzingConfigurationQueryResponse);
const createMockApolloProvider = (handler) =>
createMockApollo([[apiFuzzingCiConfigurationQuery, handler]]);
const findLoadingSpinner = () => wrapper.find(GlLoadingIcon);
const findConfigurationForm = () => wrapper.find(ConfigurationForm);
const createWrapper = (options) => {
wrapper = shallowMount(
App,
merge(
{
apolloProvider: () => createMockApolloProvider(successHandler),
stubs: {
GlSprintf,
},
provide: {
fullPath: projectFullPath,
apiFuzzingDocumentationPath: '/api_fuzzing/documentation/path',
},
data() {
return {
apiFuzzingCiConfiguration: {},
};
},
},
options,
),
);
};
afterEach(() => {
wrapper.destroy();
});
it('shows a loading spinner while fetching the configuration from the API', () => {
createWrapper({
apolloProvider: createMockApolloProvider(pendingHandler),
});
expect(pendingHandler).toHaveBeenCalledWith({ fullPath: projectFullPath });
expect(findLoadingSpinner().exists()).toBe(true);
expect(findConfigurationForm().exists()).toBe(false);
});
describe('configuration fetched successfully', () => {
beforeEach(() => {
createWrapper();
});
it('shows the form once the configuration has loaded', () => {
expect(findConfigurationForm().exists()).toBe(true);
expect(findLoadingSpinner().exists()).toBe(false);
});
it('passes the configuration to the form', () => {
expect(findConfigurationForm().props('apiFuzzingCiConfiguration')).toEqual(
stripTypenames(apiFuzzingConfigurationQueryResponse.data.project.apiFuzzingCiConfiguration),
);
});
it("shows a notice about the tool's purpose", () => {
const alert = wrapper.find(GlAlert);
expect(alert.exists()).toBe(true);
expect(alert.text()).toBe(
'Use this tool to generate API fuzzing configuration YAML to copy into your .gitlab-ci.yml file. This tool does not reflect or update your .gitlab-ci.yml file automatically.',
);
});
it('includes a link to API fuzzing documentation ', () => {
const link = wrapper.find(GlLink);
expect(link.exists()).toBe(true);
expect(link.attributes('href')).toBe('/api_fuzzing/documentation/path');
});
});
});
import { mount } from '@vue/test-utils';
import { merge } from 'lodash';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { SCAN_MODES } from 'ee/security_configuration/api_fuzzing/constants';
import ConfigurationForm from 'ee/security_configuration/api_fuzzing/components/configuration_form.vue';
import FormInput from 'ee/security_configuration/components/form_input.vue';
import DropdownInput from 'ee/security_configuration/components/dropdown_input.vue';
const makeScanProfile = (name) => ({
name,
description: `${name} description`,
yaml: `
---
:Name: ${name}
`.trim(),
});
describe('EE - ApiFuzzingConfigurationForm', () => {
let wrapper;
const apiFuzzingCiConfiguration = {
scanModes: Object.keys(SCAN_MODES),
scanProfiles: [makeScanProfile('Quick-10'), makeScanProfile('Medium-20')],
};
const findEnableAuthenticationCheckbox = () =>
wrapper.findByTestId('api-fuzzing-enable-authentication-checkbox');
const findScanModeInput = () => wrapper.findAll(DropdownInput).at(0);
const findSpecificationFileInput = () => wrapper.findAll(FormInput).at(1);
const findAuthenticationNotice = () => wrapper.findByTestId('api-fuzzing-authentication-notice');
const findScanProfileDropdownInput = () => wrapper.findAll(DropdownInput).at(1);
const findScanProfileYamlViewer = () =>
wrapper.findByTestId('api-fuzzing-scan-profile-yaml-viewer');
const createWrapper = (options = {}) => {
wrapper = extendedWrapper(
mount(
ConfigurationForm,
merge(
{
provide: {
apiFuzzingAuthenticationDocumentationPath:
'api_fuzzing_authentication/documentation/path',
ciVariablesDocumentationPath: '/ci_cd_variables/documentation/path',
projectCiSettingsPath: '/project/settings/ci_cd',
canSetProjectCiVariables: true,
},
propsData: {
apiFuzzingCiConfiguration,
},
},
options,
),
),
);
};
afterEach(() => {
wrapper.destroy();
});
it('includes a link to API fuzzing authentication documentation', () => {
createWrapper();
expect(wrapper.html()).toContain('api_fuzzing_authentication/documentation/path');
});
describe('scan modes', () => {
beforeEach(() => {
createWrapper();
});
it('displays a dropdown option for each scan mode', () => {
findScanModeInput()
.findAll('li')
.wrappers.forEach((item, index) => {
expect(item.text()).toBe(
SCAN_MODES[apiFuzzingCiConfiguration.scanModes[index]].scanModeLabel,
);
});
});
it('by default, the specification file input is hidden', () => {
expect(wrapper.findAll(FormInput)).toHaveLength(1);
});
describe.each(Object.keys(SCAN_MODES))('when %s scan mode is selected', (scanMode) => {
it('the specificationfile input becomes visible and has the correct labels', async () => {
const selectedScanMode = SCAN_MODES[scanMode];
findScanModeInput().vm.$emit('input', scanMode);
await wrapper.vm.$nextTick();
const specificationFileInput = findSpecificationFileInput();
expect(specificationFileInput.exists()).toBe(true);
expect(specificationFileInput.text()).toContain(selectedScanMode.label);
expect(specificationFileInput.text()).toContain(selectedScanMode.description);
expect(specificationFileInput.find('input').attributes('placeholder')).toBe(
selectedScanMode.placeholder,
);
});
});
});
describe('authentication', () => {
it('authentication section is hidden by default', () => {
createWrapper();
expect(findAuthenticationNotice().exists()).toBe(false);
});
it('authentication section becomes visible once checkbox is checked', async () => {
createWrapper();
await findEnableAuthenticationCheckbox().trigger('click');
expect(findAuthenticationNotice().exists()).toBe(true);
});
it('sees the the proper notice as a maintainer', async () => {
createWrapper();
await findEnableAuthenticationCheckbox().trigger('click');
expect(findAuthenticationNotice().text()).toMatchInterpolatedText(
'Make sure your credentials are secured To prevent a security leak, authentication info must be added as a CI variable. As a user with maintainer access rights, you can manage CI variables in the Settings area.',
);
});
it('sees the the proper notice as a developer', async () => {
createWrapper({
provide: {
canSetProjectCiVariables: false,
},
});
await findEnableAuthenticationCheckbox().trigger('click');
expect(findAuthenticationNotice().text()).toMatchInterpolatedText(
"You may need a maintainer's help to secure your credentials. To prevent a security leak, authentication info must be added as a CI variable. A user with maintainer access rights can manage CI variables in the Settings area. We detected that you are not a maintainer. Commit your changes and assign them to a maintainer to update the credentials before merging.",
);
});
});
describe('scan profiles', () => {
beforeEach(() => {
createWrapper();
});
it('displays a dropdown option for each scan profile', () => {
findScanProfileDropdownInput()
.findAll('li')
.wrappers.forEach((item, index) => {
expect(item.text()).toBe(apiFuzzingCiConfiguration.scanProfiles[index].description);
});
});
it('by default, YAML viewer is not visible', () => {
expect(findScanProfileYamlViewer().exists()).toBe(false);
});
it('when a scan profile is selected, its YAML is visible', async () => {
const selectedScanProfile = apiFuzzingCiConfiguration.scanProfiles[0];
wrapper.findAll(DropdownInput).at(1).vm.$emit('input', selectedScanProfile.name);
await wrapper.vm.$nextTick();
expect(findScanProfileYamlViewer().exists()).toBe(true);
expect(findScanProfileYamlViewer().text()).toBe(selectedScanProfile.yaml);
});
});
});
export const apiFuzzingConfigurationQueryResponse = {
data: {
project: {
apiFuzzingCiConfiguration: {
scanModes: ['HAR', 'OPENAPI'],
scanProfiles: [
{
name: 'Quick-10',
description: 'Fuzzing 10 times per parameter',
yaml:
'---\nName: Quick-10\nDefaultProfile: Empty\nRoutes:\n- Route:\n Order: 0\n Url: "**"\n Mutate: true\n SwaggerUrl:\n Script:\n Headers:\n - Pattern: Host\n Mutate: false\n - Pattern: Connection\n Mutate: false\n - Pattern: Content-Length\n Mutate: false\n ApiTokens:\n - Name: Authorization\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: FormData\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: Query\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: X-API-Key\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n Checks:\n - Name: FormBodyFuzzingCheck\n Configuration:\n FuzzingCount: 10\n UnicodeFuzzing: true\n - Name: GeneralFuzzingCheck\n Configuration:\n FuzzingCount: 10\n UnicodeFuzzing: true\n HeaderFuzzing: false\n Headers:\n - Name: JsonFuzzingCheck\n Configuration:\n FuzzingCount: 10\n UnicodeFuzzing: true\n - Name: XmlFuzzingCheck\n Configuration:\n FuzzingCount: 10\n UnicodeFuzzing: true\n',
__typename: 'ApiFuzzingScanProfile',
},
{
name: 'Medium-20',
description: 'Fuzzing 20 times per parameter',
yaml:
'---\nName: Medium-20\nDefaultProfile: Empty\nRoutes:\n- Route:\n Order: 0\n Url: "**"\n Mutate: true\n SwaggerUrl:\n Script:\n Headers:\n - Pattern: Host\n Mutate: false\n - Pattern: Connection\n Mutate: false\n - Pattern: Content-Length\n Mutate: false\n ApiTokens:\n - Name: Authorization\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: FormData\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: Query\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: X-API-Key\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n Checks:\n - Name: FormBodyFuzzingCheck\n Configuration:\n FuzzingCount: 20\n UnicodeFuzzing: true\n - Name: GeneralFuzzingCheck\n Configuration:\n FuzzingCount: 20\n UnicodeFuzzing: true\n HeaderFuzzing: false\n Headers:\n - Name: JsonFuzzingCheck\n Configuration:\n FuzzingCount: 20\n UnicodeFuzzing: true\n - Name: XmlFuzzingCheck\n Configuration:\n FuzzingCount: 20\n UnicodeFuzzing: true\n',
__typename: 'ApiFuzzingScanProfile',
},
{
name: 'Medium-50',
description: 'Fuzzing 50 times per parameter',
yaml:
'---\nName: Medium-50\nDefaultProfile: Empty\nRoutes:\n- Route:\n Order: 0\n Url: "**"\n Mutate: true\n SwaggerUrl:\n Script:\n Headers:\n - Pattern: Host\n Mutate: false\n - Pattern: Connection\n Mutate: false\n - Pattern: Content-Length\n Mutate: false\n ApiTokens:\n - Name: Authorization\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: FormData\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: Query\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: X-API-Key\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n Checks:\n - Name: FormBodyFuzzingCheck\n Configuration:\n FuzzingCount: 50\n UnicodeFuzzing: true\n - Name: GeneralFuzzingCheck\n Configuration:\n FuzzingCount: 50\n UnicodeFuzzing: true\n HeaderFuzzing: false\n Headers:\n - Name: JsonFuzzingCheck\n Configuration:\n FuzzingCount: 50\n UnicodeFuzzing: true\n - Name: XmlFuzzingCheck\n Configuration:\n FuzzingCount: 50\n UnicodeFuzzing: true\n',
__typename: 'ApiFuzzingScanProfile',
},
{
name: 'Long-100',
description: 'Fuzzing 100 times per parameter',
yaml:
'---\nName: Long-100\nDefaultProfile: Empty\nRoutes:\n- Route:\n Order: 0\n Url: "**"\n Mutate: true\n SwaggerUrl:\n Script:\n Headers:\n - Pattern: Host\n Mutate: false\n - Pattern: Connection\n Mutate: false\n - Pattern: Content-Length\n Mutate: false\n ApiTokens:\n - Name: Authorization\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: FormData\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: access_token\n Where: Query\n Expiration: 120\n IsSignatureOfRequest: false\n - Name: X-API-Key\n Where: Header\n Expiration: 120\n IsSignatureOfRequest: false\n Checks:\n - Name: FormBodyFuzzingCheck\n Configuration:\n FuzzingCount: 100\n UnicodeFuzzing: true\n - Name: GeneralFuzzingCheck\n Configuration:\n FuzzingCount: 100\n UnicodeFuzzing: true\n HeaderFuzzing: false\n Headers:\n - Name: JsonFuzzingCheck\n Configuration:\n FuzzingCount: 100\n UnicodeFuzzing: true\n - Name: XmlFuzzingCheck\n Configuration:\n FuzzingCount: 100\n UnicodeFuzzing: true\n',
__typename: 'ApiFuzzingScanProfile',
},
],
__typename: 'ApiFuzzingCiConfiguration',
},
__typename: 'Project',
},
},
};
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Projects::Security::ApiFuzzingConfigurationHelper do
let_it_be(:project) { create(:project) }
let_it_be(:user) { create(:user) }
let(:full_path) { project.full_path }
let(:api_fuzzing_documentation_path) { help_page_path('user/application_security/api_fuzzing/index') }
let(:api_fuzzing_authentication_documentation_path) { help_page_path('user/application_security/api_fuzzing/index', anchor: 'authentication') }
let(:ci_variables_documentation_path) { help_page_path('ci/variables/README') }
let(:project_ci_settings_path) { project_settings_ci_cd_path(project) }
subject { helper.api_fuzzing_configuration_data(project) }
before do
allow(helper).to receive(:current_user).and_return(user)
end
describe '#api_fuzzing_configuration_data' do
context 'user with admin_pipeline permissions' do
before do
allow(helper).to receive(:can?).with(user, :admin_pipeline, project).and_return(true)
end
it {
is_expected.to eq(
full_path: full_path,
api_fuzzing_documentation_path: api_fuzzing_documentation_path,
api_fuzzing_authentication_documentation_path: api_fuzzing_authentication_documentation_path,
ci_variables_documentation_path: ci_variables_documentation_path,
project_ci_settings_path: project_ci_settings_path,
can_set_project_ci_variables: 'true'
)
}
end
context 'user without admin_pipeline permissions' do
before do
allow(helper).to receive(:can?).with(user, :admin_pipeline, project).and_return(false)
end
it {
is_expected.to eq(
full_path: full_path,
api_fuzzing_documentation_path: api_fuzzing_documentation_path,
api_fuzzing_authentication_documentation_path: api_fuzzing_authentication_documentation_path,
ci_variables_documentation_path: ci_variables_documentation_path,
project_ci_settings_path: project_ci_settings_path,
can_set_project_ci_variables: 'false'
)
}
end
end
end
......@@ -1393,6 +1393,90 @@ msgstr ""
msgid "API Token"
msgstr ""
msgid "APIFuzzing|API Fuzzing Configuration"
msgstr ""
msgid "APIFuzzing|Authentication is handled by providing HTTP basic authentication token as a header or cookie. %{linkStart}More information%{linkEnd}."
msgstr ""
msgid "APIFuzzing|Base URL of API fuzzing target."
msgstr ""
msgid "APIFuzzing|Choose a method"
msgstr ""
msgid "APIFuzzing|Choose a profile"
msgstr ""
msgid "APIFuzzing|Customize common API fuzzing settings to suit your requirements. For details of more advanced configuration options, see the %{docsLinkStart}GitLab API Fuzzing documentation%{docsLinkEnd}."
msgstr ""
msgid "APIFuzzing|Enable authentication"
msgstr ""
msgid "APIFuzzing|Ex: $TestPassword"
msgstr ""
msgid "APIFuzzing|Ex: $TestUsername"
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.har"
msgstr ""
msgid "APIFuzzing|Ex: Project_Test/File/example_fuzz.json"
msgstr ""
msgid "APIFuzzing|Generate code snippet"
msgstr ""
msgid "APIFuzzing|HAR files may contain sensitive information such as authentication tokens, API keys, and session cookies. We recommend that you review the HAR files' contents before adding them to a repository."
msgstr ""
msgid "APIFuzzing|Instead of entering the password directly, enter the key of the CI variable set to the password."
msgstr ""
msgid "APIFuzzing|Instead of entering the username directly, enter the key of the CI variable set to the username."
msgstr ""
msgid "APIFuzzing|Make sure your credentials are secured"
msgstr ""
msgid "APIFuzzing|Password for basic authentication"
msgstr ""
msgid "APIFuzzing|Scan mode"
msgstr ""
msgid "APIFuzzing|Scan profile"
msgstr ""
msgid "APIFuzzing|Show code snippet for the profile"
msgstr ""
msgid "APIFuzzing|Target URL"
msgstr ""
msgid "APIFuzzing|There are two ways to perform scans."
msgstr ""
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. A user with maintainer access rights can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area. We detected that you are not a maintainer. Commit your changes and assign them to a maintainer to update the credentials before merging."
msgstr ""
msgid "APIFuzzing|To prevent a security leak, authentication info must be added as a %{ciVariablesLinkStart}CI variable%{ciVariablesLinkEnd}. As a user with maintainer access rights, you can manage CI variables in the %{ciSettingsLinkStart}Settings%{ciSettingsLinkEnd} area."
msgstr ""
msgid "APIFuzzing|Use this tool to generate API fuzzing configuration YAML to copy into your .gitlab-ci.yml file. This tool does not reflect or update your .gitlab-ci.yml file automatically."
msgstr ""
msgid "APIFuzzing|Username for basic authentication"
msgstr ""
msgid "APIFuzzing|We recommend that you review the JSON specifications file before adding it to a repository."
msgstr ""
msgid "APIFuzzing|You may need a maintainer's help to secure your credentials."
msgstr ""
msgid "AWS Access Key"
msgstr ""
......@@ -4138,6 +4222,9 @@ msgstr ""
msgid "Authenticating"
msgstr ""
msgid "Authentication"
msgstr ""
msgid "Authentication Failure"
msgstr ""
......@@ -11850,6 +11937,9 @@ msgstr ""
msgid "Evidence collection"
msgstr ""
msgid "Ex: Example.com"
msgstr ""
msgid "Exactly one of %{attributes} is required"
msgstr ""
......@@ -14507,6 +14597,12 @@ msgstr ""
msgid "Guideline"
msgstr ""
msgid "HAR (HTTP Archive)"
msgstr ""
msgid "HAR file path"
msgstr ""
msgid "HTTP Basic: Access denied\\nYou must use a personal access token with 'api' scope for Git over HTTP.\\nYou can generate one at %{profile_personal_access_tokens_url}"
msgstr ""
......@@ -20664,6 +20760,12 @@ msgstr ""
msgid "Open"
msgstr ""
msgid "Open API"
msgstr ""
msgid "Open API specification file path"
msgstr ""
msgid "Open Selection"
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