From 1b6bea7a9dc6742d79f33fb082b702669829d00e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Hal=C3=A1sz?= Date: Wed, 4 Sep 2024 17:26:04 +0200 Subject: [PATCH] feat(V2): RHINENG-11269 use the V2 models for SSG import --- app/models/v2/profile.rb | 9 + app/models/v2/rule.rb | 18 + app/models/v2/rule_group.rb | 9 + app/models/v2/security_guide.rb | 6 + app/models/v2/value_definition.rb | 10 + app/services/concerns/xccdf/benchmarks.rb | 48 --- app/services/concerns/xccdf/hosts.rb | 2 +- .../xccdf/profile_os_minor_versions.rb | 12 +- app/services/concerns/xccdf/profile_rules.rb | 17 +- app/services/concerns/xccdf/profiles.rb | 23 +- .../xccdf/rule_group_relationships.rb | 54 --- app/services/concerns/xccdf/rule_groups.rb | 34 +- .../xccdf/rule_references_containers.rb | 45 --- app/services/concerns/xccdf/rule_results.rb | 2 +- app/services/concerns/xccdf/rules.rb | 21 +- .../concerns/xccdf/security_guides.rb | 48 +++ app/services/concerns/xccdf/util.rb | 12 +- .../concerns/xccdf/value_definitions.rb | 20 +- app/services/datastream_importer.rb | 17 +- .../concerns/xccdf/benchmarks_test.rb | 139 ------- .../xccdf/profile_os_minor_versions_test.rb | 80 ---- .../concerns/xccdf/profile_rules_test.rb | 53 --- test/services/concerns/xccdf/profiles_test.rb | 57 --- .../xccdf/rule_group_relationships_test.rb | 112 ------ .../concerns/xccdf/rule_groups_test.rb | 92 ----- .../xccdf/rule_references_containers_test.rb | 64 ---- test/services/concerns/xccdf/rules_test.rb | 141 ------- .../concerns/xccdf/test_result_test.rb | 125 ------ .../concerns/xccdf/value_definitions_test.rb | 79 ---- test/services/xccdf_report_parser_test.rb | 358 ------------------ 30 files changed, 179 insertions(+), 1528 deletions(-) delete mode 100644 app/services/concerns/xccdf/benchmarks.rb delete mode 100644 app/services/concerns/xccdf/rule_group_relationships.rb delete mode 100644 app/services/concerns/xccdf/rule_references_containers.rb create mode 100644 app/services/concerns/xccdf/security_guides.rb delete mode 100644 test/services/concerns/xccdf/benchmarks_test.rb delete mode 100644 test/services/concerns/xccdf/profile_os_minor_versions_test.rb delete mode 100644 test/services/concerns/xccdf/profile_rules_test.rb delete mode 100644 test/services/concerns/xccdf/profiles_test.rb delete mode 100644 test/services/concerns/xccdf/rule_group_relationships_test.rb delete mode 100644 test/services/concerns/xccdf/rule_groups_test.rb delete mode 100644 test/services/concerns/xccdf/rule_references_containers_test.rb delete mode 100644 test/services/concerns/xccdf/rules_test.rb delete mode 100644 test/services/concerns/xccdf/test_result_test.rb delete mode 100644 test/services/concerns/xccdf/value_definitions_test.rb delete mode 100644 test/services/xccdf_report_parser_test.rb diff --git a/app/models/v2/profile.rb b/app/models/v2/profile.rb index 699d401367..d61854429c 100644 --- a/app/models/v2/profile.rb +++ b/app/models/v2/profile.rb @@ -27,5 +27,14 @@ def variant_for_minor(version) os_minor_versions: { os_minor_version: version } ) end + + def self.from_parser(obj, existing: nil, security_guide_id: nil, value_overrides: nil) + record = existing || new(ref_id: obj.id, security_guide_id: security_guide_id) + + record.assign_attributes(title: obj.title, description: obj.description, + value_overrides: value_overrides, upstream: false) + + record + end end end diff --git a/app/models/v2/rule.rb b/app/models/v2/rule.rb index e314dc6e42..ac7d64c77f 100644 --- a/app/models/v2/rule.rb +++ b/app/models/v2/rule.rb @@ -10,6 +10,8 @@ class Rule < ApplicationRecord indexable_by :ref_id, &->(scope, value) { scope.find_by!(ref_id: value.try(:gsub, '-', '.')) } + attr_accessor :op_source + # rubocop:disable Metrics/AbcSize def self.sorted_severities(table = arel_table) Arel.sql( @@ -70,5 +72,21 @@ def remediation_issue_id def self.short_ref_id(ref_id) ref_id.downcase[SHORT_REF_ID_RE] || ref_id end + + # rubocop:disable Metrics/ParameterLists + def self.from_parser(obj, existing: nil, rule_group_id: nil, + security_guide_id: nil, precedence: nil, value_checks: nil) + record = existing || new(ref_id: obj.id, security_guide_id: security_guide_id) + + record.op_source = obj + + record.assign_attributes(title: obj.title, description: obj.description, rationale: obj.rationale, + severity: obj.severity, precedence: precedence, rule_group_id: rule_group_id, + upstream: false, value_checks: value_checks, identifier: obj.identifier&.to_h, + references: obj.references.map(&:to_h), remediation_available: false) + + record + end + # rubocop:enable Metrics/ParameterLists end end diff --git a/app/models/v2/rule_group.rb b/app/models/v2/rule_group.rb index 694da54327..8ee2090dc2 100644 --- a/app/models/v2/rule_group.rb +++ b/app/models/v2/rule_group.rb @@ -16,5 +16,14 @@ class RuleGroup < ApplicationRecord searchable_by :title, %i[like unlike eq ne in notin] searchable_by :ref_id, %i[like unlike] + + def self.from_parser(obj, existing: nil, security_guide_id: nil, parent_id: nil, precedence: nil) + record = existing || new(ref_id: obj.id, security_guide_id: security_guide_id) + + record.assign_attributes(title: obj.title, description: obj.description, rationale: obj.rationale, + precedence: precedence, parent_id: parent_id) + + record + end end end diff --git a/app/models/v2/security_guide.rb b/app/models/v2/security_guide.rb index 22fe9fb916..8d7448f4fd 100644 --- a/app/models/v2/security_guide.rb +++ b/app/models/v2/security_guide.rb @@ -38,6 +38,12 @@ def self.os_versions reselect(:os_major_version).distinct.reorder(:os_major_version).map(&:os_major_version) end + def self.from_parser(obj) + record = find_or_initialize_by(ref_id: obj.id, version: obj.version) + record.assign_attributes(title: obj.title, description: obj.description) + record + end + # Builds the hierarchical structure of groups and rules def rule_tree cached_rules = rules.order(:precedence).select(:id, :rule_group_id).group_by(&:rule_group_id) diff --git a/app/models/v2/value_definition.rb b/app/models/v2/value_definition.rb index 6b66011742..ec755091e9 100644 --- a/app/models/v2/value_definition.rb +++ b/app/models/v2/value_definition.rb @@ -15,6 +15,8 @@ class ValueDefinition < ApplicationRecord searchable_by :title, %i[like unlike eq ne in notin] searchable_by :ref_id, %i[like unlike] + attr_accessor :op_source + def validate_value(value) return false unless value.is_a?(String) @@ -27,5 +29,13 @@ def validate_value(value) true end end + + def self.from_parser(obj, existing: nil, security_guide_id: nil) + record = existing || new(ref_id: obj.id, security_guide_id: security_guide_id) + record.op_source = obj + record.assign_attributes(title: obj.title, description: obj.description, + value_type: obj.type, default_value: obj.value) + record + end end end diff --git a/app/services/concerns/xccdf/benchmarks.rb b/app/services/concerns/xccdf/benchmarks.rb deleted file mode 100644 index fad163ba82..0000000000 --- a/app/services/concerns/xccdf/benchmarks.rb +++ /dev/null @@ -1,48 +0,0 @@ -# frozen_string_literal: true - -module Xccdf - # Methods related to saving xccdf benchmarks - module Benchmarks - extend ActiveSupport::Concern - - included do - def save_benchmark - benchmark.package_name = package_name - - return unless benchmark.new_record? || benchmark.package_name_changed? - - benchmark.save! - end - - def benchmark_saved? - benchmark.package_name == package_name && benchmark.persisted? - end - - def benchmark_profiles_saved? - benchmark.profiles.canonical.count == @op_benchmark.profiles.count - end - - def benchmark_rules_saved? - benchmark.rules.count == @op_benchmark.rules.count - end - - def benchmark_contents_equal_to_op? - return false if Settings.force_import_ssgs - - benchmark_saved? && benchmark_rules_saved? && benchmark_profiles_saved? - end - - def benchmark - @benchmark ||= ::Xccdf::Benchmark.from_openscap_parser(@op_benchmark) - end - - def package_name - @package_name ||= begin - SupportedSsg.by_os_major[benchmark.os_major_version].find do |item| - item.version == benchmark.version - end&.package - end - end - end - end -end diff --git a/app/services/concerns/xccdf/hosts.rb b/app/services/concerns/xccdf/hosts.rb index f87ca0af79..a98107d9c8 100644 --- a/app/services/concerns/xccdf/hosts.rb +++ b/app/services/concerns/xccdf/hosts.rb @@ -26,7 +26,7 @@ def test_result_profile name: @test_result_file.test_result.profile_id ).find_or_initialize_by( ref_id: @test_result_file.test_result.profile_id, - benchmark: benchmark + benchmark_id: security_guide.id ) end end diff --git a/app/services/concerns/xccdf/profile_os_minor_versions.rb b/app/services/concerns/xccdf/profile_os_minor_versions.rb index fb9a760734..d64343ea60 100644 --- a/app/services/concerns/xccdf/profile_os_minor_versions.rb +++ b/app/services/concerns/xccdf/profile_os_minor_versions.rb @@ -7,11 +7,11 @@ module ProfileOsMinorVersions included do def save_profile_os_minor_versions - ::ProfileOsMinorVersion.transaction do + ::V2::ProfileOsMinorVersion.transaction do # Delete all existing mappings for the given benchmark old_profile_os_minor_versions.delete_all # Import the new mappings - ::ProfileOsMinorVersion.import!(new_profile_os_minor_versions) + ::V2::ProfileOsMinorVersion.import!(new_profile_os_minor_versions) end end @@ -20,18 +20,18 @@ def save_profile_os_minor_versions def new_profile_os_minor_versions @profiles.flat_map do |profile| os_minor_versions.map do |os_minor_version| - ::ProfileOsMinorVersion.new(profile: profile, os_minor_version: os_minor_version) + ::V2::ProfileOsMinorVersion.new(profile: profile, os_minor_version: os_minor_version) end end end def old_profile_os_minor_versions - @old_profile_os_minor_versions ||= ::ProfileOsMinorVersion.where(profile: @profiles.map(&:id)) + @old_profile_os_minor_versions ||= ::V2::ProfileOsMinorVersion.where(profile: @profiles.map(&:id)) end def os_minor_versions - SupportedSsg.by_ssg_version(true)[@benchmark.version] - .select { |ssg| ssg.os_major_version == @benchmark.os_major_version } + SupportedSsg.by_ssg_version(true)[@security_guide.version] + .select { |ssg| ssg.os_major_version == @security_guide.os_major_version } .map(&:os_minor_version) end end diff --git a/app/services/concerns/xccdf/profile_rules.rb b/app/services/concerns/xccdf/profile_rules.rb index 6c5c2589f1..a53a4ddb27 100644 --- a/app/services/concerns/xccdf/profile_rules.rb +++ b/app/services/concerns/xccdf/profile_rules.rb @@ -7,15 +7,14 @@ module ProfileRules included do def save_profile_rules - ::ProfileRule.transaction do - ::ProfileRule.import!(profile_rules, - on_duplicate_key_update: { - conflict_target: %i[rule_id profile_id], - columns: %i[rule_id profile_id] - }) + ::V2::ProfileRule.transaction do + ::V2::ProfileRule.import!(profile_rules, + on_duplicate_key_update: { + conflict_target: %i[rule_id profile_id], + columns: %i[rule_id profile_id] + }) - base = ::ProfileRule.joins(profile: :benchmark) - .where('profiles.parent_profile_id' => nil) + base = ::V2::ProfileRule.joins(profile: :security_guide) profile_rule_links_to_remove(base).delete_all end @@ -36,7 +35,7 @@ def profile_rules def profile_rule_links_to_remove(base) grouped_rules = profile_rules.group_by(&:profile_id) - grouped_rules.reduce(ProfileRule.none) do |query, (profile_id, prs)| + grouped_rules.reduce(V2::ProfileRule.none) do |query, (profile_id, prs)| query.or( base.where(profile_id: profile_id) .where.not(rule_id: prs.map(&:rule_id)) diff --git a/app/services/concerns/xccdf/profiles.rb b/app/services/concerns/xccdf/profiles.rb index 3e72868183..43c62aa482 100644 --- a/app/services/concerns/xccdf/profiles.rb +++ b/app/services/concerns/xccdf/profiles.rb @@ -8,10 +8,10 @@ module Profiles included do def profiles @profiles ||= @op_profiles.map do |op_profile| - ::Profile.from_openscap_parser( + ::V2::Profile.from_parser( op_profile, existing: old_profiles[op_profile.id], - benchmark_id: @benchmark&.id, + security_guide_id: @security_guide.id, value_overrides: value_overrides(op_profile) ) end @@ -19,16 +19,15 @@ def profiles def save_profiles # Import the new records first with validation - ::Profile.import!(new_profiles, ignore: true) + ::V2::Profile.import!(new_profiles, ignore: true) # Update the fields on existing profiles, validation is not necessary - ::Profile.import(old_profiles.values, - on_duplicate_key_update: { - conflict_target: %i[ref_id benchmark_id], - columns: %i[name value_overrides], - index_predicate: 'parent_profile_id IS NULL' - }, - validate: false) + ::V2::Profile.import(old_profiles.values, + on_duplicate_key_update: { + conflict_target: %i[ref_id security_guide_id], + columns: %i[name value_overrides] + }, + validate: false) end private @@ -38,8 +37,8 @@ def new_profiles end def old_profiles - @old_profiles ||= ::Profile.where( - ref_id: @op_profiles.map(&:id), benchmark: @benchmark&.id, parent_profile_id: nil + @old_profiles ||= ::V2::Profile.where( + ref_id: @op_profiles.map(&:id), security_guide_id: @security_guide.id ).index_by(&:ref_id) end diff --git a/app/services/concerns/xccdf/rule_group_relationships.rb b/app/services/concerns/xccdf/rule_group_relationships.rb deleted file mode 100644 index e7d3eef935..0000000000 --- a/app/services/concerns/xccdf/rule_group_relationships.rb +++ /dev/null @@ -1,54 +0,0 @@ -# frozen_string_literal: true - -module Xccdf - # Methods related to saving RuleGroupRelationships - module RuleGroupRelationships - extend ActiveSupport::Concern - included do - def save_rule_group_relationships - @op_rules_and_rule_groups = @op_rule_groups + @op_rules - - ::RuleGroupRelationship.import!( - rule_group_relationships.select(&:new_record?), ignore: true - ) - - rgr_links_to_remove(::RuleGroupRelationship).delete_all - end - - private - - def rule_group_relationships - @rule_group_relationships ||= @op_rules_and_rule_groups.flat_map do |op_r_or_rg| - %i[conflicts requires].flat_map { |type| with_relationship(op_r_or_rg, type) } - end.compact - end - - def with_relationship(entity, type) - left = rule_or_rule_group_for(ref_id: entity.id) - entity.send(type).map do |relationship_ref_id| - right = rule_or_rule_group_for(ref_id: relationship_ref_id) - next unless right - - ::RuleGroupRelationship.find_or_initialize_by( - left: left, right: right, relationship: type - ) - end - end - - def rgr_links_to_remove(base) - grouped_by_rgr = rule_group_relationships&.group_by(&:left_id) - grouped_by_rgr&.reduce(RuleGroupRelationship.none) do |query, (left_id, rgr)| - query.or( - base.where(left_id: left_id) - .where.not(right_id: rgr.map(&:right_id)) - .where.not(relationship: rgr.map(&:relationship)) - ) - end - end - - def rule_or_rule_group_for(ref_id:) - rule_for(ref_id: ref_id) || rule_group_for(ref_id: ref_id) - end - end - end -end diff --git a/app/services/concerns/xccdf/rule_groups.rb b/app/services/concerns/xccdf/rule_groups.rb index eb86c249e6..3bb97cc43b 100644 --- a/app/services/concerns/xccdf/rule_groups.rb +++ b/app/services/concerns/xccdf/rule_groups.rb @@ -7,31 +7,35 @@ module RuleGroups included do def save_rule_groups - @rule_groups ||= @op_rule_groups.each_with_index.map do |op_rule_group, idx| - ::RuleGroup.from_openscap_parser(op_rule_group, - existing: old_rule_groups[op_rule_group.id], - precedence: idx, benchmark_id: @benchmark&.id) - end - - ::RuleGroup.import!(new_rule_groups, ignore: true) + ::V2::RuleGroup.import!(new_rule_groups, ignore: true) # Overwite a superset of old_rule_groups because the IDs of the ancestors are not # available in the first import! above - ::RuleGroup.import(rule_groups_with_ancestry, on_duplicate_key_update: { - conflict_target: %i[ref_id benchmark_id], - columns: %i[description rationale precedence ancestry] - }, validate: false) + ::V2::RuleGroup.import(rule_groups_with_ancestry, on_duplicate_key_update: { + conflict_target: %i[ref_id security_guide_id], + columns: %i[description rationale precedence ancestry] + }, validate: false) end private + def rule_groups + @rule_groups ||= @op_rule_groups.each_with_index.map do |op_rule_group, idx| + ::V2::RuleGroup.from_parser( + op_rule_group, + existing: old_rule_groups[op_rule_group.id], precedence: idx, + security_guide_id: @security_guide.id + ) + end + end + def new_rule_groups - @new_rule_groups ||= @rule_groups.select(&:new_record?) + @new_rule_groups ||= rule_groups.select(&:new_record?) end def old_rule_groups - @old_rule_groups ||= ::RuleGroup.where( - ref_id: @op_rule_groups.map(&:id), benchmark: @benchmark&.id + @old_rule_groups ||= ::V2::RuleGroup.where( + ref_id: @op_rule_groups.map(&:id), security_guide_id: @security_guide.id ).index_by(&:ref_id) end @@ -50,7 +54,7 @@ def rule_groups_with_ancestry end def rule_group_for(ref_id:) - @cached_rule_groups ||= @rule_groups.index_by(&:ref_id) + @cached_rule_groups ||= rule_groups.index_by(&:ref_id) @cached_rule_groups[ref_id] end end diff --git a/app/services/concerns/xccdf/rule_references_containers.rb b/app/services/concerns/xccdf/rule_references_containers.rb deleted file mode 100644 index d1e205ecd5..0000000000 --- a/app/services/concerns/xccdf/rule_references_containers.rb +++ /dev/null @@ -1,45 +0,0 @@ -# frozen_string_literal: true - -module Xccdf - # Methods related to saving rule references - module RuleReferencesContainers - extend ActiveSupport::Concern - - included do - def rule_references_containers - @rule_references_containers = @op_rules.map do |op_rule| - rule_id = rule_for(ref_id: op_rule.id).id - ::RuleReferencesContainer.from_openscap_parser( - op_rule, - existing: old_rule_references_containers[rule_id], - rule_id: rule_id - ) - end - end - - def save_rule_references_containers - # Import the new records first with validation - ::RuleReferencesContainer.import!(new_rule_references_containers, ignore: true) - - # Update the fields on existing rules, validation is not necessary - ::RuleReferencesContainer.import(old_rule_references_containers.values, - on_duplicate_key_update: { - conflict_target: %i[rule_id], - columns: %i[rule_references] - }, validate: false) - end - - private - - def old_rule_references_containers - @old_rule_references_containers ||= ::RuleReferencesContainer.where( - rule_id: rules.map(&:id) - ).index_by(&:rule_id) - end - - def new_rule_references_containers - @new_rule_references_containers ||= rule_references_containers.select(&:new_record?) - end - end - end -end diff --git a/app/services/concerns/xccdf/rule_results.rb b/app/services/concerns/xccdf/rule_results.rb index c5c829d0eb..005b632bd1 100644 --- a/app/services/concerns/xccdf/rule_results.rb +++ b/app/services/concerns/xccdf/rule_results.rb @@ -41,7 +41,7 @@ def test_result_rules_unknown def rule_ids @rule_ids ||= Rule.where( - benchmark: benchmark, ref_id: selected_op_rule_results.map(&:id) + benchmark_id: security_guide.id, ref_id: selected_op_rule_results.map(&:id) ).pluck(:ref_id, :id).to_h end end diff --git a/app/services/concerns/xccdf/rules.rb b/app/services/concerns/xccdf/rules.rb index 07efa056ec..a5c443beb7 100644 --- a/app/services/concerns/xccdf/rules.rb +++ b/app/services/concerns/xccdf/rules.rb @@ -11,25 +11,26 @@ def rules rule_group = rule_group_for(ref_id: op_rule.parent_id) value_checks = op_rule.values.map { |value_ref_id| value_definition_for(ref_id: value_ref_id).id } - ::Rule.from_openscap_parser( + ::V2::Rule.from_parser( op_rule, existing: old_rules[op_rule.id], precedence: idx, rule_group_id: rule_group&.id, value_checks: value_checks, - benchmark_id: @benchmark&.id + security_guide_id: @security_guide&.id ) end end def save_rules # Import the new records first with validation - ::Rule.import!(new_rules, ignore: true) + ::V2::Rule.import!(new_rules, ignore: true) # Update the fields on existing rules, validation is not necessary - ::Rule.import(old_rules.values, - on_duplicate_key_update: { - conflict_target: %i[ref_id benchmark_id], - columns: %i[identifier description precedence rationale rule_group_id severity value_checks] - }, validate: false) + ::V2::Rule.import(old_rules.values, + on_duplicate_key_update: { + conflict_target: %i[ref_id security_guide_id], + columns: %i[identifier references description precedence rationale + rule_group_id severity value_checks] + }, validate: false) end private @@ -44,8 +45,8 @@ def rule_for(ref_id:) end def old_rules - @old_rules ||= ::Rule.where( - ref_id: @op_rules.map(&:id), benchmark_id: @benchmark&.id + @old_rules ||= ::V2::Rule.where( + ref_id: @op_rules.map(&:id), security_guide_id: @security_guide.id ).index_by(&:ref_id) end end diff --git a/app/services/concerns/xccdf/security_guides.rb b/app/services/concerns/xccdf/security_guides.rb new file mode 100644 index 0000000000..9c71911452 --- /dev/null +++ b/app/services/concerns/xccdf/security_guides.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module Xccdf + # Methods related to saving xccdf security guides + module SecurityGuides + extend ActiveSupport::Concern + + included do + def save_security_guide + security_guide.package_name = package_name + + return unless security_guide.new_record? || security_guide.package_name_changed? + + security_guide.save! + end + + def security_guide_saved? + security_guide.package_name == package_name && security_guide.persisted? + end + + def security_guide_profiles_saved? + security_guide.profiles.canonical.count == @op_security_guide.profiles.count + end + + def security_guide_rules_saved? + security_guide.rules.count == @op_security_guide.rules.count + end + + def security_guide_contents_equal_to_op? + return false if Settings.force_import_ssgs + + security_guide_saved? && security_guide_rules_saved? && security_guide_profiles_saved? + end + + def security_guide + @security_guide ||= ::V2::SecurityGuide.from_parser(@op_security_guide) + end + + def package_name + @package_name ||= begin + SupportedSsg.by_os_major[security_guide.ref_id[/(?<=RHEL-)\d/]].find do |item| + item.version == security_guide.version + end&.package + end + end + end + end +end diff --git a/app/services/concerns/xccdf/util.rb b/app/services/concerns/xccdf/util.rb index f62bd8e48e..25d3a309a3 100644 --- a/app/services/concerns/xccdf/util.rb +++ b/app/services/concerns/xccdf/util.rb @@ -6,32 +6,28 @@ module Util extend ActiveSupport::Concern included do - include ::Xccdf::Benchmarks + include ::Xccdf::SecurityGuides include ::Xccdf::Profiles include ::Xccdf::Rules include ::Xccdf::RuleGroups include ::Xccdf::ValueDefinitions include ::Xccdf::ProfileRules include ::Xccdf::ProfileOsMinorVersions - include ::Xccdf::RuleReferencesContainers - include ::Xccdf::RuleGroupRelationships include ::Xccdf::Hosts include ::Xccdf::RuleResults include ::Xccdf::TestResult # rubocop:disable Metrics/MethodLength - def save_all_benchmark_info - return if benchmark_contents_equal_to_op? + def save_all_security_guide_info + return if security_guide_contents_equal_to_op? - save_benchmark + save_security_guide save_value_definitions save_profiles save_rule_groups save_rules - save_rule_group_relationships save_profile_rules save_profile_os_minor_versions - save_rule_references_containers end # rubocop:enable Metrics/MethodLength diff --git a/app/services/concerns/xccdf/value_definitions.rb b/app/services/concerns/xccdf/value_definitions.rb index d7fadf9f03..bbf5dc973e 100644 --- a/app/services/concerns/xccdf/value_definitions.rb +++ b/app/services/concerns/xccdf/value_definitions.rb @@ -8,24 +8,24 @@ module ValueDefinitions included do def value_definitions @value_definitions ||= @op_value_definitions.map do |op_value_definition| - ::ValueDefinition.from_openscap_parser( + ::V2::ValueDefinition.from_parser( op_value_definition, existing: old_value_definitions[op_value_definition.id], - benchmark_id: @benchmark&.id + security_guide_id: @security_guide&.id ) end end def save_value_definitions # Import the new records first with validation - ::ValueDefinition.import!(new_value_definitions, ignore: true) + ::V2::ValueDefinition.import!(new_value_definitions, ignore: true) # Update the fields on existing value_definitions, validation is not necessary - ::ValueDefinition.import(old_value_definitions.values, - on_duplicate_key_update: { - conflict_target: %i[ref_id benchmark_id], - columns: %i[description default_value] - }, validate: false) + ::V2::ValueDefinition.import(old_value_definitions.values, + on_duplicate_key_update: { + conflict_target: %i[ref_id security_guide_id], + columns: %i[description default_value] + }, validate: false) end private @@ -35,8 +35,8 @@ def new_value_definitions end def old_value_definitions - @old_value_definitions ||= ::ValueDefinition.where( - ref_id: @op_value_definitions.map(&:id), benchmark_id: @benchmark&.id + @old_value_definitions ||= ::V2::ValueDefinition.where( + ref_id: @op_value_definitions.map(&:id), security_guide_id: @security_guide.id ).index_by(&:ref_id) end diff --git a/app/services/datastream_importer.rb b/app/services/datastream_importer.rb index f8b1cc1012..fd657cc132 100644 --- a/app/services/datastream_importer.rb +++ b/app/services/datastream_importer.rb @@ -6,18 +6,17 @@ class DatastreamImporter include ::Xccdf::Datastreams def initialize(datastream_filename) - @op_benchmark = op_datastream_file(datastream_filename).benchmark - @op_profiles = @op_benchmark.profiles - @op_rule_groups = @op_benchmark.groups - @op_rules = @op_benchmark.rules - @op_value_definitions = @op_benchmark.values - @op_rule_references = - @op_benchmark.rule_references.reject { |rr| rr.label.empty? } + @op_security_guide = op_datastream_file(datastream_filename).benchmark + @op_profiles = @op_security_guide.profiles + @op_rule_groups = @op_security_guide.groups + @op_rules = @op_security_guide.rules + @op_value_definitions = @op_security_guide.values + @op_rule_references = @op_security_guide.rule_references.reject { |rr| rr.label.empty? } end def import! - Xccdf::Benchmark.transaction do - save_all_benchmark_info + ::V2::SecurityGuide.transaction do + save_all_security_guide_info end end end diff --git a/test/services/concerns/xccdf/benchmarks_test.rb b/test/services/concerns/xccdf/benchmarks_test.rb deleted file mode 100644 index a653763b39..0000000000 --- a/test/services/concerns/xccdf/benchmarks_test.rb +++ /dev/null @@ -1,139 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -module Xccdf - # A class to test Xccdf::Benchmarks - class BenchmarksTest < ActiveSupport::TestCase - OP_BENCHMARK = OpenStruct.new(id: '1', version: 'v0.1.49', - title: 'one', description: 'first', - profiles: ['profile-mock1'], - rules: ['rule-mock1']) - - class Mock - include Xccdf::Util - - def initialize(op_benchmark) - @op_benchmark = op_benchmark - end - - def rules - ['rule-mock'] - end - end - - setup do - PolicyHost.any_instance.stubs(:host_supported?).returns(true) - end - - test 'save_benchmark' do - mock = Mock.new(OP_BENCHMARK) - ::Xccdf::Benchmark.any_instance.expects(:rules) - .returns(['rule-mock1']).at_least_once - ::Xccdf::Benchmark.any_instance.expects(:profiles) - .returns(stub(canonical: ['profile-mock1'])) - .at_least_once - - SupportedSsg.expects(:by_os_major).returns( - nil => [ - OpenStruct.new( - version: 'v0.1.49', - package: 'foo' - ) - ] - ).at_least_once - - assert_difference('Xccdf::Benchmark.count', 1) do - mock.save_benchmark - end - assert mock.benchmark_contents_equal_to_op? - end - - test 'does not try to save an existing benchmark' do - mock = Mock.new(OP_BENCHMARK) - ::Xccdf::Benchmark.any_instance.expects(:rules) - .returns(['rule-mock1']).at_least_once - ::Xccdf::Benchmark.any_instance.expects(:profiles) - .returns(stub(canonical: ['profile-mock1'])) - .at_least_once - - SupportedSsg.expects(:by_os_major).returns( - nil => [ - OpenStruct.new( - version: 'v0.1.49', - package: nil - ) - ] - ).at_least_once - - mock.save_benchmark - assert mock.benchmark_contents_equal_to_op? - - mock.expects(:save_benchmark).never - assert_no_difference('Xccdf::Benchmark.count') do - mock.save_all_benchmark_info - end - assert mock.benchmark_contents_equal_to_op? - end - - test 'benchmark is not saved if rules count differ' do - mock = Mock.new(OP_BENCHMARK) - assert_not_equal mock.benchmark.rules.count, OP_BENCHMARK.rules.count - - SupportedSsg.expects(:by_os_major).returns( - nil => [ - OpenStruct.new( - version: 'v0.1.49', - package: nil - ) - ] - ) - - assert_not mock.benchmark_contents_equal_to_op? - end - - test 'benchmark is not saved if package_name differs' do - mock = Mock.new(OP_BENCHMARK) - ::Xccdf::Benchmark.any_instance.expects(:rules) - .returns(['rule-mock1']).at_least_once - ::Xccdf::Benchmark.any_instance.expects(:profiles) - .returns(stub(canonical: ['profile-mock1'])) - .at_least_once - - SupportedSsg.expects(:by_os_major).returns( - nil => [ - OpenStruct.new( - version: 'v0.1.49', - package: 'foo' - ) - ] - ).at_least_once - - mock.save_benchmark - assert mock.benchmark_contents_equal_to_op? - - mock.instance_variable_get(:@benchmark).update(package_name: 'bar') - - assert_not mock.benchmark_contents_equal_to_op? - end - - test 'benchmark is not saved if profiles count differ' do - mock = Mock.new(OP_BENCHMARK) - ::Xccdf::Benchmark.any_instance.expects(:profiles) - .returns(%w[profiles-mock1 mock2]).at_least_once - - SupportedSsg.expects(:by_os_major).returns( - nil => [ - OpenStruct.new( - version: 'v0.1.49', - package: nil - ) - ] - ).at_least_once - - assert_not_equal mock.benchmark.profiles.count, - OP_BENCHMARK.profiles.count - assert_not mock.benchmark_contents_equal_to_op? - end - end -end diff --git a/test/services/concerns/xccdf/profile_os_minor_versions_test.rb b/test/services/concerns/xccdf/profile_os_minor_versions_test.rb deleted file mode 100644 index 6f62d65dba..0000000000 --- a/test/services/concerns/xccdf/profile_os_minor_versions_test.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -module Xccdf - # A class to test Xccdf::ProfileOsMinorVersions - class ProfileOsMinorVersionsTest < ActiveSupport::TestCase - # Mock class for testing - class Mock - include Xccdf::Datastreams - include Xccdf::Benchmarks - include Xccdf::Profiles - include Xccdf::ValueDefinitions - include Xccdf::ProfileOsMinorVersions - - # attr_reader :benchmark - - def initialize(datastream_filename) - @op_benchmark = op_datastream_file(datastream_filename).benchmark - @op_profiles = @op_benchmark.profiles - @op_value_definitions = @op_benchmark.values - end - end - - setup do - @mock = Mock.new(file_fixture('ssg-rhel7-ds.xml')) - @mock.save_benchmark - @mock.save_value_definitions - @mock.save_profiles - end - - test 'save the support matrix' do - SupportedSsg.expects(:by_ssg_version).returns( - @mock.benchmark.version => [ - OpenStruct.new( - os_major_version: @mock.benchmark.os_major_version, - os_minor_version: '4' - ), - OpenStruct.new( - os_major_version: @mock.benchmark.os_major_version, - os_minor_version: '5' - ) - ] - ).at_least_once - - @mock.save_profile_os_minor_versions - - assert_equal(ProfileOsMinorVersion.count, @mock.profiles.count * 2) - end - - test 'removes old records from the support matrix' do - SupportedSsg.expects(:by_ssg_version).returns( - @mock.benchmark.version => [ - OpenStruct.new( - os_major_version: @mock.benchmark.os_major_version, - os_minor_version: '4' - ) - ] - ).at_least_once - - @mock.save_profile_os_minor_versions - assert_equal(ProfileOsMinorVersion.where(os_minor_version: 4).count, @mock.profiles.count) - assert_equal(ProfileOsMinorVersion.where(os_minor_version: 5).count, 0) - - SupportedSsg.unstub(:by_ssg_version) - SupportedSsg.expects(:by_ssg_version).returns( - @mock.benchmark.version => [ - OpenStruct.new( - os_major_version: @mock.benchmark.os_major_version, - os_minor_version: '5' - ) - ] - ).at_least_once - - @mock.save_profile_os_minor_versions - assert_equal(ProfileOsMinorVersion.where(os_minor_version: 4).count, 0) - assert_equal(ProfileOsMinorVersion.where(os_minor_version: 5).count, @mock.profiles.count) - end - end -end diff --git a/test/services/concerns/xccdf/profile_rules_test.rb b/test/services/concerns/xccdf/profile_rules_test.rb deleted file mode 100644 index d42ef42378..0000000000 --- a/test/services/concerns/xccdf/profile_rules_test.rb +++ /dev/null @@ -1,53 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -module Xccdf - # A class to test Xccdf::Profiles - class ProfileRulessTest < ActiveSupport::TestCase - include Xccdf::ProfileRules - - setup do - @account = FactoryBot.create(:account) - @host = FactoryBot.create(:host, org_id: @account.org_id) - @benchmark = FactoryBot.create(:canonical_profile).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('xccdf_report.xml').read - ) - @op_profiles = parser.benchmark.profiles - @profiles = @op_profiles.map do |op_profile| - ::Profile.from_openscap_parser(op_profile, benchmark_id: @benchmark&.id) - end - ::Profile.import!(@profiles, ignore: true) - @op_rules = parser.benchmark.rules - rule_group = FactoryBot.create(:rule_group, benchmark: @benchmark) - @rules = @op_rules.map do |op_rule| - ::Rule.from_openscap_parser(op_rule, benchmark_id: @benchmark&.id, rule_group_id: rule_group.id) - end - ::Rule.import!(@rules, ignore: true) - end - - test 'saves profile rules only once' do - assert_difference('ProfileRule.count', 74) do - save_profile_rules - end - - assert_no_difference('ProfileRule.count') do - save_profile_rules - end - end - - test 'updates profile rule connections' do - rule1 = FactoryBot.create(:rule, benchmark: @benchmark) - rule2 = ::Rule.find_by(ref_id: 'xccdf_org.ssgproject.content_rule_disable_prelink', benchmark: @benchmark) - @profiles.first.update(rules: [rule1, rule2]) - - assert_difference('ProfileRule.count', 72) do - save_profile_rules - end - - assert_nil ProfileRule.find_by(rule: rule1, profile: @profiles.first) - assert ProfileRule.find_by(rule: rule2, profile: @profiles.first) - end - end -end diff --git a/test/services/concerns/xccdf/profiles_test.rb b/test/services/concerns/xccdf/profiles_test.rb deleted file mode 100644 index a918f33395..0000000000 --- a/test/services/concerns/xccdf/profiles_test.rb +++ /dev/null @@ -1,57 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -module Xccdf - # A class to test Xccdf::Profiles - class ProfilesTest < ActiveSupport::TestCase - def test_result - OpenStruct.new(id: ['xccdf_org.ssgproject.content_profile_standard']) - end - - def report_description - 'description' - end - - include Xccdf::Profiles - include Xccdf::ValueDefinitions - - setup do - @account = FactoryBot.create(:account) - @host = FactoryBot.create(:host, org_id: @account.org_id) - @benchmark = FactoryBot.create(:canonical_profile, :with_rules).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('rhel-xccdf-report.xml').read - ) - @op_profiles = parser.benchmark.profiles - @op_value_definitions = parser.benchmark.values - @op_test_result = parser.test_result - save_value_definitions - end - - test 'save_profiles' do - assert_difference('Profile.count', 10) do - save_profiles - end - - assert_no_difference('Profile.count') do - save_profiles - end - end - - test 'updates value_overrides' do - save_profiles - - Profile.update(value_overrides: {}) - assert_equal Profile.where(value_overrides: {}).count, 11 - - @profiles = nil - @new_profiles = nil - @old_profiles = nil - - save_profiles - - assert_equal Profile.where.not(value_overrides: {}).count, 9 - end - end -end diff --git a/test/services/concerns/xccdf/rule_group_relationships_test.rb b/test/services/concerns/xccdf/rule_group_relationships_test.rb deleted file mode 100644 index 7ef74b2c4e..0000000000 --- a/test/services/concerns/xccdf/rule_group_relationships_test.rb +++ /dev/null @@ -1,112 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class RuleGroupRelationshipsTest < ActiveSupport::TestCase - include Xccdf::Profiles - include Xccdf::Rules - include Xccdf::RuleGroups - include Xccdf::ProfileRules - include Xccdf::RuleGroupRelationships - - attr_accessor :benchmark, :account, :op_profiles, :op_rules, :rules, - :rule_groups, :op_rule_groups, :rule_groups_with_parents, - :op_rules_and_rule_groups - - context 'without any required or conflicting rules or rule groups' do - setup do - @account = FactoryBot.create(:account) - @host = FactoryBot.create(:host, org_id: @account.org_id) - @benchmark = FactoryBot.create(:canonical_profile).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('rhel-xccdf-report.xml').read - ) - @op_rules = parser.benchmark.rules - @op_rule_groups = parser.benchmark.groups - @rule_groups = @op_rule_groups.map do |op_rule_group| - ::RuleGroup.from_openscap_parser(op_rule_group, benchmark_id: @benchmark&.id) - end - ::RuleGroup.import!(@rule_groups, ignore: true) - @rules = @op_rules.map do |op_rule| - ::Rule.from_openscap_parser(op_rule, benchmark_id: @benchmark&.id, rule_group_id: @rule_groups.first.id) - end - ::Rule.import!(@rules, ignore: true) - @op_rules_and_rule_groups = @op_rules + @op_rule_groups - end - - should 'save no rule group relationships when there are no required or conflicting rules or rule groups' do - assert_no_difference('RuleGroupRelationship.count') do - save_rule_group_relationships - end - end - - should 'return empty array are no required rules or rule groups' do - required_rule_group_relationships = send(:rule_group_relationships) - assert_equal [], required_rule_group_relationships - end - end - - context 'with required and conflicting rules or rule groups' do - setup do - @account = FactoryBot.create(:account) - @host = FactoryBot.create(:host, org_id: @account.org_id) - @benchmark = FactoryBot.create(:canonical_profile).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('xccdf_report.xml').read - ) - @op_rules = parser.benchmark.rules - @op_rule_groups = parser.benchmark.groups - @rule_groups = @op_rule_groups.map do |op_rule_group| - ::RuleGroup.from_openscap_parser(op_rule_group, benchmark_id: @benchmark&.id) - end - ::RuleGroup.import!(@rule_groups, ignore: true) - @rules = @op_rules.map do |op_rule| - ::Rule.from_openscap_parser(op_rule, benchmark_id: @benchmark&.id, rule_group_id: @rule_groups.first.id) - end - ::Rule.import!(@rules, ignore: true) - @op_rules_and_rule_groups = @op_rules + @op_rule_groups - end - - should 'save rule group rules with required and conflicting rules and rule groups' do - assert_difference('RuleGroupRelationship.count', 8) do - save_rule_group_relationships - end - end - - should 'save rule group rules only once' do - assert_difference('RuleGroupRelationship.count', 8) do - save_rule_group_relationships - end - - assert_no_difference('RuleGroupRelationship.count') do - save_rule_group_relationships - end - end - - should 'save a rule group rule for all required rules or rule groups' do - required_rule_group_relationships = send(:rule_group_relationships) - assert_equal 8, required_rule_group_relationships.count - end - - should 'delete rule_group_relationships that are no longer relevant' do - rule_groups_by_ref_id = @rule_groups.index_by(&:ref_id) - rg = rule_groups_by_ref_id['xccdf_org.ssgproject.content_group_disabling_squid'] - rgr = FactoryBot.create(:rule_group_relationship, :for_rule_group_and_rule_requires) - rgr.update!(left: rg, right: @rules.first) - assert_not_nil RuleGroupRelationship.where(id: rgr.id).first - save_rule_group_relationships - assert_nil RuleGroupRelationship.where(id: rgr.id).first - end - - should 'not delete rule_group_relationships that are still relevant' do - rule_groups_by_ref_id = @rule_groups.index_by(&:ref_id) - rg = rule_groups_by_ref_id['xccdf_org.ssgproject.content_group_disabling_squid'] - required_rg = rule_groups_by_ref_id['xccdf_org.ssgproject.content_group_openstack'] - rgr = FactoryBot.create(:rule_group_relationship, :for_rule_group_and_rule_conflicts) - rgr.update!(left: rg, right: required_rg) - assert_not_nil RuleGroupRelationship.where(id: rgr.id).first - save_rule_group_relationships - assert_not_nil RuleGroupRelationship.where(id: rgr.id).first - end - end -end diff --git a/test/services/concerns/xccdf/rule_groups_test.rb b/test/services/concerns/xccdf/rule_groups_test.rb deleted file mode 100644 index 257b066d7a..0000000000 --- a/test/services/concerns/xccdf/rule_groups_test.rb +++ /dev/null @@ -1,92 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class RuleGroupsTest < ActiveSupport::TestCase - include Xccdf::Profiles - include Xccdf::Rules - include Xccdf::RuleGroups - include Xccdf::ProfileRules - - attr_accessor :benchmark, :account, :op_profiles, :op_rules, :rules, - :rule_groups, :op_rule_groups, :rule_groups_with_parents - - setup do - @account = FactoryBot.create(:account) - @host = FactoryBot.create(:host, org_id: @account.org_id) - @benchmark = FactoryBot.create(:canonical_profile).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('rhel-xccdf-report.xml').read - ) - @op_rules = parser.benchmark.rules - @op_rule_groups = parser.benchmark.groups - end - - test 'save all rule groups as new' do - assert_difference('RuleGroup.count', 232) do - save_rule_groups - end - end - - test 'returns rule groups saved in the report' do - rule_group = RuleGroup.from_openscap_parser(@op_rule_groups.sample, - benchmark_id: @benchmark.id) - assert rule_group.save - save_rule_groups - assert_includes @rule_groups, rule_group - end - - test 'update ancestry column for rule group with ancestors' do - save_rule_groups - - assert_equal 228, @rule_groups.reject { |rg| rg.ancestry == '' }.count - assert_equal 4, @rule_groups.select { |rg| rg.ancestry == '' }.count - - rg_with_ancestors = @rule_groups.select { |rg| rg.ref_id == 'xccdf_org.ssgproject.content_group_root_logins' }.first - parent1 = rg_with_ancestors.parent - parent2 = parent1.parent - root = parent2.parent - rg_ancestor_ids = rg_with_ancestors.ancestors.map(&:id) - - assert_equal "#{root.id}/#{parent2.id}/#{parent1.id}", rg_with_ancestors.ancestry - assert_nil root.parent - assert_equal '', @rule_groups[0].ancestry - assert_equal @rule_groups[2].parent.id.to_s, @rule_groups[2].ancestry - assert_includes rg_ancestor_ids, root.id - assert_includes rg_ancestor_ids, parent2.id - assert_includes rg_ancestor_ids, parent1.id - end - - test 'saves rule groups only once' do - assert_difference('RuleGroup.count', 232) do - save_rule_groups - end - assert_equal 228, @rule_groups.reject { |rg| rg.ancestry == '' }.count - assert_equal 4, @rule_groups.select { |rg| rg.ancestry == '' }.count - - @new_rule_groups = nil - - assert_no_difference('RuleGroup.count') do - save_rule_groups - end - assert_equal 228, @rule_groups.reject { |rg| rg.ancestry == '' }.count - assert_equal 4, @rule_groups.select { |rg| rg.ancestry == '' }.count - end - - test 'reorders rule groups when needed' do - save_rule_groups - before = @benchmark.rule_groups.order(:precedence).pluck(:ref_id) - - @rule_groups = nil - @old_rule_groups = nil - @new_rule_groups = nil - @cached_rule_groups = nil - @op_rule_groups.reverse! - - save_rule_groups - - after = @benchmark.rule_groups.order(:precedence).pluck(:ref_id) - - assert_equal before, after.reverse - end -end diff --git a/test/services/concerns/xccdf/rule_references_containers_test.rb b/test/services/concerns/xccdf/rule_references_containers_test.rb deleted file mode 100644 index e58dbb3ee8..0000000000 --- a/test/services/concerns/xccdf/rule_references_containers_test.rb +++ /dev/null @@ -1,64 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class RuleReferencesContainersTest < ActiveSupport::TestCase - class Mock - include Xccdf::Profiles - include Xccdf::Rules - include Xccdf::RuleGroups - include Xccdf::ValueDefinitions - include Xccdf::RuleReferencesContainers - - attr_accessor :benchmark, :account, :op_profiles, :op_rules - - def initialize(report_contents) - test_result_file = OpenscapParser::TestResultFile.new(report_contents) - @op_benchmark = test_result_file.benchmark - @op_test_result = test_result_file.test_result - @op_rules = @op_benchmark.rules - @op_profiles = @op_benchmark.profiles - @op_rule_groups = @op_benchmark.groups - @op_value_definitions = @op_benchmark.values - end - end - - setup do - @mock = Mock.new(file_fixture('xccdf_report.xml').read) - @mock.benchmark = FactoryBot.create( - :canonical_profile, :with_rules - ).benchmark - @mock.account = FactoryBot.create(:account) - @mock.save_rule_groups - @mock.save_value_definitions - @mock.save_rules - end - - test 'save all rule references containers as new' do - assert_difference('RuleReferencesContainer.count', 367) do - @mock.save_rule_references_containers - end - end - - test 'returns rule references containers saved in the report' do - rrc = RuleReferencesContainer.from_openscap_parser(@mock.op_rules.first, - rule_id: @mock.rules.first.id) - assert rrc.save - @mock.save_rule_references_containers - assert_includes @mock.rule_references_containers, rrc - end - - test 'updates rule_references field when needed' do - @mock.save_rule_references_containers - - rule = @mock.rules.first - - @mock.instance_variable_set(:@rule_references_containers, nil) - @mock.instance_variable_set(:@old_rule_references_containers, nil) - @mock.instance_variable_set(:@new_rule_references_containers, nil) - @mock.instance_variable_get(:@op_rules).first.stubs(:references).returns([{ foo: 'bar' }]) - @mock.save_rule_references_containers - - assert_equal RuleReferencesContainer.find_by(rule_id: rule.id).rule_references, [{ 'foo' => 'bar' }] - end -end diff --git a/test/services/concerns/xccdf/rules_test.rb b/test/services/concerns/xccdf/rules_test.rb deleted file mode 100644 index 0e55265b4d..0000000000 --- a/test/services/concerns/xccdf/rules_test.rb +++ /dev/null @@ -1,141 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class RulesTest < ActiveSupport::TestCase - class Mock - include Xccdf::Profiles - include Xccdf::Rules - include Xccdf::RuleGroups - include Xccdf::ValueDefinitions - include Xccdf::ProfileRules - - attr_accessor :benchmark, :account, :op_profiles, :op_rules - - def initialize(report_contents) - test_result_file = OpenscapParser::TestResultFile.new(report_contents) - @op_benchmark = test_result_file.benchmark - @op_test_result = test_result_file.test_result - @op_rules = @op_benchmark.rules - @op_value_definitions = @op_benchmark.values - @op_profiles = @op_benchmark.profiles - @op_rule_groups = @op_benchmark.groups - end - end - - setup do - @mock = Mock.new(file_fixture('xccdf_report.xml').read) - @mock.benchmark = FactoryBot.create( - :canonical_profile, :with_rules - ).benchmark - RuleReferencesContainer.delete_all - @mock.account = FactoryBot.create(:account) - @mock.save_rule_groups - @mock.save_value_definitions - end - - test 'save all rules as new' do - assert_difference('Rule.count', 367) do - @mock.save_rules - end - end - - test 'returns rules saved in the report' do - rule = Rule.from_openscap_parser(@mock.op_rules.sample, - benchmark_id: @mock.benchmark.id, - rule_group_id: @mock.benchmark.rule_groups.first.id) - assert rule.save - @mock.save_rules - assert_includes @mock.rules, rule - end - - test 'correctly saves value_checks' do - @mock.save_rules - rule1 = Rule.find_by(ref_id: 'xccdf_org.ssgproject.content_rule_network_ipv6_disable_interfaces') - rule2 = Rule.find_by(ref_id: 'xccdf_org.ssgproject.content_rule_sshd_set_idle_timeout') - value1 = ValueDefinition.find_by(ref_id: 'xccdf_org.ssgproject.content_value_sshd_required') - value2 = ValueDefinition.find_by(ref_id: 'xccdf_org.ssgproject.content_value_sshd_idle_timeout_value') - - assert_includes rule2.value_checks, value1.id - assert_includes rule2.value_checks, value2.id - assert_equal 2, rule2.value_checks.length - assert_equal [], rule1.value_checks - end - - test 'save all rules and add profiles to pre existing one' do - profile = Profile.create(ref_id: @mock.op_profiles.first.id, - name: @mock.op_profiles.first.name, - benchmark: @mock.benchmark) - assert_no_difference('Profile.count') do # only 1 profile in this benchmark - @mock.save_profiles - end - rule = Rule.from_openscap_parser(@mock.op_rules.find do |r| - r.id == @mock.op_profiles.first.selected_rule_ids.first - end, benchmark_id: @mock.benchmark.id, rule_group_id: @mock.benchmark.rule_groups.first.id) - assert rule.save - assert_difference('Rule.count', 366) do - @mock.save_rules - end - - assert_difference('ProfileRule.count', 74) do - @mock.save_profile_rules - end - - assert_includes rule.profiles.pluck(:id), profile.id - end - - test 'reorders rules when needed' do - @mock.benchmark.rules.clear - @mock.save_rules - - before = @mock.benchmark.rules.order(:precedence).pluck(:ref_id) - - @mock.instance_variable_set(:@rules, nil) - @mock.instance_variable_set(:@old_rules, nil) - @mock.instance_variable_set(:@new_rules, nil) - @mock.instance_variable_set(:@op_rules, @mock.instance_variable_get(:@op_rules).reverse) - @mock.save_rules - - after = @mock.benchmark.rules.order(:precedence).pluck(:ref_id) - - assert_equal before, after.reverse - end - - %i[description rationale severity].each do |field| - test "updates #{field} field when needed" do - @mock.benchmark.rules.clear - @mock.save_rules - - rule = @mock.benchmark.rules.order(:precedence).first - - @mock.instance_variable_set(:@rules, nil) - @mock.instance_variable_set(:@old_rules, nil) - @mock.instance_variable_set(:@new_rules, nil) - @mock.instance_variable_get(:@op_rules).first.instance_variable_set("@#{field}".to_sym, 'foobar') - @mock.save_rules - - assert_equal rule.reload[field], 'foobar' - end - end - - test 'updates value_checks field when needed' do - @mock.benchmark.rules.clear - @mock.save_rules - - rule = @mock.benchmark.rules.order(:precedence).first - - assert_equal [], rule.value_checks - - value_ref_id = @mock.benchmark.value_definitions.first.ref_id - value_id = @mock.benchmark.value_definitions.first.id - - @mock.instance_variable_set(:@rules, nil) - @mock.instance_variable_set(:@old_rules, nil) - @mock.instance_variable_set(:@new_rules, nil) - @mock.instance_variable_get(:@op_rules).first.stubs(:values).returns([value_ref_id]) - - @mock.save_rules - - assert_equal [value_id], rule.reload[:value_checks] - end -end diff --git a/test/services/concerns/xccdf/test_result_test.rb b/test/services/concerns/xccdf/test_result_test.rb deleted file mode 100644 index eab838cbf1..0000000000 --- a/test/services/concerns/xccdf/test_result_test.rb +++ /dev/null @@ -1,125 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class TestResultTest < ActiveSupport::TestCase - class Mock - include Xccdf::TestResult - include Xccdf::RuleResults - - attr_accessor :host, :host_profile, :op_test_result, :test_result - end - - setup do - @account = FactoryBot.create(:account) - @end_time = DateTime.now - @mock = Mock.new - @mock.host_profile = FactoryBot.create(:profile, account: @account) - @mock.host = FactoryBot.create(:host, org_id: @account.org_id) - @mock.op_test_result = OpenStruct.new(score: 30, - start_time: @end_time - 2.minutes, - end_time: @end_time) - supported_ssg = SupportedSsg.new(version: '0.1.33', - os_major_version: '7', os_minor_version: '4') - - SupportedSsg.stubs(:all).returns([supported_ssg]) - end - - test 'old test results are destroyed and replaced by the new test result' do - assert_difference('TestResult.count' => 2) do - TestResult.create!( - host: @mock.host, - profile: @mock.host_profile, - end_time: @end_time - 3.minutes - ) - - TestResult.create!( - host: @mock.host, - profile: @mock.host_profile, - end_time: @end_time - 8.minutes - ) - end - - assert_difference('TestResult.count' => -1) do - @mock.save_test_result - end - end - - test 'old test results within a policy replaced by the new test result' do - p2 = FactoryBot.create( - :profile, - policy: @mock.host_profile.policy, - account: @account - ) - - assert_difference('TestResult.count' => 2) do - TestResult.create!( - host: @mock.host, - profile: @mock.host_profile, - end_time: @end_time - 3.minutes, - score: 0 - ) - - TestResult.create!( - host: @mock.host, - profile: p2, - end_time: @end_time - 8.minutes, - score: 0 - ) - end - - assert_difference('TestResult.count' => -1) do - @mock.save_test_result - end - assert_equal TestResult.find_by(host: @mock.host), @mock.test_result - end - - context 'supportability' do - should 'default to supported' do - tr = FactoryBot.create( - :test_result, - profile: @mock.host_profile, - host: @mock.host - ) - assert tr.supported - end - - should 'mark hosts without an OS as unsupported' do - @mock.host.stubs(:os_major_version) - @mock.host.stubs(:os_minor_version) - - @mock.save_test_result - - test_result = TestResult.find_by(host: @mock.host, - profile: @mock.host_profile) - assert_not test_result.supported - end - - should 'mark hosts with a mismatched OS as unsupported' do - @mock.host.expects(:os_major_version).returns(7) - @mock.host.expects(:os_minor_version).returns(4) - assert_not_includes SupportedSsg.ssg_versions_for_os(7, 4), - @mock.host_profile.ssg_version - - @mock.save_test_result - - test_result = TestResult.find_by(host: @mock.host, - profile: @mock.host_profile) - assert_not test_result.supported - end - - should 'mark hosts with a matched OS as supported' do - @mock.host.expects(:os_major_version).returns(7) - @mock.host.expects(:os_minor_version).returns(4) - @mock.host_profile.benchmark.update!(version: '0.1.33') - assert_includes SupportedSsg.ssg_versions_for_os(7, 4), - @mock.host_profile.ssg_version - - @mock.save_test_result - - test_result = TestResult.find_by(host: @mock.host, - profile: @mock.host_profile) - assert test_result.supported - end - end -end diff --git a/test/services/concerns/xccdf/value_definitions_test.rb b/test/services/concerns/xccdf/value_definitions_test.rb deleted file mode 100644 index 1e4b0f027b..0000000000 --- a/test/services/concerns/xccdf/value_definitions_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class ValueDefinitionsTest < ActiveSupport::TestCase - include Xccdf::Profiles - include Xccdf::ValueDefinitions - include Xccdf::Rules - include Xccdf::RuleGroups - include Xccdf::ProfileRules - include Xccdf::RuleReferencesContainers - - attr_accessor :benchmark, :account, :op_profiles, :op_value_definitions - - setup do - account = FactoryBot.create(:account) - FactoryBot.create(:host, org_id: account.org_id) - @benchmark = FactoryBot.create(:canonical_profile).benchmark - parser = OpenscapParser::TestResultFile.new( - file_fixture('rhel-xccdf-report.xml').read - ) - @op_rules = parser.benchmark.rules - @op_rule_groups = parser.benchmark.groups - @op_value_definitions = parser.benchmark.values - end - - test 'save all value_definitions as new' do - assert_difference('ValueDefinition.count', 408) do - save_value_definitions - end - - assert_no_difference('ValueDefinition.count') do - save_value_definitions - end - end - - test 'updates value field when needed' do - save_value_definitions - - value_def = @benchmark.value_definitions.find_by(ref_id: 'xccdf_org.ssgproject.content_value_var_mcelog_server') - - assert_equal value_def.default_value, 'false' - - @value_definitions = nil - @old_value_definitions = nil - @new_value_definitions = nil - op_vd = @op_value_definitions.find { |t| t.id == 'xccdf_org.ssgproject.content_value_var_mcelog_server' } - op_vd.stubs(:value).returns('true') - - save_value_definitions - - assert_equal value_def.reload[:default_value], 'true' - end - - test 'updates description field when needed' do - save_value_definitions - - @value_definitions = nil - @old_value_definitions = nil - @new_value_definitions = nil - op_vd = @op_value_definitions.find { |t| t.id == 'xccdf_org.ssgproject.content_value_var_mcelog_server' } - op_vd.instance_variable_set('@description'.to_sym, 'foobar') - - save_value_definitions - - after = @benchmark.value_definitions.find_by(ref_id: 'xccdf_org.ssgproject.content_value_var_mcelog_server') - - assert_equal after.description, 'foobar' - end - - test 'correctly assigns all attributes' do - save_value_definitions - - val = @value_definitions.find { |vd| vd.ref_id == 'xccdf_org.ssgproject.content_value_var_accounts_tmout' } - - assert_equal '600', val.default_value - assert_equal 'number', val.value_type - end -end diff --git a/test/services/xccdf_report_parser_test.rb b/test/services/xccdf_report_parser_test.rb deleted file mode 100644 index 6bd33d1456..0000000000 --- a/test/services/xccdf_report_parser_test.rb +++ /dev/null @@ -1,358 +0,0 @@ -# frozen_string_literal: true - -require 'test_helper' - -class XccdfReportParserTest < ActiveSupport::TestCase - class TestParser < ::XccdfReportParser - attr_accessor :op_benchmark, :op_test_result, :op_profiles, :op_rules, - :op_rule_results, :op_rule_groups, :rule_groups - attr_reader :test_result_file, :host - - def package_name - nil - end - end - - setup do - PolicyHost.any_instance.stubs(:host_supported?).returns(true) - fake_report = file_fixture('xccdf_report.xml').read - @profile = { - 'xccdf_org.ssgproject.content_profile_standard' => - 'Standard System Security Profile for Fedora' - } - @account = FactoryBot.create(:account) - @host = FactoryBot.create( - :host, - org_id: @account.org_id, - display_name: 'MyStringone' - ) - - @report_parser = TestParser - .new(fake_report, - 'org_id' => @account.org_id, - 'b64_identity' => @account.b64_identity, - 'id' => @host.id, - 'metadata' => { - 'display_name' => @host.name - }) - @report_parser.host.stubs(:os_major_version).returns(8) - @report_parser.host.stubs(:os_minor_version).returns(3) - @report_parser.set_openscap_parser_data - stub_supported_ssg([@host], ['0.1.40']) - end - - context 'benchmark' do - setup do - @report_parser.stubs(:external_report?) - end - - should 'never save a new benchmark that is not in the support matrix' do - @report_parser.op_benchmark.stubs(:version).returns('0.1.15') - assert_difference('Xccdf::Benchmark.count', 0) do - assert_raises(XccdfReportParser::UnknownBenchmarkError) do - @report_parser.save_all - end - end - end - - should 'find and return an existing benchmark' do - @report_parser.save_all_benchmark_info - assert_no_difference('Xccdf::Benchmark.count') do - assert_nothing_raised do - @report_parser.save_all - end - end - end - end - - context 'profile' do - setup do - @report_parser.save_benchmark - @report_parser.stubs(:external_report?) - end - - should 'never save a new canonical profile if it did not exist before' do - @report_parser.stubs(:save_missing_supported_benchmark) - assert_difference('Profile.count', 0) do - assert_raises(XccdfReportParser::UnknownProfileError) do - @report_parser.save_all - end - end - end - end - - context 'rule_group' do - setup do - @report_parser.save_benchmark - @report_parser.save_profiles - end - end - - context 'host' do - setup do - @report_parser.save_all_benchmark_info - end - - should 'be able to parse host name' do - assert_equal( - @host.name, - @report_parser.test_result_file.test_result.host - ) - end - - should 'raise on OS version mismatch' do - @report_parser.host.stubs(:os_major_version).returns(7) - assert_raises(XccdfReportParser::OSVersionMismatch) do - @report_parser.check_os_version - end - end - end - - context 'rule results' do - setup do - @report_parser.stubs(:external_report?) - @report_parser.save_all_benchmark_info - end - - should 'save them, associate them with a rule and a host' do - profile = FactoryBot.create( - :profile, :with_rules, rule_count: 1, account: @account, policy: nil - ) - tr = FactoryBot.create(:test_result, host: @host, profile: profile) - FactoryBot.create( - :rule_result, - rule: profile.rules.first, - test_result: tr, - host: @host - ) - - assert_difference('RuleResult.count', 58) do - @report_parser.save_all - rule_results = @report_parser.rule_results - op_rule_results = @report_parser.op_rule_results - selected_op_rule_results = op_rule_results.reject do |rr| - RuleResult::NOT_SELECTED.include? rr.result - end - - assert_equal @report_parser.test_result_file.test_result.host, - RuleResult.find(rule_results.sample.id).host.name - rule_ids = Rule.includes(:rule_results) - .where(rule_results: { id: rule_results.map(&:id) }) - .pluck(:ref_id) - assert rule_ids.include?(selected_op_rule_results.sample.id) - selected_op_rule_results.each do |rule_result| - assert_equal rule_result.result, - RuleResult.joins(:rule) - .find_by(rules: { ref_id: rule_result.id }) - .result - end - end - end - - should 'provide failed results' do - @report_parser.save_all - failed_rule_results = @report_parser.failed_rule_results - assert_equal failed_rule_results.count, 45 - assert failed_rule_results.all? do |rr| - ::RuleResult::FAILED.include?(rr.result) - end - end - - should 'provide failed rules' do - @report_parser.save_all - assert_equal @report_parser.failed_rules.count, - @report_parser.failed_rule_results.count - end - end - - context 'error handling' do - should 'raise error if message ID is not present' do - assert_raises(XccdfReportParser::MissingIdError) do - TestParser.new( - 'fakereport', - 'org_id' => @account.org_id, - 'b64_identity' => @account.b64_identity, - 'metadata' => { 'display_name': '123' } - ) - end - end - - should 'validate b64_identity is present' do - assert_raises(XccdfReportParser::MissingIdError) do - TestParser.new( - 'fakereport', - 'org_id' => @account.org_id, - 'id' => @host.id, - 'metadata' => { 'display_name': '123' } - ) - end - end - end - - context 'rules' do - setup do - @arbitrary_rules = [ - # rubocop:disable Layout/LineLength - 'xccdf_org.ssgproject.content_rule_dir_perms_world_writable_system_owned', - 'xccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions', - 'xccdf_org.ssgproject.content_rule_gconf_gnome_screensaver_lock_enabled', - 'xccdf_org.ssgproject.content_rule_selinux_all_devicefiles_labeled' - # rubocop:enable Layout/LineLength - ] - @report_parser.save_benchmark - @report_parser.save_value_definitions - @report_parser.save_profiles - @report_parser.save_rule_groups - end - - should 'link the rules with the profile' do - @report_parser.save_rules - @report_parser.save_profile_rules - rule_ref_id = @report_parser.op_profiles - .find { |p| p.id == @profile.keys.first } - .selected_rule_ids.sample - rule = Rule.find_by(ref_id: rule_ref_id) - assert_equal @profile.keys.first, rule.profiles.pluck(:ref_id).first - end - - should 'never save new rules in the database' do - @report_parser.stubs(:save_missing_supported_benchmark) - @report_parser.stubs(:external_report?) - Rule.new( - ref_id: @arbitrary_rules[0], - benchmark: @report_parser.benchmark - ).save(validate: false) - Rule.new( - ref_id: @arbitrary_rules[1], - benchmark: @report_parser.benchmark - ).save(validate: false) - - assert_difference('Rule.count', 0) do - assert_raises(XccdfReportParser::UnknownRuleError) do - @report_parser.save_all - end - end - end - - should 'not try to append already assigned profiles to a rule' do - profile = Profile.create!( - ref_id: @profile.keys[0], - name: @profile.values[0], - benchmark: FactoryBot.create(:benchmark) - ) - rule = Rule.create!( - ref_id: @arbitrary_rules[0], - title: 'foo', - description: 'foo', - severity: 'low', - benchmark: profile.benchmark, - profiles: [profile], - rule_group: FactoryBot.create(:rule_group, benchmark: profile.benchmark) - ) - assert_nothing_raised do - assert_difference('rule.profiles.count', 0) do - @report_parser.save_rules - @report_parser.save_profile_rules - end - end - assert_includes rule.profiles, profile - end - - should 'add rules to new non-canonical (external) profiles' do - parent_profile = Profile.canonical.find_by( - ref_id: @report_parser.op_test_result.profile_id, - benchmark: @report_parser.benchmark - ) - - @report_parser.save_rules - @report_parser.save_profile_rules - assert_empty(Profile.where(parent_profile: parent_profile)) - assert_difference( - -> { Profile.count } => 1, - lambda { - Profile.find_by(parent_profile: parent_profile)&.rules&.count || 0 - } => parent_profile.rules.count - ) do - @report_parser.save_host_profile - end - end - - should 'not add rules to existing profiles' do - parent_profile = Profile.canonical.find_by( - ref_id: @report_parser.op_test_result.profile_id, - benchmark: @report_parser.benchmark - ) - (profile = Profile.new( - parent_profile: parent_profile, - account: @account - ).fill_from_parent).update!(rules: []) - - assert profile.persisted? - - assert_difference( - -> { Profile.count } => 0, - -> { profile.reload.rules.count } => 0 - ) do - @report_parser.save_rules - @report_parser.save_profile_rules - @report_parser.save_host_profile - end - end - end - - context 'datastream-based reports only' do - should 'raise an error if the report is not coming from a datastream' do - fake_report = file_fixture('rhel-xccdf-report-wrong.xml').read - assert_raises(XccdfReportParser::WrongFormatError) do - TestParser.new( - fake_report, - 'org_id' => @account.org_id, - 'id' => @host.id, - 'b64_identity' => @account.b64_identity, - 'metadata' => { - 'display_name' => @host.name - } - ) - end - end - end - - context 'finding an associated policy' do - should 'raise an error with no policy found (external report)' do - @report_parser.stubs(:external_report?).returns(true) - assert_raises(XccdfReportParser::ExternalReportError) do - @report_parser.check_for_external_reports - end - end - - should 'find a policy profile by hosts, account, and ref_id' do - profile = FactoryBot.create( - :canonical_profile, - ref_id: 'xccdf_org.ssgproject.content_profile_standard' - ) - - policy = FactoryBot.create( - :policy, - account: @account, - hosts: [@report_parser.host] - ) - FactoryBot.create( - :policy, - account: @account, - hosts: [@report_parser.host] - ) - - @report_parser.stubs(:external_report?) - Profile.any_instance.expects(:clone_to).with(policy: nil, - account: @account, - os_minor_version: '3') - @report_parser.save_host_profile - - profile.update!(policy: policy, account: @account) - Profile.any_instance.expects(:clone_to).with(policy: policy, - account: @account, - os_minor_version: '3') - @report_parser.save_host_profile - end - end -end