diff options
27 files changed, 586 insertions, 25 deletions
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 5617ab0af41..09b335f9ba2 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -883,7 +883,7 @@ .time-tracking-help-state { background: $white-light; - margin: 16px -20px 0; + margin: 16px -20px -20px; padding: 16px 20px; border-top: 1px solid $border-gray-light; border-bottom: 1px solid $border-gray-light; diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb index c244eba9e08..ba2330dfc9a 100644 --- a/app/helpers/emails_helper.rb +++ b/app/helpers/emails_helper.rb @@ -112,20 +112,20 @@ module EmailsHelper end end - # "You are receiving this email because #{reason}" + # "You are receiving this email because #{reason} on #{gitlab_host}." def notification_reason_text(reason) - string = case reason - when NotificationReason::OWN_ACTIVITY - 'of your activity' - when NotificationReason::ASSIGNED - 'you have been assigned an item' - when NotificationReason::MENTIONED - 'you have been mentioned' - else - 'of your account' - end - - "#{string} on #{Gitlab.config.gitlab.host}" + gitlab_host = Gitlab.config.gitlab.host + + case reason + when NotificationReason::OWN_ACTIVITY + _("You're receiving this email because of your activity on %{host}.") % { host: gitlab_host } + when NotificationReason::ASSIGNED + _("You're receiving this email because you have been assigned an item on %{host}.") % { host: gitlab_host } + when NotificationReason::MENTIONED + _("You're receiving this email because you have been mentioned on %{host}.") % { host: gitlab_host } + else + _("You're receiving this email because of your account on %{host}.") % { host: gitlab_host } + end end def create_list_id_string(project, list_id_max_length = 255) diff --git a/app/models/active_session.rb b/app/models/active_session.rb index a6d5fc1137d..3ecc3137157 100644 --- a/app/models/active_session.rb +++ b/app/models/active_session.rb @@ -146,8 +146,9 @@ class ActiveSession # remove sessions if there are more than ALLOWED_NUMBER_OF_ACTIVE_SESSIONS. sessions = active_session_entries(session_ids, user.id, redis) sessions.sort_by! {|session| session.updated_at }.reverse! - sessions = sessions[ALLOWED_NUMBER_OF_ACTIVE_SESSIONS..-1].map { |session| session.session_id } - destroy_sessions(redis, user, sessions) + sessions = sessions.drop(ALLOWED_NUMBER_OF_ACTIVE_SESSIONS) + sessions = sessions.map { |session| session.session_id } + destroy_sessions(redis, user, sessions) if sessions.any? end def self.cleaned_up_lookup_entries(redis, user) diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb index 62b2217a9af..315cdcffb42 100644 --- a/app/models/clusters/cluster.rb +++ b/app/models/clusters/cluster.rb @@ -271,6 +271,21 @@ module Clusters kubernetes_namespaces.delete_all(:delete_all) end + def clusterable + return unless cluster_type + + case cluster_type + when 'project_type' + project + when 'group_type' + group + when 'instance_type' + instance + else + raise NotImplementedError + end + end + private def unique_management_project_environment_scope diff --git a/app/services/clusters/applications/ingress_modsecurity_usage_service.rb b/app/services/clusters/applications/ingress_modsecurity_usage_service.rb new file mode 100644 index 00000000000..4aac8bb3cbd --- /dev/null +++ b/app/services/clusters/applications/ingress_modsecurity_usage_service.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +# rubocop: disable CodeReuse/ActiveRecord +module Clusters + module Applications + ## + # This service measures usage of the Modsecurity Web Application Firewall across the entire + # instance's deployed environments. + # + # The default configuration is`AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE=DetectionOnly` so we + # measure non-default values via definition of either ci_variables or ci_pipeline_variables. + # Since both these values are encrypted, we must decrypt and count them in memory. + # + # NOTE: this service is an approximation as it does not yet take into account `environment_scope` or `ci_group_variables`. + ## + class IngressModsecurityUsageService + ADO_MODSEC_KEY = "AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE" + + def initialize(blocking_count: 0, disabled_count: 0) + @blocking_count = blocking_count + @disabled_count = disabled_count + end + + def execute + conditions = -> { merge(::Environment.available).merge(::Deployment.success).where(key: ADO_MODSEC_KEY) } + + ci_pipeline_var_enabled = + ::Ci::PipelineVariable + .joins(pipeline: { environments: :last_visible_deployment }) + .merge(conditions) + .order('deployments.environment_id, deployments.id DESC') + + ci_var_enabled = + ::Ci::Variable + .joins(project: { environments: :last_visible_deployment }) + .merge(conditions) + .merge( + # Give priority to pipeline variables by excluding from dataset + ::Ci::Variable.joins(project: :environments).where.not( + environments: { id: ci_pipeline_var_enabled.select('DISTINCT ON (deployments.environment_id) deployments.environment_id') } + ) + ).select('DISTINCT ON (deployments.environment_id) ci_variables.*') + + sum_modsec_config_counts( + ci_pipeline_var_enabled.select('DISTINCT ON (deployments.environment_id) ci_pipeline_variables.*') + ) + sum_modsec_config_counts(ci_var_enabled) + + { + ingress_modsecurity_blocking: @blocking_count, + ingress_modsecurity_disabled: @disabled_count + } + end + + private + + # These are encrypted so we must decrypt and count in memory + def sum_modsec_config_counts(dataset) + dataset.each do |var| + case var.value + when "On" then @blocking_count += 1 + when "Off" then @disabled_count += 1 + # `else` could be default or any unsupported user input + end + end + end + end + end +end diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index de487a94d40..e922b505be8 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -20,7 +20,7 @@ #{link_to _("View it on GitLab"), @target_url}. %br -# Don't link the host in the line below, one link in the email is easier to quickly click than two. - = _("You're receiving this email because %{reason}.") % { reason: notification_reason_text(@reason) } + = notification_reason_text(@reason) If you'd like to receive fewer emails, you can - if @labels_url adjust your #{link_to 'label subscriptions', @labels_url}. diff --git a/app/views/layouts/notify.text.erb b/app/views/layouts/notify.text.erb index 0ee30c2a6cf..49ad0b5abc5 100644 --- a/app/views/layouts/notify.text.erb +++ b/app/views/layouts/notify.text.erb @@ -11,7 +11,7 @@ <% end -%> <% end -%> -<%= "You're receiving this email because #{notification_reason_text(@reason)}." %> +<%= notification_reason_text(@reason) %> <%= render_if_exists 'layouts/mailer/additional_text' %> <%= text_footer_message -%> diff --git a/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml b/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml new file mode 100644 index 00000000000..3c2b2ad7e1d --- /dev/null +++ b/changelogs/unreleased/32358-add-modsec-blocking-usage-stats-per-project.yml @@ -0,0 +1,5 @@ +--- +title: Add modsecurity deployment counts to usage ping +merge_request: 20196 +author: +type: added diff --git a/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml b/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml new file mode 100644 index 00000000000..11d2cfa4d4b --- /dev/null +++ b/changelogs/unreleased/37480-validate-instance-level-cluster-environment-scope.yml @@ -0,0 +1,5 @@ +--- +title: Validate unique environment scope for instance clusters +merge_request: 20886 +author: +type: fixed diff --git a/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml b/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml new file mode 100644 index 00000000000..0abf69d9d4c --- /dev/null +++ b/changelogs/unreleased/remove_extra_spacing_below_sidebar_time_tracking_info.yml @@ -0,0 +1,5 @@ +--- +title: Remove extra spacing below sidebar time tracking info +merge_request: 20657 +author: Lee Tickett +type: other diff --git a/config/routes/group.rb b/config/routes/group.rb index 437c80b8c92..30671d4e0a1 100644 --- a/config/routes/group.rb +++ b/config/routes/group.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true +# rubocop: disable Cop/PutGroupRoutesUnderScope resources :groups, only: [:index, :new, :create] do post :preview_markdown end +# rubocop: enable Cop/PutGroupRoutesUnderScope constraints(::Constraints::GroupUrlConstrainer.new) do scope(path: 'groups/*id', diff --git a/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb b/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb new file mode 100644 index 00000000000..169ecf5ea41 --- /dev/null +++ b/db/migrate/20191115001123_add_index_to_mod_sec_ci_variables.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToModSecCiVariables < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :ci_variables, :project_id, where: "key = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'" + end + + def down + remove_concurrent_index :ci_variables, :project_id + end +end diff --git a/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb b/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb new file mode 100644 index 00000000000..6b13f565a11 --- /dev/null +++ b/db/migrate/20191115001843_add_index_to_mod_sec_ci_pipeline_variables.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddIndexToModSecCiPipelineVariables < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :ci_pipeline_variables, :pipeline_id, where: "key = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'" + end + + def down + remove_concurrent_index :ci_pipeline_variables, :pipeline_id + end +end diff --git a/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb b/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb new file mode 100644 index 00000000000..bda461af7bc --- /dev/null +++ b/db/post_migrate/20191202031812_drop_operations_feature_flags_clients_token.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +class DropOperationsFeatureFlagsClientsToken < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + # Ignored in 12.5 - https://gitlab.com/gitlab-org/gitlab/merge_requests/18923 + remove_column :operations_feature_flags_clients, :token + end + + def down + unless column_exists?(:operations_feature_flags_clients, :token) + add_column :operations_feature_flags_clients, :token, :string # rubocop:disable Migration/AddLimitToStringColumns + end + + add_concurrent_index :operations_feature_flags_clients, [:project_id, :token], unique: true, + name: 'index_operations_feature_flags_clients_on_project_id_and_token' + end +end diff --git a/db/schema.rb b/db/schema.rb index 253d82e790b..3bdf41e609f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_11_25_140458) do +ActiveRecord::Schema.define(version: 2019_12_02_031812) do # These are extensions that must be enabled in order to support this database enable_extension "pg_trgm" @@ -821,6 +821,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do t.integer "pipeline_id", null: false t.integer "variable_type", limit: 2, default: 1, null: false t.index ["pipeline_id", "key"], name: "index_ci_pipeline_variables_on_pipeline_id_and_key", unique: true + t.index ["pipeline_id"], name: "index_ci_pipeline_variables_on_pipeline_id", where: "((key)::text = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'::text)" end create_table "ci_pipelines", id: :serial, force: :cascade do |t| @@ -979,6 +980,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do t.boolean "masked", default: false, null: false t.integer "variable_type", limit: 2, default: 1, null: false t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true + t.index ["project_id"], name: "index_ci_variables_on_project_id", where: "((key)::text = 'AUTO_DEVOPS_MODSECURITY_SEC_RULE_ENGINE'::text)" end create_table "cluster_groups", id: :serial, force: :cascade do |t| @@ -2774,9 +2776,7 @@ ActiveRecord::Schema.define(version: 2019_11_25_140458) do create_table "operations_feature_flags_clients", force: :cascade do |t| t.integer "project_id", null: false - t.string "token" t.string "token_encrypted" - t.index ["project_id", "token"], name: "index_operations_feature_flags_clients_on_project_id_and_token", unique: true t.index ["project_id", "token_encrypted"], name: "index_feature_flags_clients_on_project_id_and_token_encrypted", unique: true end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index a0ff34c76e2..7268e11ff23 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -108,7 +108,8 @@ module Gitlab services_usage, approximate_counts, usage_counters, - user_preferences_usage + user_preferences_usage, + ingress_modsecurity_usage ) } end @@ -170,6 +171,10 @@ module Gitlab } end + def ingress_modsecurity_usage + ::Clusters::Applications::IngressModsecurityUsageService.new.execute + end + # rubocop: disable CodeReuse/ActiveRecord def services_usage types = { diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 9926fd8994e..8d28cf86dd6 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -20306,15 +20306,21 @@ msgstr "" msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options." msgstr "" -msgid "You're receiving this email because %{reason}." -msgstr "" - msgid "You're receiving this email because of your account on %{host}." msgstr "" msgid "You're receiving this email because of your account on %{host}. %{manage_notifications_link} · %{help_link}" msgstr "" +msgid "You're receiving this email because of your activity on %{host}." +msgstr "" + +msgid "You're receiving this email because you have been assigned an item on %{host}." +msgstr "" + +msgid "You're receiving this email because you have been mentioned on %{host}." +msgstr "" + msgid "You've already enabled two-factor authentication using one time password authenticators. In order to register a different device, you must first disable two-factor authentication." msgstr "" diff --git a/rubocop/cop/put_group_routes_under_scope.rb b/rubocop/cop/put_group_routes_under_scope.rb new file mode 100644 index 00000000000..bcdde01fdb5 --- /dev/null +++ b/rubocop/cop/put_group_routes_under_scope.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + # Checks for a group routes outside '/-/' scope. + # For more information see: https://gitlab.com/gitlab-org/gitlab/issues/29572 + class PutGroupRoutesUnderScope < RuboCop::Cop::Cop + MSG = 'Put new group routes under /-/ scope' + + def_node_matcher :dash_scope?, <<~PATTERN + (:send nil? :scope (hash <(pair (sym :path)(str "groups/*group_id/-")) ...>)) + PATTERN + + def on_send(node) + return unless in_group_routes?(node) + return unless resource?(node) + return unless outside_scope?(node) + + add_offense(node) + end + + def outside_scope?(node) + node.each_ancestor(:block).none? do |parent| + dash_scope?(parent.to_a.first) + end + end + + def in_group_routes?(node) + path = node.location.expression.source_buffer.name + dirname = File.dirname(path) + filename = File.basename(path) + + dirname.end_with?('config/routes') && + filename.end_with?('group.rb') + end + + def resource?(node) + node.method_name == :resource || + node.method_name == :resources + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 9c9948e2a61..1465c73d570 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -15,6 +15,7 @@ require_relative 'cop/avoid_route_redirect_leading_slash' require_relative 'cop/line_break_around_conditional_block' require_relative 'cop/prefer_class_methods_over_module' require_relative 'cop/put_project_routes_under_scope' +require_relative 'cop/put_group_routes_under_scope' require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_concurrent_foreign_key' require_relative 'cop/migration/add_concurrent_index' diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb index 931b7008173..0ff9080ef94 100644 --- a/spec/helpers/emails_helper_spec.rb +++ b/spec/helpers/emails_helper_spec.rb @@ -74,6 +74,28 @@ describe EmailsHelper do end end + describe 'notification_reason_text' do + subject { helper.notification_reason_text(reason_code) } + + using RSpec::Parameterized::TableSyntax + + where(:reason_code, :reason_text) do + NotificationReason::OWN_ACTIVITY | ' of your activity ' + NotificationReason::ASSIGNED | ' you have been assigned an item ' + NotificationReason::MENTIONED | ' you have been mentioned ' + "" | ' of your account ' + nil | ' of your account ' + end + + with_them do + it { is_expected.to start_with "You're receiving this email because" } + + it { is_expected.to include reason_text } + + it { is_expected.to end_with "on #{Gitlab.config.gitlab.host}." } + end + end + describe 'sanitize_name' do context 'when name contains a valid URL string' do it 'returns name with `.` replaced with `_` to prevent mail clients from auto-linking URLs' do diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 713ddca6c2b..7a5d6f5ad5a 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -297,6 +297,24 @@ describe Gitlab::UsageData do end end + describe '#ingress_modsecurity_usage' do + subject { described_class.ingress_modsecurity_usage } + + it 'gathers variable data' do + allow_any_instance_of( + ::Clusters::Applications::IngressModsecurityUsageService + ).to receive(:execute).and_return( + { + ingress_modsecurity_blocking: 1, + ingress_modsecurity_disabled: 2 + } + ) + + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(2) + end + end + describe '#license_usage_data' do subject { described_class.license_usage_data } diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb index c26675e75bf..072d0fa86e5 100644 --- a/spec/models/active_session_spec.rb +++ b/spec/models/active_session_spec.rb @@ -329,6 +329,35 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do ) end end + + context 'when the number of active sessions is lower than the limit' do + before do + Gitlab::Redis::SharedState.with do |redis| + ((max_number_of_sessions_plus_two - 4)..max_number_of_sessions_plus_two).each do |number| + redis.del("session:user:gitlab:#{user.id}:#{number}") + end + end + end + + it 'does not remove active session entries, but removes lookup entries' do + lookup_entries_before_cleanup = Gitlab::Redis::SharedState.with do |redis| + redis.smembers("session:lookup:user:gitlab:#{user.id}") + end + + sessions_before_cleanup = Gitlab::Redis::SharedState.with do |redis| + redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a + end + + ActiveSession.cleanup(user) + + Gitlab::Redis::SharedState.with do |redis| + lookup_entries = redis.smembers("session:lookup:user:gitlab:#{user.id}") + sessions = redis.scan_each(match: "session:user:gitlab:#{user.id}:*").to_a + expect(sessions.count).to eq(sessions_before_cleanup.count) + expect(lookup_entries.count).to be < lookup_entries_before_cleanup.count + end + end + end end end end diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb index 7c419a195cd..807214dcc14 100644 --- a/spec/models/clusters/cluster_spec.rb +++ b/spec/models/clusters/cluster_spec.rb @@ -976,4 +976,38 @@ describe Clusters::Cluster, :use_clean_rails_memory_store_caching do expect(cluster.kubernetes_namespaces).to be_empty end end + + describe '#clusterable' do + subject { cluster.clusterable } + + context 'project type' do + let(:cluster) { create(:cluster, :project) } + + it { is_expected.to eq(cluster.project) } + end + + context 'group type' do + let(:cluster) { create(:cluster, :group) } + + it { is_expected.to eq(cluster.group) } + end + + context 'instance type' do + let(:cluster) { create(:cluster, :instance) } + + it { is_expected.to be_a(Clusters::Instance) } + end + + context 'unknown type' do + let(:cluster) { create(:cluster, :project) } + + before do + allow(cluster).to receive(:cluster_type).and_return('unknown_type') + end + + it 'raises NotImplementedError' do + expect { subject }.to raise_error(NotImplementedError) + end + end + end end diff --git a/spec/rubocop/cop/put_group_routes_under_scope_spec.rb b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb new file mode 100644 index 00000000000..fc4d0015dde --- /dev/null +++ b/spec/rubocop/cop/put_group_routes_under_scope_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'rubocop' +require_relative '../../../rubocop/cop/put_group_routes_under_scope' + +describe RuboCop::Cop::PutGroupRoutesUnderScope do + include CopHelper + + subject(:cop) { described_class.new } + + before do + allow(cop).to receive(:in_group_routes?).and_return(true) + end + + it 'registers an offense when route is outside scope' do + expect_offense(<<~PATTERN.strip_indent) + scope(path: 'groups/*group_id/-', module: :groups) do + resource :issues + end + + resource :notes + ^^^^^^^^^^^^^^^ Put new group routes under /-/ scope + PATTERN + end + + it 'does not register an offense when resource inside the scope' do + expect_no_offenses(<<~PATTERN.strip_indent) + scope(path: 'groups/*group_id/-', module: :groups) do + resource :issues + resource :notes + end + PATTERN + end + + it 'does not register an offense when resource is deep inside the scope' do + expect_no_offenses(<<~PATTERN.strip_indent) + scope(path: 'groups/*group_id/-', module: :groups) do + resource :issues + resource :projects do + resource :issues do + resource :notes + end + end + end + PATTERN + end +end diff --git a/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb b/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb new file mode 100644 index 00000000000..d456284f76a --- /dev/null +++ b/spec/services/clusters/applications/ingress_modsecurity_usage_service_spec.rb @@ -0,0 +1,196 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Clusters::Applications::IngressModsecurityUsageService do + describe '#execute' do + ADO_MODSEC_KEY = Clusters::Applications::IngressModsecurityUsageService::ADO_MODSEC_KEY + + let(:project_with_ci_var) { create(:environment).project } + let(:project_with_pipeline_var) { create(:environment).project } + + subject { described_class.new.execute } + + context 'with multiple projects' do + let(:pipeline1) { create(:ci_pipeline, :with_job, project: project_with_pipeline_var) } + let(:pipeline2) { create(:ci_pipeline, :with_job, project: project_with_ci_var) } + + let!(:deployment_with_pipeline_var) do + create( + :deployment, + :success, + environment: project_with_pipeline_var.environments.first, + project: project_with_pipeline_var, + deployable: pipeline1.builds.last + ) + end + let!(:deployment_with_project_var) do + create( + :deployment, + :success, + environment: project_with_ci_var.environments.first, + project: project_with_ci_var, + deployable: pipeline2.builds.last + ) + end + + context 'mixed data' do + let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, key: ADO_MODSEC_KEY, value: "On") } + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, key: ADO_MODSEC_KEY, value: "Off") } + + it 'gathers variable data' do + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(1) + end + end + + context 'blocking' do + let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "On" } } + + let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) } + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, **modsec_values) } + + it 'gathers variable data' do + expect(subject[:ingress_modsecurity_blocking]).to eq(2) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + end + end + + context 'disabled' do + let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "Off" } } + + let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) } + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline1, **modsec_values) } + + it 'gathers variable data' do + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(2) + end + end + end + + context 'when set as both ci and pipeline variables' do + let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "Off" } } + + let(:pipeline) { create(:ci_pipeline, :with_job, project: project_with_ci_var) } + let!(:deployment) do + create( + :deployment, + :success, + environment: project_with_ci_var.environments.first, + project: project_with_ci_var, + deployable: pipeline.builds.last + ) + end + + let!(:ci_variable) { create(:ci_variable, project: project_with_ci_var, **modsec_values) } + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline, **modsec_values) } + + it 'wont double-count projects' do + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(1) + end + + it 'gives precedence to pipeline variable' do + pipeline_variable.update(value: "On") + + expect(subject[:ingress_modsecurity_blocking]).to eq(1) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + end + end + + context 'when a project has multiple environments' do + let(:modsec_values) { { key: ADO_MODSEC_KEY, value: "On" } } + + let!(:env1) { project_with_pipeline_var.environments.first } + let!(:env2) { create(:environment, project: project_with_pipeline_var) } + + let!(:pipeline_with_2_deployments) do + create(:ci_pipeline, :with_job, project: project_with_ci_var).tap do |pip| + pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var) + end + end + + let!(:deployment1) do + create( + :deployment, + :success, + environment: env1, + project: project_with_pipeline_var, + deployable: pipeline_with_2_deployments.builds.last + ) + end + let!(:deployment2) do + create( + :deployment, + :success, + environment: env2, + project: project_with_pipeline_var, + deployable: pipeline_with_2_deployments.builds.last + ) + end + + context 'when set as ci variable' do + let!(:ci_variable) { create(:ci_variable, project: project_with_pipeline_var, **modsec_values) } + + it 'gathers variable data' do + expect(subject[:ingress_modsecurity_blocking]).to eq(2) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + end + end + + context 'when set as pipeline variable' do + let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_with_2_deployments, **modsec_values) } + + it 'gathers variable data' do + expect(subject[:ingress_modsecurity_blocking]).to eq(2) + expect(subject[:ingress_modsecurity_disabled]).to eq(0) + end + end + end + + context 'when an environment has multiple deployments' do + let!(:env) { project_with_pipeline_var.environments.first } + + let!(:pipeline_first) do + create(:ci_pipeline, :with_job, project: project_with_pipeline_var).tap do |pip| + pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var) + end + end + let!(:pipeline_last) do + create(:ci_pipeline, :with_job, project: project_with_pipeline_var).tap do |pip| + pip.builds << build(:ci_build, pipeline: pip, project: project_with_pipeline_var) + end + end + + let!(:deployment_first) do + create( + :deployment, + :success, + environment: env, + project: project_with_pipeline_var, + deployable: pipeline_first.builds.last + ) + end + let!(:deployment_last) do + create( + :deployment, + :success, + environment: env, + project: project_with_pipeline_var, + deployable: pipeline_last.builds.last + ) + end + + context 'when set as pipeline variable' do + let!(:first_pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_first, key: ADO_MODSEC_KEY, value: "On") } + let!(:last_pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline_last, key: ADO_MODSEC_KEY, value: "Off") } + + it 'gives precedence to latest deployment' do + expect(subject[:ingress_modsecurity_blocking]).to eq(0) + expect(subject[:ingress_modsecurity_disabled]).to eq(1) + end + end + end + end +end diff --git a/vendor/gitignore/C++.gitignore b/vendor/gitignore/C++.gitignore index 259148fa18f..259148fa18f 100644..100755 --- a/vendor/gitignore/C++.gitignore +++ b/vendor/gitignore/C++.gitignore diff --git a/vendor/gitignore/Java.gitignore b/vendor/gitignore/Java.gitignore index a1c2a238a96..a1c2a238a96 100644..100755 --- a/vendor/gitignore/Java.gitignore +++ b/vendor/gitignore/Java.gitignore |