Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new Git repository type.md2
-rw-r--r--.gitlab/issue_templates/Geo Replicate a new blob type.md2
-rw-r--r--app/assets/javascripts/profile/profile.js11
-rw-r--r--app/assets/javascripts/related_issues/components/add_issuable_form.vue6
-rw-r--r--app/assets/stylesheets/pages/profiles/preferences.scss4
-rw-r--r--app/views/profiles/show.html.haml4
-rw-r--r--config/feature_flags/development/ci_minutes_cost_factor_for_all_public_projects.yml8
-rw-r--r--db/migrate/20220610140605_change_public_projects_cost_factor.rb35
-rw-r--r--db/migrate/20220617123135_drop_temp_index_on_projects_on_id_and_runners_token.rb21
-rw-r--r--db/migrate/20220617123144_drop_temp_index_on_projects_on_id_and_runners_token_encrypted.rb21
-rw-r--r--db/post_migrate/20220617123022_add_unique_index_on_projects_on_runners_token.rb18
-rw-r--r--db/post_migrate/20220617123034_add_unique_index_on_projects_on_runners_token_encrypted.rb18
-rw-r--r--db/post_migrate/20220617123105_drop_index_on_projects_on_runners_token.rb17
-rw-r--r--db/post_migrate/20220617123113_drop_index_on_projects_on_runners_token_encrypted.rb17
-rw-r--r--db/schema_migrations/202206101406051
-rw-r--r--db/schema_migrations/202206171230221
-rw-r--r--db/schema_migrations/202206171230341
-rw-r--r--db/schema_migrations/202206171231051
-rw-r--r--db/schema_migrations/202206171231131
-rw-r--r--db/schema_migrations/202206171231351
-rw-r--r--db/schema_migrations/202206171231441
-rw-r--r--db/structure.sql12
-rw-r--r--doc/administration/gitaly/configure_gitaly.md8
-rw-r--r--doc/administration/gitaly/monitoring.md4
-rw-r--r--doc/ci/pipelines/cicd_minutes.md6
-rw-r--r--doc/development/testing_guide/contract/consumer_tests.md40
-rw-r--r--doc/development/testing_guide/contract/index.md39
-rw-r--r--doc/development/testing_guide/contract/provider_tests.md69
-rw-r--r--doc/update/index.md16
-rw-r--r--doc/user/application_security/dast/checks/16.10.md30
-rw-r--r--doc/user/application_security/dast/checks/16.8.md30
-rw-r--r--doc/user/application_security/dast/checks/16.9.md32
-rw-r--r--doc/user/application_security/dast/checks/index.md3
-rw-r--r--qa/lib/gitlab/page/admin/subscription.rb7
-rw-r--r--qa/qa/resource/user.rb14
-rw-r--r--spec/migrations/change_public_projects_cost_factor_spec.rb68
36 files changed, 489 insertions, 80 deletions
diff --git a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
index 34e6e70015b..a00893dd867 100644
--- a/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new Git repository type.md
@@ -128,7 +128,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
```ruby
# frozen_string_literal: true
- class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
+ class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"
diff --git a/.gitlab/issue_templates/Geo Replicate a new blob type.md b/.gitlab/issue_templates/Geo Replicate a new blob type.md
index e6f96c575d2..35978c87418 100644
--- a/.gitlab/issue_templates/Geo Replicate a new blob type.md
+++ b/.gitlab/issue_templates/Geo Replicate a new blob type.md
@@ -130,7 +130,7 @@ The Geo primary site needs to checksum every replicable so secondaries can verif
```ruby
# frozen_string_literal: true
- class CreateCoolWidgetStates < Gitlab::Database::Migration[1.0]
+ class CreateCoolWidgetStates < Gitlab::Database::Migration[2.0]
VERIFICATION_STATE_INDEX_NAME = "index_cool_widget_states_on_verification_state"
PENDING_VERIFICATION_INDEX_NAME = "index_cool_widget_states_pending_verification"
FAILED_VERIFICATION_INDEX_NAME = "index_cool_widget_states_failed_verification"
diff --git a/app/assets/javascripts/profile/profile.js b/app/assets/javascripts/profile/profile.js
index 25fefff219c..064bcf8e4c4 100644
--- a/app/assets/javascripts/profile/profile.js
+++ b/app/assets/javascripts/profile/profile.js
@@ -1,5 +1,5 @@
import $ from 'jquery';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { VARIANT_DANGER, VARIANT_INFO, createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { parseBoolean } from '~/lib/utils/common_utils';
import { Rails } from '~/lib/utils/rails_ujs';
@@ -10,7 +10,7 @@ import TimezoneDropdown, {
export default class Profile {
constructor({ form } = {}) {
this.onSubmitForm = this.onSubmitForm.bind(this);
- this.form = form || $('.edit-user');
+ this.form = form || $('.js-edit-user');
this.setRepoRadio();
this.bindEvents();
this.initAvatarGlCrop();
@@ -84,9 +84,9 @@ export default class Profile {
this.updateHeaderAvatar();
}
- createFlash({
+ createAlert({
message: data.message,
- type: data.status === 'error' ? FLASH_TYPES.ALERT : FLASH_TYPES.NOTICE,
+ variant: data.status === 'error' ? VARIANT_DANGER : VARIANT_INFO,
});
})
.then(() => {
@@ -95,8 +95,9 @@ export default class Profile {
self.form.find(':input[disabled]').enable();
})
.catch((error) =>
- createFlash({
+ createAlert({
message: error.message,
+ variant: VARIANT_DANGER,
}),
);
}
diff --git a/app/assets/javascripts/related_issues/components/add_issuable_form.vue b/app/assets/javascripts/related_issues/components/add_issuable_form.vue
index 42de419aec4..d765033d00b 100644
--- a/app/assets/javascripts/related_issues/components/add_issuable_form.vue
+++ b/app/assets/javascripts/related_issues/components/add_issuable_form.vue
@@ -173,7 +173,7 @@ export default {
:label="issuableCategoryHeaderText"
label-for="linked-issue-type-radio"
label-class="label-bold"
- class="mb-2"
+ class="gl-mb-3"
>
<gl-form-radio-group
id="linked-issue-type-radio"
@@ -216,12 +216,12 @@ export default {
:disabled="isSubmitButtonDisabled"
:loading="isSubmitting"
type="submit"
- class="float-left"
+ class="gl-float-left"
data-qa-selector="add_issue_button"
>
{{ __('Add') }}
</gl-button>
- <gl-button class="float-right" @click="onFormCancel">
+ <gl-button class="gl-float-right" @click="onFormCancel">
{{ __('Cancel') }}
</gl-button>
</div>
diff --git a/app/assets/stylesheets/pages/profiles/preferences.scss b/app/assets/stylesheets/pages/profiles/preferences.scss
index 518ec181e5e..c7d7aacceec 100644
--- a/app/assets/stylesheets/pages/profiles/preferences.scss
+++ b/app/assets/stylesheets/pages/profiles/preferences.scss
@@ -1,6 +1,6 @@
.application-theme {
- $ui-gray-bg: #2e2e2e;
- $ui-light-gray-bg: #dfdfdf;
+ $ui-gray-bg: #303030;
+ $ui-light-gray-bg: #f0f0f0;
$ui-dark-mode-bg: #1f1f1f;
.preview {
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d1f1ff892d5..9c467bf5485 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -5,9 +5,7 @@
- availability = availability_values
- custom_emoji = show_status_emoji?(@user.status)
-= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
- = form_errors(@user)
-
+= gitlab_ui_form_for @user, url: profile_path, method: :put, html: { multipart: true, class: 'edit-user js-edit-user gl-mt-3 js-quick-submit gl-show-field-errors js-password-prompt-form', remote: true }, authenticity_token: true do |f|
.row.js-search-settings-section
.col-lg-4.profile-settings-sidebar
%h4.gl-mt-0
diff --git a/config/feature_flags/development/ci_minutes_cost_factor_for_all_public_projects.yml b/config/feature_flags/development/ci_minutes_cost_factor_for_all_public_projects.yml
deleted file mode 100644
index 24932e0cfe4..00000000000
--- a/config/feature_flags/development/ci_minutes_cost_factor_for_all_public_projects.yml
+++ /dev/null
@@ -1,8 +0,0 @@
----
-name: ci_minutes_cost_factor_for_all_public_projects
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/85357
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/359094
-milestone: '15.0'
-type: development
-group: group::pipeline execution
-default_enabled: false
diff --git a/db/migrate/20220610140605_change_public_projects_cost_factor.rb b/db/migrate/20220610140605_change_public_projects_cost_factor.rb
new file mode 100644
index 00000000000..cf0c275828c
--- /dev/null
+++ b/db/migrate/20220610140605_change_public_projects_cost_factor.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class ChangePublicProjectsCostFactor < Gitlab::Database::Migration[2.0]
+ # This migration updates SaaS Runner cost factors for public projects.
+ # Previously we had a disabled cost factor for public projects, meaning
+ # that no CI minutes were counted by default. With a low cost factor
+ # we count CI minutes consumption at a very low rate to prevent
+ # abuses.
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_ci
+
+ DISABLED_COST_FACTOR = 0
+ LOW_COST_FACTOR = 0.008
+
+ class Runner < MigrationRecord
+ self.table_name = 'ci_runners'
+
+ scope :shared, -> { where(runner_type: 1) }
+ end
+
+ def up
+ return unless Gitlab.com?
+
+ Runner.shared.where(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
+ .update_all(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
+ end
+
+ def down
+ return unless Gitlab.com?
+
+ Runner.shared.where(public_projects_minutes_cost_factor: LOW_COST_FACTOR)
+ .update_all(public_projects_minutes_cost_factor: DISABLED_COST_FACTOR)
+ end
+end
diff --git a/db/migrate/20220617123135_drop_temp_index_on_projects_on_id_and_runners_token.rb b/db/migrate/20220617123135_drop_temp_index_on_projects_on_id_and_runners_token.rb
new file mode 100644
index 00000000000..c860a2208fe
--- /dev/null
+++ b/db/migrate/20220617123135_drop_temp_index_on_projects_on_id_and_runners_token.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropTempIndexOnProjectsOnIdAndRunnersToken < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token'
+
+ def up
+ finalize_background_migration 'ResetDuplicateCiRunnersTokenValuesOnProjects'
+
+ remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :projects,
+ [:id, :runners_token],
+ where: "runners_token IS NOT NULL",
+ unique: false,
+ name: TEMP_INDEX_NAME
+ end
+end
diff --git a/db/migrate/20220617123144_drop_temp_index_on_projects_on_id_and_runners_token_encrypted.rb b/db/migrate/20220617123144_drop_temp_index_on_projects_on_id_and_runners_token_encrypted.rb
new file mode 100644
index 00000000000..254a73a9b1f
--- /dev/null
+++ b/db/migrate/20220617123144_drop_temp_index_on_projects_on_id_and_runners_token_encrypted.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class DropTempIndexOnProjectsOnIdAndRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ TEMP_INDEX_NAME = 'tmp_index_projects_on_id_and_runners_token_encrypted'
+
+ def up
+ finalize_background_migration 'ResetDuplicateCiRunnersTokenEncryptedValuesOnProjects'
+
+ remove_concurrent_index_by_name :projects, TEMP_INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :projects,
+ [:id, :runners_token_encrypted],
+ where: "runners_token_encrypted IS NOT NULL",
+ unique: false,
+ name: TEMP_INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220617123022_add_unique_index_on_projects_on_runners_token.rb b/db/post_migrate/20220617123022_add_unique_index_on_projects_on_runners_token.rb
new file mode 100644
index 00000000000..1e0409b16ea
--- /dev/null
+++ b/db/post_migrate/20220617123022_add_unique_index_on_projects_on_runners_token.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddUniqueIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_uniq_projects_on_runners_token'
+
+ def up
+ add_concurrent_index :projects,
+ :runners_token,
+ name: INDEX_NAME,
+ unique: true
+ end
+
+ def down
+ remove_concurrent_index_by_name :projects, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220617123034_add_unique_index_on_projects_on_runners_token_encrypted.rb b/db/post_migrate/20220617123034_add_unique_index_on_projects_on_runners_token_encrypted.rb
new file mode 100644
index 00000000000..b9ba570606e
--- /dev/null
+++ b/db/post_migrate/20220617123034_add_unique_index_on_projects_on_runners_token_encrypted.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddUniqueIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_uniq_projects_on_runners_token_encrypted'
+
+ def up
+ add_concurrent_index :projects,
+ :runners_token_encrypted,
+ name: INDEX_NAME,
+ unique: true
+ end
+
+ def down
+ remove_concurrent_index_by_name :projects, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220617123105_drop_index_on_projects_on_runners_token.rb b/db/post_migrate/20220617123105_drop_index_on_projects_on_runners_token.rb
new file mode 100644
index 00000000000..6b76a92a9f4
--- /dev/null
+++ b/db/post_migrate/20220617123105_drop_index_on_projects_on_runners_token.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class DropIndexOnProjectsOnRunnersToken < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_projects_on_runners_token'
+
+ def up
+ remove_concurrent_index_by_name :projects, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :projects,
+ :runners_token,
+ name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20220617123113_drop_index_on_projects_on_runners_token_encrypted.rb b/db/post_migrate/20220617123113_drop_index_on_projects_on_runners_token_encrypted.rb
new file mode 100644
index 00000000000..213f55bfcc4
--- /dev/null
+++ b/db/post_migrate/20220617123113_drop_index_on_projects_on_runners_token_encrypted.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class DropIndexOnProjectsOnRunnersTokenEncrypted < Gitlab::Database::Migration[2.0]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_projects_on_runners_token_encrypted'
+
+ def up
+ remove_concurrent_index_by_name :projects, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :projects,
+ :runners_token_encrypted,
+ name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20220610140605 b/db/schema_migrations/20220610140605
new file mode 100644
index 00000000000..c2ae3528d21
--- /dev/null
+++ b/db/schema_migrations/20220610140605
@@ -0,0 +1 @@
+9c64f9fb286992b6cdad8f7f22084c2d538bccf97e9c649f47284c5828a850e3 \ No newline at end of file
diff --git a/db/schema_migrations/20220617123022 b/db/schema_migrations/20220617123022
new file mode 100644
index 00000000000..043b16b6cb6
--- /dev/null
+++ b/db/schema_migrations/20220617123022
@@ -0,0 +1 @@
+247c6cba3cee4413a17193aeebd77eae79a7ced17a5a2b785f0ecd682e823c02 \ No newline at end of file
diff --git a/db/schema_migrations/20220617123034 b/db/schema_migrations/20220617123034
new file mode 100644
index 00000000000..f0ecdc68113
--- /dev/null
+++ b/db/schema_migrations/20220617123034
@@ -0,0 +1 @@
+d72ffd09437a576edb2d046963e8d004c5a2b13586f7318361fea6d673f5cece \ No newline at end of file
diff --git a/db/schema_migrations/20220617123105 b/db/schema_migrations/20220617123105
new file mode 100644
index 00000000000..a3ffe22bca4
--- /dev/null
+++ b/db/schema_migrations/20220617123105
@@ -0,0 +1 @@
+d382bfcfcf79ba38a388ac5496a194adc0c392ba6685c024d7bd55a14d57c1b8 \ No newline at end of file
diff --git a/db/schema_migrations/20220617123113 b/db/schema_migrations/20220617123113
new file mode 100644
index 00000000000..deb71083ab0
--- /dev/null
+++ b/db/schema_migrations/20220617123113
@@ -0,0 +1 @@
+ebe1b2db48b987720e7c561b30ce41e7542d8cd190e4b454bd28d6fdfa8bff0d \ No newline at end of file
diff --git a/db/schema_migrations/20220617123135 b/db/schema_migrations/20220617123135
new file mode 100644
index 00000000000..3cafd00560c
--- /dev/null
+++ b/db/schema_migrations/20220617123135
@@ -0,0 +1 @@
+2fcb9e7ecdc387d5dd4dfe78544289026bb2626eca2650da590f6181deeaadef \ No newline at end of file
diff --git a/db/schema_migrations/20220617123144 b/db/schema_migrations/20220617123144
new file mode 100644
index 00000000000..6f9c5e33092
--- /dev/null
+++ b/db/schema_migrations/20220617123144
@@ -0,0 +1 @@
+5a03ed4bc5791d0feb72203553f77ed37f37127309eda6c7dc75c7ac950e28e3 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 4b17fa31b59..a4aeae58dc5 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -29124,10 +29124,6 @@ CREATE UNIQUE INDEX index_projects_on_project_namespace_id ON projects USING btr
CREATE INDEX index_projects_on_repository_storage ON projects USING btree (repository_storage);
-CREATE INDEX index_projects_on_runners_token ON projects USING btree (runners_token);
-
-CREATE INDEX index_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
-
CREATE INDEX index_projects_on_star_count ON projects USING btree (star_count);
CREATE INDEX index_projects_on_updated_at_and_id ON projects USING btree (updated_at, id);
@@ -29594,6 +29590,10 @@ CREATE INDEX index_u2f_registrations_on_user_id ON u2f_registrations USING btree
CREATE UNIQUE INDEX index_uniq_im_issuable_escalation_statuses_on_issue_id ON incident_management_issuable_escalation_statuses USING btree (issue_id);
+CREATE UNIQUE INDEX index_uniq_projects_on_runners_token ON projects USING btree (runners_token);
+
+CREATE UNIQUE INDEX index_uniq_projects_on_runners_token_encrypted ON projects USING btree (runners_token_encrypted);
+
CREATE UNIQUE INDEX index_unique_ci_runner_projects_on_runner_id_and_project_id ON ci_runner_projects USING btree (runner_id, project_id);
CREATE UNIQUE INDEX index_unique_issue_metrics_issue_id ON issue_metrics USING btree (issue_id);
@@ -30008,10 +30008,6 @@ CREATE UNIQUE INDEX tmp_index_on_tmp_project_id_on_namespaces ON namespaces USIN
CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING btree (id) WHERE (state <> 2);
-CREATE INDEX tmp_index_projects_on_id_and_runners_token ON projects USING btree (id, runners_token) WHERE (runners_token IS NOT NULL);
-
-CREATE INDEX tmp_index_projects_on_id_and_runners_token_encrypted ON projects USING btree (id, runners_token_encrypted) WHERE (runners_token_encrypted IS NOT NULL);
-
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_components_on_distribution_id_and_name ON packages_debian_group_components USING btree (distribution_id, name);
diff --git a/doc/administration/gitaly/configure_gitaly.md b/doc/administration/gitaly/configure_gitaly.md
index 26aa750900b..426d07b154d 100644
--- a/doc/administration/gitaly/configure_gitaly.md
+++ b/doc/administration/gitaly/configure_gitaly.md
@@ -825,7 +825,9 @@ information, see the [relevant documentation](monitoring.md#monitor-gitaly-concu
## Control groups
-> Introduced in GitLab 13.10.
+FLAG:
+On self-managed GitLab, by default cgroups are not available. To make it available, ask an administrator to
+[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
Gitaly shells out to Git for many of its operations. Git can consume a lot of resources for certain operations,
especially for large repositories.
@@ -862,10 +864,6 @@ Two ways of configuring cgroups are available.
> This method of configuring cgroups introduced in GitLab 15.1.
-FLAG:
-On self-managed GitLab, by default this method of configuring cgroups is not available. To make it available, ask an administrator to
-[enable the feature flag](../feature_flags.md) named `gitaly_run_cmds_in_cgroup`.
-
Gitaly creates a pool of cgroups that are isolated based on the repository used in the Git command to be placed under one of these cgroups.
To configure cgroups in Gitaly, add `gitaly['cgroups']` to `/etc/gitlab/gitlab.rb`.
diff --git a/doc/administration/gitaly/monitoring.md b/doc/administration/gitaly/monitoring.md
index a0a2e43569c..ac0c4cf139d 100644
--- a/doc/administration/gitaly/monitoring.md
+++ b/doc/administration/gitaly/monitoring.md
@@ -48,8 +48,8 @@ the Gitaly logs and Prometheus:
You can observe the status of [control groups (cgroups)](configure_gitaly.md#control-groups) using Prometheus:
-- `gitaly_cgroups_memory_failed_total`, a gauge for the total number of times
- the memory limit has been hit. This number resets each time a server is
+- `gitaly_cgroups_reclaim_attempts_total`, a gauge for the total number of times
+ there has been a memory relcaim attempt. This number resets each time a server is
restarted.
- `gitaly_cgroups_cpu_usage`, a gauge that measures CPU usage per cgroup.
- `gitaly_cgroup_procs_total`, a gauge that measures the total number of
diff --git a/doc/ci/pipelines/cicd_minutes.md b/doc/ci/pipelines/cicd_minutes.md
index ff30d5701cc..96568886e7e 100644
--- a/doc/ci/pipelines/cicd_minutes.md
+++ b/doc/ci/pipelines/cicd_minutes.md
@@ -195,12 +195,12 @@ the duration of the pipeline if many jobs ran at the same time.
The cost factor for a job running on a shared runner is:
-- `0.008` for public projects on GitLab SaaS, if [created 2021-07-17 or later](https://gitlab.com/gitlab-org/gitlab/-/issues/332708).
+- `0.008` for public projects on GitLab SaaS.
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
- `0.008` for projects members of GitLab [Open Source program](../../subscriptions/index.md#gitlab-for-open-source).
(For every 125 minutes of job time, you accrue 1 CI/CD minute.)
-- `0` for public projects on GitLab self-managed instances, and for GitLab SaaS public projects created before 2021-07-17.
-- `1` for internal and private projects.
+- `0` for public projects on GitLab self-managed instances.
+- `1` for internal or private projects.
### Additional costs on GitLab SaaS
diff --git a/doc/development/testing_guide/contract/consumer_tests.md b/doc/development/testing_guide/contract/consumer_tests.md
index b4d6882a655..ad047a9b301 100644
--- a/doc/development/testing_guide/contract/consumer_tests.md
+++ b/doc/development/testing_guide/contract/consumer_tests.md
@@ -10,13 +10,15 @@ This tutorial guides you through writing a consumer test from scratch. To start,
## Create the skeleton
-Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs` called `discussions.spec.js`.
+Start by creating the skeleton of a consumer test. Create a file under `spec/contracts/consumer/specs/project/merge_request` called `discussions.spec.js`.
Then, populate it with the following function and parameters:
- [`pactWith`](#the-pactwith-function)
- [`PactOptions`](#the-pactoptions-parameter)
- [`PactFn`](#the-pactfn-parameter)
+To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
+
### The `pactWith` function
The Pact consumer test is defined through the `pactWith` function that takes `PactOptions` and the `PactFn`.
@@ -36,15 +38,17 @@ const { pactWith } = require('jest-pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
PactFn
);
```
+To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
+
### The `PactFn` parameter
The `PactFn` is where your tests are defined. This is where you set up the mock provider and where you can use the standard Jest methods like [`Jest.describe`](https://jestjs.io/docs/api#describename-fn), [`Jest.beforeEach`](https://jestjs.io/docs/api#beforeeachfn-timeout), and [`Jest.it`](https://jestjs.io/docs/api#testname-fn-timeout). For more information, see [https://jestjs.io/docs/api](https://jestjs.io/docs/api).
@@ -54,14 +58,14 @@ const { pactWith } = require('jest-pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
dir: '../contracts',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
});
@@ -93,14 +97,14 @@ const { Matchers } = require('@pact-foundation/pact');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@@ -144,7 +148,7 @@ Notice how we use `Matchers` in the `body` of the expected response. This allows
After the mock provider is set up, you can write the test. For this test, you make a request and expect a particular response.
-First, set up the client that makes the API request. To do that, either create or find an existing file under `spec/contracts/consumer/endpoints` and add the following API request.
+First, set up the client that makes the API request. To do that, create `spec/contracts/consumer/endpoints/project/merge_requests.js` and add the following API request.
```javascript
const axios = require('axios');
@@ -169,18 +173,18 @@ After that's set up, import it to the test file and call it to make the request.
const { pactWith } = require('jest-pact');
const { Matchers } = require('@pact-foundation/pact');
-const { getDiscussions } = require('../endpoints/merge_requests');
+const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
@@ -230,7 +234,7 @@ There we have it! The consumer test is now set up. You can now try [running this
As you may have noticed, the request and response definitions can get large. This results in the test being difficult to read, with a lot of scrolling to find what you want. You can make the test easier to read by extracting these out to a `fixture`.
-Create a file under `spec/contracts/consumer/fixtures` called `discussions.fixture.js`. You place the `request` and `response` definitions here.
+Create a file under `spec/contracts/consumer/fixtures/project/merge_request` called `discussions.fixture.js` where you will place the `request` and `response` definitions.
```javascript
const { Matchers } = require('@pact-foundation/pact');
@@ -274,18 +278,18 @@ With all of that moved to the `fixture`, you can simplify the test to the follow
const { pactWith } = require('jest-pact');
const { Discussions } = require('../fixtures/discussions.fixture');
-const { getDiscussions } = require('../endpoints/merge_requests');
+const { getDiscussions } = require('../endpoints/project/merge_requests');
pactWith(
{
- consumer: 'Merge Request Page',
+ consumer: 'MergeRequest#show',
provider: 'Merge Request Discussions Endpoint',
log: '../logs/consumer.log',
- dir: '../contracts',
+ dir: '../contracts/project/merge_request/show',
},
(provider) => {
- describe('Discussions Endpoint', () => {
+ describe('Merge Request Discussions Endpoint', () => {
beforeEach(() => {
const interaction = {
state: 'a merge request with discussions exists',
diff --git a/doc/development/testing_guide/contract/index.md b/doc/development/testing_guide/contract/index.md
index 6556bd85624..8e12eea2874 100644
--- a/doc/development/testing_guide/contract/index.md
+++ b/doc/development/testing_guide/contract/index.md
@@ -37,3 +37,42 @@ rake contracts:mr:pact:verify:discussions # Verify provider against the
rake contracts:mr:pact:verify:metadata # Verify provider against the consumer pacts for metadata
rake contracts:mr:test:merge_request[contract_mr] # Run all merge request contract tests
```
+
+## Test suite folder structure and naming conventions
+
+To keep the consumer and provider test suite organized and maintainable, it's important that tests are organized, also that consumers and providers are named consistently. Therefore, it's important to adhere to the following conventions.
+
+### Test suite folder structure
+
+Having an organized and sensible folder structure for the test suite makes it easier to find relevant files when reviewing, debugging, or introducing tests.
+
+#### Consumer tests
+
+The consumer tests are grouped according to the different pages in the application. Each file contains various types of requests found in a page. As such, the consumer test files are named using the Rails standards of how pages are referenced. For example, the project pipelines page would be the `Project::Pipeline#index` page so the equivalent consumer test would be located in `consumer/specs/project/pipelines/index.spec.js`.
+
+When defining the location to output the contract generated by the test, we want to follow the same file structure which would be `contracts/project/pipelines/` for this example. This is the structure in `consumer/endpoints` and `consumer/fixtures` as well.
+
+#### Provider tests
+
+The provider tests are grouped similarly to our controllers. Each of these tests contains various tests for an API endpoint. For example, the API endpoint to get a list of pipelines for a project would be located in `provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb`. The provider states are structured the same way.
+
+### Naming conventions
+
+When writing the consumer and provider tests, there are parts where a name is required for the consumer and provider. Since there are no restrictions imposed by Pact on how these should be named, a naming convention is important to keep it easy for us to figure out which consumer and provider tests are involved during debugging. Pact also uses the consumer and provider names to generate the generated contracts in the `#{consumer_name}-#{provider_name}` format.
+
+#### Consumer naming
+
+As mentioned in the [folder structure section](#consumer-tests), consumer tests are grouped according to the different pages in the application. As such, consumer names should follow the same naming format using the Rails standard. For example, the consumer test for `Project::Pipeline#index` would be `ProjectPipeline#index` as the consumer name. Since Pact uses this name to name the contracts it generates, the colons (`::`) are dropped as colons are not valid characters in file names.
+
+#### Provider naming
+
+These are the API endpoints that provides the data to the consumer so they are simply named according to the API endpoint they pertain to. Be mindful that this name is as descriptive as possible. For example, if we're writing a test for the `GET /groups/:id/projects` endpoint, we don't want to simply name it "Projects endpoint" as there is a `GET /projects` endpoint as well that also fetches a list of projects the user has access to across all of GitLab. An easy way to name them is by checking out our [API documentation](../../../api/api_resources.md) and naming it the same way it is named in there. So the [`GET /groups/:id/projects`](../../../api/groups.md#list-a-groups-projects) would be called `List a group’s projects` and [`GET /projects`](../../../api/projects.md#list-all-projects) would be called `List all projects`. Subsequently, the test files are named `list_a_groups_projects_helper.rb` and `list_all_projects_helper.rb` respectively.
+
+There are some cases where the provider being tested may not be documented so, in those cases, fall back to choosing a name that is as descriptive as possible to ensure it's easy to tell what the provider is for.
+
+#### Conventions summary
+
+| Tests | Folder structure | Naming convention |
+| ----- | ---------------- | ----------------- |
+| Consumer Test | Follows the Rails reference standards. For example, `Project::Pipeline#index` would be `consumer/specs/project/pipelines/index.spec.js` | Follows the Rails naming standard. For example, `Project::Pipeline#index` would be `ProjectPipeline#index` |
+| Provider Test | Grouped like the Rails controllers. For example, [`List project pipelines` API endpoint](../../../api/pipelines.md#list-project-pipelines) would be `provider/pact_helpers/project/pipelines/provider/pact_helpers/project/pipelines/get_list_project_pipelines_helper.rb` | Follows the API documentation naming scheme. For example, [`GET /projects/:id/pipelines`](../../../api/pipelines.md#list-project-pipelines) would be called `List project pipelines`. |
diff --git a/doc/development/testing_guide/contract/provider_tests.md b/doc/development/testing_guide/contract/provider_tests.md
index 0da5bcb4aef..dba951ac651 100644
--- a/doc/development/testing_guide/contract/provider_tests.md
+++ b/doc/development/testing_guide/contract/provider_tests.md
@@ -6,18 +6,20 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Writing provider tests
-This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`.
+This tutorial guides you through writing a provider test from scratch. It is a continuation of the [consumer test tutorial](consumer_tests.md). To start, the provider tests are written using [`pact-ruby`](https://github.com/pact-foundation/pact-ruby). In this tutorial, you write a provider test that addresses the contract generated by `discussions.spec.js`. As Pact is a consumer-driven testing tool, this tutorial assumes that there is an existing consumer test that had already generated a contract for us to work with.
## Create the skeleton
-Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/specs`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
+Provider tests are quite simple. The goal is to set up the test data and then link that with the corresponding contract. Start by creating a file called `discussions_helper.rb` under `spec/contracts/provider/pact_helpers/project/merge_request`. Note that the files are called `helpers` to match how they are called by Pact in the Rake tasks, which are set up at the end of this tutorial.
+
+To learn more about how the contract test directory is structured, see the contract testing [test suite folder structure](index.md#test-suite-folder-structure).
### The `service_provider` block
The `service_provider` block is where the provider test is defined. For this block, put in a description of the service provider. Name it exactly as it is called in the contracts that are derived from the consumer tests.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
@@ -33,12 +35,12 @@ end
The `honours_pact_with` block describes which consumer this provider test is addressing. Similar to the `service_provider` block, name this exactly the same as it's called in the contracts that are derived from the consumer tests.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
- honours_pact_with 'Merge Request Page' do
+ honours_pact_with 'MergeRequest#show' do
end
end
@@ -46,19 +48,21 @@ module Provider
end
```
+To learn more about how to name the consumers and providers, see contract testing [naming conventions](index.md#naming-conventions).
+
## Configure the test app
For the provider tests to verify the contracts, you must hook it up to a test app that makes the actual request and return a response to verify against the contract. To do this, configure the `app` the test uses as `Environment::Test.app`, which is defined in [`spec/contracts/provider/environments/test.rb`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/environments/test.rb).
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
- honours_pact_with 'Merge Request Page' do
+ honours_pact_with 'MergeRequest#show' do
end
end
@@ -71,15 +75,15 @@ end
Now that the test app is configured, all that is left is to define which contract this provider test is verifying. To do this, set the `pact_uri`.
```ruby
-require_relative '../spec_helper'
+require_relative '../../../spec_helper'
module Provider
module DiscussionsHelper
Pact.service_provider 'Merge Request Discussions Endpoint' do
app { Environment::Test.app }
- honours_pact_with 'Merge Request Page' do
- pact_uri '../contracts/merge_request_page-merge_request_discussions_endpoint.json'
+ honours_pact_with 'MergeRequest#show' do
+ pact_uri '../contracts/project/merge_request/show/mergerequest#show-merge_request_discussions_endpoint.json'
end
end
end
@@ -95,8 +99,8 @@ Under the `contracts:mr` namespace, introduce the Rake task to run this new test
```ruby
Pact::VerificationTask.new(:discussions) do |pact|
pact.uri(
- "#{contracts}/contracts/merge_request_page-merge_request_discussions_endpoint.json",
- pact_helper: "#{provider}/specs/discussions_helper.rb"
+ "#{contracts}/contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json",
+ pact_helper: "#{provider}/pact_helpers/project/merge_request/discussions_helper.rb"
)
end
```
@@ -109,7 +113,7 @@ As the last step, create the test data that allows the provider test to return t
You can read more about [provider states](https://docs.pact.io/implementation_guides/ruby/provider_states). We can do global provider states but for this tutorial, the provider state is for one specific `state`.
-To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states`. As a quick aside, make sure to also import this state file in the `discussions_helper.rb` file.
+To create the test data, create `discussions_state.rb` under `spec/contracts/provider/states/project/merge_request`. Be sure to also import this state file in the `discussions_helper.rb` file.
### Default user in `spec/contracts/provider/spec_helper.rb`
@@ -118,10 +122,13 @@ Before you create the test data, note that a default user is created in the [`sp
```ruby
RSpec.configure do |config|
config.include Devise::Test::IntegrationHelpers
+ config.include FactoryBot::Syntax::Methods
+
config.before do
- user = FactoryBot.create(:user, name: "Contract Test").tap do |user|
+ user = create(:user, name: Provider::UsersHelper::CONTRACT_USER_NAME).tap do |user|
user.current_sign_in_at = Time.current
end
+
sign_in user
end
end
@@ -134,7 +141,7 @@ Any further modifications to the user that's needed can be done through the indi
In the state file, you must define which consumer this provider state is for. You can do that with `provider_states_for`. Make sure that the `name` provided matches the name defined for the consumer.
```ruby
-Pact.provider_states_for 'Merge Request Page' do
+Pact.provider_states_for 'MergeRequest#show' do
end
```
@@ -143,9 +150,9 @@ end
In the `provider_states_for` block, you then define the state the test data is for. These states are also defined in the consumer test. In this case, there is a `'a merge request with discussions exists'` state.
```ruby
-Pact.provider_states_for "Merge Request Page" do
+Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
-
+
end
end
```
@@ -155,7 +162,7 @@ end
This is where you define the test data creation steps. Use `FactoryBot` to create the data. As you create the test data, you can keep [running the provider test](index.md#run-the-provider-tests) to check on the status of the test and figure out what else is missing in your data setup.
```ruby
-Pact.provider_states_for "Merge Request Page" do
+Pact.provider_states_for "MergeRequest#show" do
provider_state "a merge request with discussions exists" do
set_up do
user = User.find_by(name: Provider::UsersHelper::CONTRACT_USER_NAME)
@@ -172,6 +179,28 @@ Pact.provider_states_for "Merge Request Page" do
end
```
-Note the `Provider::UsersHelper::CONTRACT_USER_NAME` here to fetch a user is a user that is from the [`spec_helper`](https://gitlab.com/gitlab-org/gitlab/-/tree/master/spec/contracts/provider/spec_helper.rb) that sets up a user before any of these tests run.
+## Using the test data
+
+Now that the provider state file is created, you need to import the state file to the provider test.
+
+```ruby
+# frozen_string_literal: true
+
+require_relative '../../../spec_helper'
+require_relative '../../../states/project/merge_request/discussions_state'
+
+module Provider
+ module DiscussionsHelper
+ Pact.service_provider "/merge_request/discussions" do
+ app { Environments::Test.app }
+
+ honours_pact_with 'Merge Request#show' do
+ pact_uri '../contracts/project/merge_request/show/merge_request#show-merge_request_discussions_endpoint.json'
+ end
+ end
+ end
+end
+
+```
-And with that, the provider tests for `discussion_helper.rb` should now pass with this.
+And there we have it. The provider test for `discussions_helper.rb` should now pass with this.
diff --git a/doc/update/index.md b/doc/update/index.md
index 39cbce23788..c1bff13005c 100644
--- a/doc/update/index.md
+++ b/doc/update/index.md
@@ -458,11 +458,27 @@ NOTE:
Specific information that follow related to Ruby and Git versions do not apply to [Omnibus installations](https://docs.gitlab.com/omnibus/)
and [Helm Chart deployments](https://docs.gitlab.com/charts/). They come with appropriate Ruby and Git versions and are not using system binaries for Ruby and Git. There is no need to install Ruby or Git when utilizing these two approaches.
+### 15.2.0 (unreleased)
+
+GitLab installations that have multiple web nodes should be
+[upgraded to 15.1](#1510) before upgrading to 15.2 (and later) due to a
+configuration change in Rails that can result in inconsistent ETag key
+generation.
+
### 15.1.0
- If you run external PostgreSQL, particularly AWS RDS,
[check you have a PostgreSQL bug fix](#postgresql-segmentation-fault-issue)
to avoid the database crashing.
+- In GitLab 15.1.0, we are switching Rails `ActiveSupport::Digest` to use SHA256 instead of MD5.
+ This affects ETag key generation for resources such as raw Snippet file
+ downloads. In order to ensure consistent ETag key generation across multiple
+ web nodes when upgrading, all servers must first be upgraded to 15.1.Z before
+ upgrading to 15.2.0 or later:
+
+ 1. Ensure all GitLab web nodes are running GitLab 15.1.Z.
+ 1. [Enable the `active_support_hash_digest_sha256` feature flag](../administration/feature_flags.md#how-to-enable-and-disable-features-behind-flags) to switch `ActiveSupport::Digest` to use SHA256:
+ 1. Only then, continue to upgrade to later versions of GitLab.
### 15.0.0
diff --git a/doc/user/application_security/dast/checks/16.10.md b/doc/user/application_security/dast/checks/16.10.md
new file mode 100644
index 00000000000..67368d80022
--- /dev/null
+++ b/doc/user/application_security/dast/checks/16.10.md
@@ -0,0 +1,30 @@
+---
+stage: Secure
+group: Dynamic Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Content-Security-Policy violations
+
+## Description
+
+A `Content-Security-Policy` (CSP) was identified on the target site that is reporting violations when
+attempting to load the page in a browser. This may cause disruption to your users when attempting to visit the page.
+
+## Remediation
+
+Review the violations to determine if any action is necessary.
+
+## Details
+
+| ID | Aggregated | CWE | Type | Risk |
+|:---|:--------|:--------|:--------|:--------|
+| 16.10 | true | 16 | Passive | Info |
+
+## Links
+
+- [CWE](https://cwe.mitre.org/data/definitions/16.html)
+- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
+- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
+- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
diff --git a/doc/user/application_security/dast/checks/16.8.md b/doc/user/application_security/dast/checks/16.8.md
new file mode 100644
index 00000000000..c9beba4544e
--- /dev/null
+++ b/doc/user/application_security/dast/checks/16.8.md
@@ -0,0 +1,30 @@
+---
+stage: Secure
+group: Dynamic Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Content-Security-Policy analysis
+
+## Description
+
+A `Content-Security-Policy` (CSP) was identified on the target site. CSP can aid in hardening
+a website against various client side attacks such as Cross-Site Scripting (XSS).
+
+## Remediation
+
+Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy`.
+
+## Details
+
+| ID | Aggregated | CWE | Type | Risk |
+|:---|:--------|:--------|:--------|:--------|
+| 16.8 | true | 16 | Passive | Info |
+
+## Links
+
+- [CWE](https://cwe.mitre.org/data/definitions/16.html)
+- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
+- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
+- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
diff --git a/doc/user/application_security/dast/checks/16.9.md b/doc/user/application_security/dast/checks/16.9.md
new file mode 100644
index 00000000000..0260beaf434
--- /dev/null
+++ b/doc/user/application_security/dast/checks/16.9.md
@@ -0,0 +1,32 @@
+---
+stage: Secure
+group: Dynamic Analysis
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
+---
+
+# Content-Security-Policy-Report-Only analysis
+
+## Description
+
+A `Content-Security-Policy-Report-Only` (CSPRO) was identified on the target site. CSP-Report-Only headers
+aid in determining how to implement a `Content-Security-Policy` that does not disrupt normal use of the target
+site.
+
+## Remediation
+
+Follow the recommendations to determine if any actions are necessary to harden this `Content-Security-Policy-Report-Only`.
+After all alerts have been resolved, we recommended that this header be changed to `Content-Security-Policy`.
+
+## Details
+
+| ID | Aggregated | CWE | Type | Risk |
+|:---|:--------|:--------|:--------|:--------|
+| 16.9 | true | 16 | Passive | Info |
+
+## Links
+
+- [CWE](https://cwe.mitre.org/data/definitions/16.html)
+- [OWASP](https://cheatsheetseries.owasp.org/cheatsheets/Content_Security_Policy_Cheat_Sheet.html)
+- [MDN](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)
+- [Content Security Policy Level 3](https://www.w3.org/TR/CSP3/)
+- [CSP Evaluator](https://csp-evaluator.withgoogle.com/)
diff --git a/doc/user/application_security/dast/checks/index.md b/doc/user/application_security/dast/checks/index.md
index e2947d5b120..c6d5f874047 100644
--- a/doc/user/application_security/dast/checks/index.md
+++ b/doc/user/application_security/dast/checks/index.md
@@ -12,12 +12,15 @@ The [DAST browser-based crawler](../browser_based.md) provides a number of vulne
|:---|:------|:---------|:-----|
| [1004.1](1004.1.md) | Sensitive cookie without HttpOnly attribute | Low | Passive |
| [16.1](16.1.md) | Missing Content-Type header | Low | Passive |
+| [16.10](16.10.md) | Content-Security-Policy violations | Info | Passive |
| [16.2](16.2.md) | Server header exposes version information | Low | Passive |
| [16.3](16.3.md) | X-Powered-By header exposes version information | Low | Passive |
| [16.4](16.4.md) | X-Backend-Server header exposes server information | Info | Passive |
| [16.5](16.5.md) | AspNet header exposes version information | Low | Passive |
| [16.6](16.6.md) | AspNetMvc header exposes version information | Low | Passive |
| [16.7](16.7.md) | Strict-Transport-Security header missing or invalid | Low | Passive |
+| [16.8](16.8.md) | Content-Security-Policy analysis | Info | Passive |
+| [16.9](16.9.md) | Content-Security-Policy-Report-Only analysis | Info | Passive |
| [200.1](200.1.md) | Exposure of sensitive information to an unauthorized actor (private IP address) | Low | Passive |
| [209.1](209.1.md) | Generation of error message containing sensitive information | Low | Passive |
| [319.1](319.1.md) | Mixed Content | Info | Passive |
diff --git a/qa/lib/gitlab/page/admin/subscription.rb b/qa/lib/gitlab/page/admin/subscription.rb
index b90a49abf4b..1538384f6ed 100644
--- a/qa/lib/gitlab/page/admin/subscription.rb
+++ b/qa/lib/gitlab/page/admin/subscription.rb
@@ -10,6 +10,8 @@ module Gitlab
text_field :activation_code
button :activate
label :terms_of_services, text: /I agree that/
+ link :remove_license, 'data-testid': 'license-remove-action'
+ button :confirm_ok_button
p :plan
p :started
p :name
@@ -21,6 +23,9 @@ module Gitlab
h2 :users_over_subscription
table :subscription_history
+ span :no_valid_license_alert, text: /no longer has a valid license/
+ h3 :no_active_subscription_title, text: /do not have an active subscription/
+
def accept_terms
terms_of_services_element.click # workaround for hidden checkbox
end
@@ -39,7 +44,7 @@ module Gitlab
# @param license_type [Hash] Type of the license
# @option license_type [String] 'license file'
# @option license_type [String] 'cloud license'
- # @return [Boolean] True if record exsists, false if not
+ # @return [Boolean] True if record exists, false if not
def has_subscription_record?(plan, users_in_license, license_type)
# find any records that have a matching plan and seats and type
subscription_history_element.hashes.any? do |record|
diff --git a/qa/qa/resource/user.rb b/qa/qa/resource/user.rb
index eab428a61e7..1b848feb50d 100644
--- a/qa/qa/resource/user.rb
+++ b/qa/qa/resource/user.rb
@@ -163,6 +163,20 @@ module QA
end
end
+ # Get users from the API
+ #
+ # @param [Integer] per_page the number of pages to traverse (used for pagination)
+ # @return [Array<Hash>] parsed response body
+ def self.all(per_page: 100)
+ response = nil
+ Resource::User.init do |user|
+ response = user.get(Runtime::API::Request.new(Runtime::API::Client.as_admin,
+ '/users',
+ per_page: per_page.to_s).url)
+ raise ResourceQueryError unless response.code == 200
+ end.parse_body(response)
+ end
+
def approve!
response = post(Runtime::API::Request.new(api_client, api_approve_path).url, nil)
return if response.code == 201
diff --git a/spec/migrations/change_public_projects_cost_factor_spec.rb b/spec/migrations/change_public_projects_cost_factor_spec.rb
new file mode 100644
index 00000000000..78030736093
--- /dev/null
+++ b/spec/migrations/change_public_projects_cost_factor_spec.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe ChangePublicProjectsCostFactor, :migration do
+ # This is a workaround to force the migration to run against the
+ # `gitlab_ci` schema. Otherwise it only runs against `gitlab_main`.
+ around do |example| # rubocop: disable Style/MultilineIfModifier
+ with_reestablished_active_record_base do
+ reconfigure_db_connection(name: :ci)
+ example.run
+ end
+ end if Gitlab::Database.has_config?(:ci)
+
+ let(:runners) { table(:ci_runners) }
+
+ let!(:shared_1) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
+ let!(:shared_2) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 0) }
+ let!(:shared_3) { runners.create!(runner_type: 1, public_projects_minutes_cost_factor: 1) }
+ let!(:group_1) { runners.create!(runner_type: 2, public_projects_minutes_cost_factor: 0) }
+
+ describe '#up' do
+ context 'when on SaaS' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ it 'updates the cost factor from 0 only for shared runners', :aggregate_failures do
+ migrate!
+
+ expect(shared_1.reload.public_projects_minutes_cost_factor).to eq(0.008)
+ expect(shared_2.reload.public_projects_minutes_cost_factor).to eq(0.008)
+ expect(shared_3.reload.public_projects_minutes_cost_factor).to eq(1)
+ expect(group_1.reload.public_projects_minutes_cost_factor).to eq(0)
+ end
+ end
+
+ context 'when on self-managed', :aggregate_failures do
+ it 'skips the migration' do
+ migrate!
+
+ expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
+ expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
+ expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
+ expect(group_1.public_projects_minutes_cost_factor).to eq(0)
+ end
+ end
+ end
+
+ describe '#down' do
+ context 'when on SaaS' do
+ before do
+ allow(Gitlab).to receive(:com?).and_return(true)
+ end
+
+ it 'resets the cost factor to 0 only for shared runners that were updated', :aggregate_failures do
+ migrate!
+ schema_migrate_down!
+
+ expect(shared_1.public_projects_minutes_cost_factor).to eq(0)
+ expect(shared_2.public_projects_minutes_cost_factor).to eq(0)
+ expect(shared_3.public_projects_minutes_cost_factor).to eq(1)
+ expect(group_1.public_projects_minutes_cost_factor).to eq(0)
+ end
+ end
+ end
+end