diff --git a/ee/app/finders/security/vulnerability_reads_finder.rb b/ee/app/finders/security/vulnerability_reads_finder.rb index 08fa0e2ad625a8c1b44a6f70ab506d54982beb11..5f853f9110086f3545a8d63f96db585cd7e94f36 100644 --- a/ee/app/finders/security/vulnerability_reads_finder.rb +++ b/ee/app/finders/security/vulnerability_reads_finder.rb @@ -144,7 +144,7 @@ def filter_by_owasp_top_10 return if params[:owasp_top_10].include?(FILTER_NONE) && owasp_top_10_none_filter_disallowed? - filter_value = filter_by_no_owasp_top_10? ? -1 : params[:owasp_top_10] + filter_value = filter_by_no_owasp_top_10? ? ::Vulnerabilities::Read::OWASP_TOP_10_DEFAULT : params[:owasp_top_10] @vulnerability_reads = vulnerability_reads.with_owasp_top_10(filter_value) end diff --git a/ee/app/models/vulnerabilities/read.rb b/ee/app/models/vulnerabilities/read.rb index c5eea8969a8b5a9f0c1ab960a2dd29710cb305b2..d0cd3c86428b9f1e0923f8d3a2b418e8cbe79bdf 100644 --- a/ee/app/models/vulnerabilities/read.rb +++ b/ee/app/models/vulnerabilities/read.rb @@ -13,6 +13,7 @@ class Read < Gitlab::Database::SecApplicationRecord declarative_enum DismissalReasonEnum SEVERITY_COUNT_LIMIT = 1001 + OWASP_TOP_10_DEFAULT = -1 self.table_name = "vulnerability_reads" self.primary_key = :vulnerability_id @@ -40,7 +41,9 @@ class Read < Gitlab::Database::SecApplicationRecord enum state: ::Enums::Vulnerability.vulnerability_states enum report_type: ::Enums::Vulnerability.report_types enum severity: ::Enums::Vulnerability.severity_levels, _prefix: :severity - enum owasp_top_10: ::Enums::Vulnerability.owasp_top_10 + enum owasp_top_10: ::Enums::Vulnerability.owasp_top_10.merge('undefined' => OWASP_TOP_10_DEFAULT) + + after_initialize :set_default_values, if: :new_record? scope :by_uuid, ->(uuids) { where(uuid: uuids) } scope :by_vulnerabilities, ->(vulnerabilities) { where(vulnerability: vulnerabilities) } @@ -230,5 +233,9 @@ def database_serialized_traversal_ids .then { |serialized_array| self.class.connection.quote(serialized_array) } .then { |quoted_array| Arel::Nodes::SqlLiteral.new(quoted_array) } end + + def set_default_values + self.owasp_top_10 = 'undefined' + end end end diff --git a/ee/app/services/security/ingestion/tasks/ingest_vulnerability_reads/update.rb b/ee/app/services/security/ingestion/tasks/ingest_vulnerability_reads/update.rb index 61d216e7b1c5f8c95ce084c5862d5bda8c938d76..45056904c88d467d5a5aed4a17dbb0a22299de21 100644 --- a/ee/app/services/security/ingestion/tasks/ingest_vulnerability_reads/update.rb +++ b/ee/app/services/security/ingestion/tasks/ingest_vulnerability_reads/update.rb @@ -20,7 +20,7 @@ def attributes has_vulnerability_resolution: has_ai_resolution?(finding_map), traversal_ids: finding_map.project.namespace.traversal_ids, archived: finding_map.project.archived, - owasp_top_10: nil, + owasp_top_10: ::Vulnerabilities::Read::OWASP_TOP_10_DEFAULT, identifier_names: identifier_names(finding_map), scanner_id: finding_map.scanner_id } @@ -62,9 +62,12 @@ def identifier_names(finding_map) # The maximum set of observed external_id is having the label, year without the name. Eg: 'A5:2017'. # See: https://gitlab.com/gitlab-org/gitlab/-/issues/423557#note_1539490082 def map_owasp_external_id(external_id) - return unless valid_owasp_external_id?(external_id) + default_value = ::Vulnerabilities::Read::OWASP_TOP_10_DEFAULT - ::Enums::Vulnerability.owasp_top_10.keys.find { |key| key.include?(external_id) } + return default_value unless valid_owasp_external_id?(external_id) + + ::Enums::Vulnerability.owasp_top_10.keys.find { |key| key.include?(external_id) } || + default_value end def valid_owasp_external_id?(external_id) @@ -82,6 +85,11 @@ def has_changes?(attributes) vulnerability_read = grouped_vulnerability_reads.fetch(attributes[:vulnerability_id]) existing_attributes = vulnerability_read.attributes.symbolize_keys.slice(*attributes.keys) + # Work around since the model_object.attributes return only human readable key value of AR Enum. + if existing_attributes[:owasp_top_10] == 'undefined' + existing_attributes[:owasp_top_10] = ::Vulnerabilities::Read::OWASP_TOP_10_DEFAULT + end + attributes != existing_attributes end diff --git a/ee/spec/factories/vulnerabilities/read.rb b/ee/spec/factories/vulnerabilities/read.rb index d63fd9e5d8589a5e4188ecb4b32e0c0cfb969f96..8432ebfcf2f057d71818bc4e51962221d61d6d0f 100644 --- a/ee/spec/factories/vulnerabilities/read.rb +++ b/ee/spec/factories/vulnerabilities/read.rb @@ -9,6 +9,7 @@ severity { :high } state { :detected } uuid { SecureRandom.uuid } + owasp_top_10 { 'undefined' } traits_for_enum :dismissal_reason, Vulnerabilities::DismissalReasonEnum.values.keys after(:build) do |vulnerability_read, _| diff --git a/ee/spec/services/security/ingestion/tasks/ingest_vulnerability_reads/update_spec.rb b/ee/spec/services/security/ingestion/tasks/ingest_vulnerability_reads/update_spec.rb index 4bdea62953d6b98715409b05ad16da99d2da20c0..d96b765793aaae444290519aaf82023735e95d93 100644 --- a/ee/spec/services/security/ingestion/tasks/ingest_vulnerability_reads/update_spec.rb +++ b/ee/spec/services/security/ingestion/tasks/ingest_vulnerability_reads/update_spec.rb @@ -151,7 +151,7 @@ it 'does not set owasp_top_10 on vulnerability reads' do execute - expect(vulnerability_read.owasp_top_10).to be_nil + expect(vulnerability_read.owasp_top_10).to eq('undefined') end it_behaves_like 'does not fire an UPDATE query' @@ -247,7 +247,7 @@ end it 'resets the `owasp_top_10` attribute' do - expect { execute }.to change { vulnerability_read.reload.owasp_top_10 }.to(nil) + expect { execute }.to change { vulnerability_read.reload.owasp_top_10 }.to('undefined') end end end