scelint 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6b16cbfa48415fa9683d44daf2788ade982a90ab980ed6c5149139f2d17d8c78
4
- data.tar.gz: 78bb451017798298bac2460b9e2ab20761f0d1757b8d40d39880943baa4cdbc5
3
+ metadata.gz: a1e8f8d8e3eb96d5aa6bc36fb178f2b3cb283240ff04c37f469f4563693e94ee
4
+ data.tar.gz: d89de6916fd66be7a7572240e8d04606ce33201b964f58b455da8423e850d474
5
5
  SHA512:
6
- metadata.gz: 661faabd127dde36f197f2bac4fad65daea62994d53d63a0df412408909d39e3c9b50de55f3a18977268f1f7a69ad1475da7e78711316c3261e3624381f02235
7
- data.tar.gz: c260920fb4aa2ac12e45e7ed77c7ead6f2d2a4ce51eee488f85ffac52e081ec59f5c4f6d2f25be96bf2665b76b62a9972e4ced9f8715e69eb9a04e1d28f4a848
6
+ metadata.gz: 29896aa3398ae2a5503f304ec68968bf4e2944084dbbee5eee3056eba57cb843d3cc9e39eb1660bb6d679485548fed575e5a027a38ce9c3f2ef0cf2d34bc0900
7
+ data.tar.gz: f34b1e846bd6a188af09b9b5e5fc4fed0510c068ed05c09488ed845ba8e83f0ab46738551a005daa5a34cc4d6aef587bc8ca0a75b6a2833cc3482da452cd9243
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ### 0.7.0 / 2026-11-02
2
+ * **BREAKING:** Report an error when files give a CE `oval-ids` for different
3
+ rules, as happens when two revisions of a benchmark ship together. Remove
4
+ the superseded files.
5
+
1
6
  ### 0.6.2 / 2026-11-02
2
7
  * Report the file and check when data cannot be merged instead of crashing
3
8
  * Report a `profiles`, `ce` or `checks` section that is not a hash, and a check's
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Scelint
4
- VERSION = '0.6.2'
4
+ VERSION = '0.7.0'
5
5
  end
data/lib/scelint.rb CHANGED
@@ -149,6 +149,8 @@ module Scelint
149
149
 
150
150
  merged_data_lint
151
151
 
152
+ check_ce_oval_ids
153
+
152
154
  validate
153
155
  end
154
156
 
@@ -801,8 +803,54 @@ module Scelint
801
803
  errors << "#{file}: #{e.message} (not a hash?)"
802
804
  end
803
805
 
806
+ # Report CEs that resolve to more than one rule
807
+ #
808
+ # A CE identifies one rule. When two files give the same CE different
809
+ # oval-ids, the merged CE carries every one of them, and scan results
810
+ # can no longer be correlated back to a single rule with any confidence.
811
+ #
812
+ # In practice this is what a superseded benchmark file looks like from the
813
+ # inside: the old and new revisions both define the same CE keys, and the
814
+ # rule ids are where they diverge.
815
+ def check_ce_oval_ids
816
+ data.ces.each do |ce, component|
817
+ by_file = component.component[:fragments].each_with_object({}) do |(file, fragment), result|
818
+ next unless fragment.is_a?(Hash)
819
+
820
+ ids = Array(fragment['oval-ids']).select { |id| id.is_a?(String) && !id.empty? }
821
+ result[file] = ids unless ids.empty?
822
+ end
823
+
824
+ by_file.to_a.combination(2) do |(file_a, ids_a), (file_b, ids_b)|
825
+ next if same_rules?(ids_a, ids_b)
826
+
827
+ errors << "CE '#{ce}': conflicting oval-ids (#{ids_a.inspect} in #{file_a}, #{ids_b.inspect} in #{file_b})"
828
+ end
829
+ end
830
+ end
831
+
804
832
  private
805
833
 
834
+ # Return true if two sets of oval-ids name the same rules
835
+ #
836
+ # The same rule is routinely written two ways in one data set: a manual
837
+ # benchmark uses the bare rule id and its SCAP counterpart prefixes it, as in
838
+ # 'SV-230221r1017040_rule' and
839
+ # 'xccdf_mil.disa.stig_rule_SV-230221r1017040_rule'. Those are not a
840
+ # disagreement, so the 'xccdf_<namespace>_rule_' prefix is ignored, and
841
+ # the remaining rule names are compared as sets. The namespace may be
842
+ # written with '.' or ':', as in 'xccdf_org.ssgproject.content_rule_foo'
843
+ # and 'xccdf_org:ssgproject:content_rule_foo'.
844
+ #
845
+ # @param ids_a [Array<String>] A set of oval-ids
846
+ # @param ids_b [Array<String>] A set of oval-ids
847
+ # @return [Boolean]
848
+ def same_rules?(ids_a, ids_b)
849
+ rule_names = ->(ids) { ids.map { |id| id.sub(%r{\Axccdf_[^_]+_rule_}, '') }.uniq.sort }
850
+
851
+ rule_names.call(ids_a) == rule_names.call(ids_b)
852
+ end
853
+
806
854
  # Merge a ComplianceEngine::Collection object into a Hash
807
855
  #
808
856
  # Components that cannot be merged (usually because a fragment is
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scelint
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Steven Pritchard