Commit 445ff2d0 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab master

parents 8ce1730f 7a57eef4
import Vue from 'vue'; import Vue from 'vue';
import { GlToast } from '@gitlab/ui';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import { parseBoolean } from '~/lib/utils/common_utils'; import { parseBoolean } from '~/lib/utils/common_utils';
import SettingsApp from './components/group_settings_app.vue'; import SettingsApp from './components/group_settings_app.vue';
import { apolloProvider } from './graphql'; import { apolloProvider } from './graphql';
Vue.use(Translate); Vue.use(Translate);
Vue.use(GlToast);
export default () => { export default () => {
const el = document.getElementById('js-packages-and-registries-settings'); const el = document.getElementById('js-packages-and-registries-settings');
......
<script> <script>
import { GlSprintf, GlLink } from '@gitlab/ui'; import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import createFlash from '~/flash';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue'; import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
...@@ -26,6 +25,7 @@ export default { ...@@ -26,6 +25,7 @@ export default {
PACKAGES_DOCS_PATH, PACKAGES_DOCS_PATH,
}, },
components: { components: {
GlAlert,
GlSprintf, GlSprintf,
GlLink, GlLink,
SettingsBlock, SettingsBlock,
...@@ -49,6 +49,7 @@ export default { ...@@ -49,6 +49,7 @@ export default {
return { return {
packageSettings: {}, packageSettings: {},
errors: {}, errors: {},
alertMessage: null,
}; };
}, },
computed: { computed: {
...@@ -57,6 +58,9 @@ export default { ...@@ -57,6 +58,9 @@ export default {
}, },
}, },
methods: { methods: {
dismissAlert() {
this.alertMessage = null;
},
updateSettings(payload) { updateSettings(payload) {
this.errors = {}; this.errors = {};
return this.$apollo return this.$apollo
...@@ -76,9 +80,10 @@ export default { ...@@ -76,9 +80,10 @@ export default {
}) })
.then(({ data }) => { .then(({ data }) => {
if (data.updateNamespacePackageSettings?.errors?.length > 0) { if (data.updateNamespacePackageSettings?.errors?.length > 0) {
createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' }); this.alertMessage = ERROR_UPDATING_SETTINGS;
} else { } else {
createFlash({ message: SUCCESS_UPDATING_SETTINGS, type: 'success' }); this.dismissAlert();
this.$toast.show(SUCCESS_UPDATING_SETTINGS, { type: 'success' });
} }
}) })
.catch((e) => { .catch((e) => {
...@@ -93,7 +98,7 @@ export default { ...@@ -93,7 +98,7 @@ export default {
this.errors = { ...this.errors, [key]: message }; this.errors = { ...this.errors, [key]: message };
}); });
} }
createFlash({ message: ERROR_UPDATING_SETTINGS, type: 'warning' }); this.alertMessage = ERROR_UPDATING_SETTINGS;
}); });
}, },
}, },
...@@ -102,6 +107,10 @@ export default { ...@@ -102,6 +107,10 @@ export default {
<template> <template>
<div> <div>
<gl-alert v-if="alertMessage" variant="warning" class="gl-mt-4" @dismiss="dismissAlert">
{{ alertMessage }}
</gl-alert>
<settings-block :default-expanded="defaultExpanded"> <settings-block :default-expanded="defaultExpanded">
<template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template> <template #title> {{ $options.i18n.PACKAGE_SETTINGS_HEADER }}</template>
<template #description> <template #description>
......
---
title: Track 5 mins production app template usage
merge_request: 53618
author:
type: other
...@@ -72,7 +72,7 @@ If you have project maintainer access you have the option to set up Service Desk ...@@ -72,7 +72,7 @@ If you have project maintainer access you have the option to set up Service Desk
WARNING: WARNING:
This email address can be used by anyone to create an issue on this project, regardless This email address can be used by anyone to create an issue on this project, regardless
of their access level to your GitLab instance. We recommend **putting this behind an alias** so it can be of their access level to your GitLab instance. We recommend **putting this behind an alias on your email system** so it can be
changed if needed. We also recommend **[enabling Akismet](../../integration/akismet.md)** on your GitLab changed if needed. We also recommend **[enabling Akismet](../../integration/akismet.md)** on your GitLab
instance to add spam checking to this service. Unblocked email spam would result in many spam instance to add spam checking to this service. Unblocked email spam would result in many spam
issues being created. issues being created.
......
...@@ -5,6 +5,8 @@ require 'spec_helper' ...@@ -5,6 +5,8 @@ require 'spec_helper'
require_migration! require_migration!
RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do
include MigrationHelpers::VulnerabilitiesFindingsHelper
let(:migration) { 'RemoveDuplicatedCsFindingsWithoutVulnerabilityId'} let(:migration) { 'RemoveDuplicatedCsFindingsWithoutVulnerabilityId'}
let(:namespaces) { table(:namespaces) } let(:namespaces) { table(:namespaces) }
let(:notes) { table(:notes) } let(:notes) { table(:notes) }
...@@ -99,21 +101,16 @@ RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do ...@@ -99,21 +101,16 @@ RSpec.describe RemoveDuplicatedCsFindingsWithoutVulnerabilityId, :migration do
end end
def finding_params(primary_identifier_id, project_id) def finding_params(primary_identifier_id, project_id)
attrs = attributes_for(:vulnerabilities_finding) # rubocop: disable RSpec/FactoriesInMigrationSpecs custom_attributes = {
{
severity: 0, severity: 0,
confidence: 5, confidence: 5,
report_type: 2, report_type: 2,
project_id: project_id, project_id: project_id,
scanner_id: 6, scanner_id: 6,
primary_identifier_id: primary_identifier_id, primary_identifier_id: primary_identifier_id
project_fingerprint: attrs[:project_fingerprint],
location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)),
uuid: SecureRandom.uuid,
name: attrs[:name],
metadata_version: '1.3',
raw_metadata: attrs[:raw_metadata]
} }
attributes_for_vulnerabilities_finding.merge(custom_attributes)
end end
def create_identifier(number_of) def create_identifier(number_of)
......
...@@ -5,6 +5,7 @@ module Gitlab::UsageDataCounters ...@@ -5,6 +5,7 @@ module Gitlab::UsageDataCounters
REDIS_SLOT = 'ci_templates'.freeze REDIS_SLOT = 'ci_templates'.freeze
TEMPLATE_TO_EVENT = { TEMPLATE_TO_EVENT = {
'5-Minute-Production-App.gitlab-ci.yml' => '5_min_production_app',
'Auto-DevOps.gitlab-ci.yml' => 'auto_devops', 'Auto-DevOps.gitlab-ci.yml' => 'auto_devops',
'AWS/CF-Provision-and-Deploy-EC2.gitlab-ci.yml' => 'aws_cf_deploy_ec2', 'AWS/CF-Provision-and-Deploy-EC2.gitlab-ci.yml' => 'aws_cf_deploy_ec2',
'AWS/Deploy-ECS.gitlab-ci.yml' => 'aws_deploy_ecs', 'AWS/Deploy-ECS.gitlab-ci.yml' => 'aws_deploy_ecs',
......
...@@ -588,6 +588,11 @@ ...@@ -588,6 +588,11 @@
aggregation: weekly aggregation: weekly
feature_flag: usage_data_p_terraform_state_api_unique_users feature_flag: usage_data_p_terraform_state_api_unique_users
# CI templates # CI templates
- name: p_ci_templates_5_min_production_app
category: ci_templates
redis_slot: ci_templates
aggregation: weekly
feature_flag: usage_data_track_ci_templates_unique_projects
- name: p_ci_templates_auto_devops - name: p_ci_templates_auto_devops
category: ci_templates category: ci_templates
redis_slot: ci_templates redis_slot: ci_templates
......
...@@ -92,12 +92,12 @@ then ...@@ -92,12 +92,12 @@ then
echo "Merge request pipeline (detached) detected. Testing all files." echo "Merge request pipeline (detached) detected. Testing all files."
else else
MERGE_BASE=$(git merge-base ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA} ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}) MERGE_BASE=$(git merge-base ${CI_MERGE_REQUEST_TARGET_BRANCH_SHA} ${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA})
if git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" | grep -E "\.vale|\.markdownlint|lint-doc\.sh" if git diff --diff-filter=d --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" | grep -E "\.vale|\.markdownlint|lint-doc\.sh"
then then
MD_DOC_PATH=${MD_DOC_PATH:-doc} MD_DOC_PATH=${MD_DOC_PATH:-doc}
echo "Vale, Markdownlint, or lint-doc.sh configuration changed. Testing all files." echo "Vale, Markdownlint, or lint-doc.sh configuration changed. Testing all files."
else else
MD_DOC_PATH=$(git diff --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" 'doc/*.md') MD_DOC_PATH=$(git diff --diff-filter=d --name-only "${MERGE_BASE}..${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" -- 'doc/*.md')
if [ -n "${MD_DOC_PATH}" ] if [ -n "${MD_DOC_PATH}" ]
then then
echo -e "Merged results pipeline detected. Testing only the following files:\n${MD_DOC_PATH}" echo -e "Merged results pipeline detected. Testing only the following files:\n${MD_DOC_PATH}"
......
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { GlSprintf, GlLink } from '@gitlab/ui'; import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
import VueApollo from 'vue-apollo'; import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper'; import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import createFlash from '~/flash';
import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue';
import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue'; import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue';
import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue'; import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
...@@ -30,6 +29,7 @@ const localVue = createLocalVue(); ...@@ -30,6 +29,7 @@ const localVue = createLocalVue();
describe('Group Settings App', () => { describe('Group Settings App', () => {
let wrapper; let wrapper;
let apolloProvider; let apolloProvider;
let show;
const defaultProvide = { const defaultProvide = {
defaultExpanded: false, defaultExpanded: false,
...@@ -40,6 +40,7 @@ describe('Group Settings App', () => { ...@@ -40,6 +40,7 @@ describe('Group Settings App', () => {
provide = defaultProvide, provide = defaultProvide,
resolver = jest.fn().mockResolvedValue(groupPackageSettingsMock), resolver = jest.fn().mockResolvedValue(groupPackageSettingsMock),
mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()), mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()),
data = {},
} = {}) => { } = {}) => {
localVue.use(VueApollo); localVue.use(VueApollo);
...@@ -54,13 +55,27 @@ describe('Group Settings App', () => { ...@@ -54,13 +55,27 @@ describe('Group Settings App', () => {
localVue, localVue,
apolloProvider, apolloProvider,
provide, provide,
data() {
return {
...data,
};
},
stubs: { stubs: {
GlSprintf, GlSprintf,
SettingsBlock, SettingsBlock,
}, },
mocks: {
$toast: {
show,
},
},
}); });
}; };
beforeEach(() => {
show = jest.fn();
});
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
wrapper = null; wrapper = null;
...@@ -70,6 +85,7 @@ describe('Group Settings App', () => { ...@@ -70,6 +85,7 @@ describe('Group Settings App', () => {
const findDescription = () => wrapper.find('[data-testid="description"'); const findDescription = () => wrapper.find('[data-testid="description"');
const findLink = () => wrapper.find(GlLink); const findLink = () => wrapper.find(GlLink);
const findMavenSettings = () => wrapper.find(MavenSettings); const findMavenSettings = () => wrapper.find(MavenSettings);
const findAlert = () => wrapper.find(GlAlert);
const waitForApolloQueryAndRender = async () => { const waitForApolloQueryAndRender = async () => {
await waitForPromises(); await waitForPromises();
...@@ -178,8 +194,7 @@ describe('Group Settings App', () => { ...@@ -178,8 +194,7 @@ describe('Group Settings App', () => {
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ expect(show).toHaveBeenCalledWith(SUCCESS_UPDATING_SETTINGS, {
message: SUCCESS_UPDATING_SETTINGS,
type: 'success', type: 'success',
}); });
}); });
...@@ -211,6 +226,12 @@ describe('Group Settings App', () => { ...@@ -211,6 +226,12 @@ describe('Group Settings App', () => {
}); });
describe('errors', () => { describe('errors', () => {
const verifyAlert = () => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toBe(ERROR_UPDATING_SETTINGS);
expect(findAlert().props('variant')).toBe('warning');
};
it('mutation payload with root level errors', async () => { it('mutation payload with root level errors', async () => {
// note this is a complex test that covers all the path around errors that are shown in the form // note this is a complex test that covers all the path around errors that are shown in the form
// it's one single it case, due to the expensive preparation and execution // it's one single it case, due to the expensive preparation and execution
...@@ -229,10 +250,8 @@ describe('Group Settings App', () => { ...@@ -229,10 +250,8 @@ describe('Group Settings App', () => {
); );
// general error message is shown // general error message is shown
expect(createFlash).toHaveBeenCalledWith({
message: ERROR_UPDATING_SETTINGS, verifyAlert();
type: 'warning',
});
emitSettingsUpdate(); emitSettingsUpdate();
...@@ -242,11 +261,11 @@ describe('Group Settings App', () => { ...@@ -242,11 +261,11 @@ describe('Group Settings App', () => {
expect(findMavenSettings().props('mavenDuplicateExceptionRegexError')).toBe(''); expect(findMavenSettings().props('mavenDuplicateExceptionRegexError')).toBe('');
}); });
it('mutation payload with local errors', async () => { it.each`
const mutationResolver = jest type | mutationResolver
.fn() ${'local'} | ${jest.fn().mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }))}
.mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] })); ${'network'} | ${jest.fn().mockRejectedValue()}
`('mutation payload with $type error', async ({ mutationResolver }) => {
mountComponent({ mutationResolver }); mountComponent({ mutationResolver });
await waitForApolloQueryAndRender(); await waitForApolloQueryAndRender();
...@@ -255,26 +274,35 @@ describe('Group Settings App', () => { ...@@ -255,26 +274,35 @@ describe('Group Settings App', () => {
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ verifyAlert();
message: ERROR_UPDATING_SETTINGS,
type: 'warning',
});
}); });
it('shows an error in case of network error', async () => { it('a successful request dismisses the alert', async () => {
const mutationResolver = jest.fn().mockRejectedValue(); mountComponent({ data: { alertMessage: 'foo' } });
mountComponent({ mutationResolver });
await waitForApolloQueryAndRender(); await waitForApolloQueryAndRender();
expect(findAlert().exists()).toBe(true);
emitSettingsUpdate(); emitSettingsUpdate();
await waitForPromises(); await waitForPromises();
expect(createFlash).toHaveBeenCalledWith({ expect(findAlert().exists()).toBe(false);
message: ERROR_UPDATING_SETTINGS, });
type: 'warning',
}); it('dismiss event from alert dismiss it from the page', async () => {
mountComponent({ data: { alertMessage: 'foo' } });
await waitForApolloQueryAndRender();
expect(findAlert().exists()).toBe(true);
findAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
}); });
}); });
}); });
......
# frozen_string_literal: true
module MigrationHelpers
module VulnerabilitiesFindingsHelper
def attributes_for_vulnerabilities_finding
uuid = SecureRandom.uuid
{
project_fingerprint: SecureRandom.hex(20),
location_fingerprint: Digest::SHA1.hexdigest(SecureRandom.hex(10)),
uuid: uuid,
name: "Vulnerability Finding #{uuid}",
metadata_version: '1.3',
raw_metadata: raw_metadata
}
end
def raw_metadata
{
"description" => "The cipher does not provide data integrity update 1",
"message" => "The cipher does not provide data integrity",
"cve" => "818bf5dacb291e15d9e6dc3c5ac32178:CIPHER",
"solution" => "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
"location" => {
"file" => "maven/src/main/java/com/gitlab/security_products/tests/App.java",
"start_line" => 29,
"end_line" => 29,
"class" => "com.gitlab.security_products.tests.App",
"method" => "insecureCypher"
},
"links" => [
{
"name" => "Cipher does not check for integrity first?",
"url" => "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
],
"assets" => [
{
"type" => "postman",
"name" => "Test Postman Collection",
"url" => "http://localhost/test.collection"
}
],
"evidence" => {
"summary" => "Credit card detected",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => nil,
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
},
"response" => {
"reason_phrase" => "OK",
"status_code" => 200,
"body" => nil,
"headers" => [
{
"name" => "Content-Length",
"value" => "0"
}
]
},
"source" => {
"id" => "assert:Response Body Analysis",
"name" => "Response Body Analysis",
"url" => "htpp://hostname/documentation"
},
"supporting_messages" => [
{
"name" => "Origional",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
}
},
{
"name" => "Recorded",
"request" => {
"method" => "GET",
"url" => "http://goat:8080/WebGoat/logout",
"body" => "",
"headers" => [
{
"name" => "Accept",
"value" => "*/*"
}
]
},
"response" => {
"reason_phrase" => "OK",
"status_code" => 200,
"body" => "",
"headers" => [
{
"name" => "Content-Length",
"value" => "0"
}
]
}
}
]
}
}
end
end
end
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