openscap_parser 0.1.0 → 1.0.2

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Dockerfile +15 -0
  4. data/README.md +32 -10
  5. data/Rakefile +2 -0
  6. data/lib/openscap_parser.rb +14 -35
  7. data/lib/openscap_parser/benchmark.rb +38 -0
  8. data/lib/openscap_parser/benchmarks.rb +21 -0
  9. data/lib/openscap_parser/datastream_file.rb +15 -0
  10. data/lib/openscap_parser/fix.rb +55 -0
  11. data/lib/openscap_parser/fixes.rb +21 -0
  12. data/lib/openscap_parser/oval_report.rb +24 -0
  13. data/lib/openscap_parser/profile.rb +31 -0
  14. data/lib/openscap_parser/profiles.rb +7 -12
  15. data/lib/openscap_parser/rule.rb +51 -10
  16. data/lib/openscap_parser/rule_identifier.rb +21 -0
  17. data/lib/openscap_parser/rule_reference.rb +14 -0
  18. data/lib/openscap_parser/rule_references.rb +32 -0
  19. data/lib/openscap_parser/rule_result.rb +31 -2
  20. data/lib/openscap_parser/rule_results.rb +21 -0
  21. data/lib/openscap_parser/rules.rb +9 -8
  22. data/lib/openscap_parser/selectors.rb +9 -0
  23. data/lib/openscap_parser/set_value.rb +18 -0
  24. data/lib/openscap_parser/set_values.rb +21 -0
  25. data/lib/openscap_parser/sub.rb +18 -0
  26. data/lib/openscap_parser/subs.rb +38 -0
  27. data/lib/openscap_parser/tailoring.rb +27 -0
  28. data/lib/openscap_parser/tailoring_file.rb +15 -0
  29. data/lib/openscap_parser/tailorings.rb +22 -0
  30. data/lib/openscap_parser/test_result.rb +62 -0
  31. data/lib/openscap_parser/test_result_file.rb +12 -0
  32. data/lib/openscap_parser/test_results.rb +19 -0
  33. data/lib/openscap_parser/util.rb +10 -0
  34. data/lib/openscap_parser/version.rb +1 -1
  35. data/lib/openscap_parser/xml_file.rb +13 -0
  36. data/lib/openscap_parser/xml_node.rb +36 -0
  37. data/lib/oval/definition.rb +47 -0
  38. data/lib/oval/definition_result.rb +17 -0
  39. data/lib/oval/reference.rb +21 -0
  40. data/lib/railtie.rb +15 -0
  41. data/lib/ssg.rb +5 -0
  42. data/lib/ssg/downloader.rb +94 -0
  43. data/lib/ssg/unarchiver.rb +34 -0
  44. data/lib/tasks/ssg.rake +33 -0
  45. data/openscap_parser.gemspec +13 -10
  46. metadata +94 -14
  47. data/lib/openscap_parser/xml_report.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b79fdf725d0cb7a4bb9157c03871095ae0a53b1813c45c6a11cb8ceebb91ec24
4
- data.tar.gz: cb31f6050f240a34d760a0c3c51eb80b38d420f2a574de61ed1d321c47a61833
3
+ metadata.gz: 65cd8f383c3d40c907ecab37235ac6c50eeddced32dc9173b7b4b8e7526c869a
4
+ data.tar.gz: a1150af6f99b020a31f174b83a1b405b1375dd0e37300ced57daa633988a8be1
5
5
  SHA512:
6
- metadata.gz: cb2cfcec90e8207806b14a8d63b5f5dc294fcdde4676858019cc95d06fdcfb0e2c51690ec4a826641a3336ee58aa1cbce2dd1a4f248464c0369175687db3ebfa
7
- data.tar.gz: 6c516e467e086ad79633392393234be4f3b9d0c4265222e91daf0d9f9bb6f41e3efd5adf68930b1dbb2694fc79577bc61d4cbb52bdc67085c87c68393ac25f4a
6
+ metadata.gz: 696960d718a5eefbd435af227822ce48144c07be20df1a85ec2a93c1328bba7d8f92e823b57feafe38f479a3c3b0a3f26d365595cf247cf13de3f2160b11ccda
7
+ data.tar.gz: a40766e4211f052ad1ab60c1e41dbb2bb70ab26ee221aeaa5eca664287a86b5cd1cf199f1fe47a3be23372b5723c11cf394c68db13203333abf3e0639dd1f841
data/.gitignore CHANGED
@@ -6,3 +6,4 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ Gemfile.lock
@@ -0,0 +1,15 @@
1
+ # docker build . -t openscap_parser # build the container image
2
+ # docker run -itv $PWD:/app:z openscap_parser rake # run tests
3
+ # docker run -itv $PWD:/app:z openscap_parser pry --gem # console
4
+
5
+ FROM ruby:2.5
6
+
7
+ RUN gem update bundler
8
+
9
+ WORKDIR /app
10
+
11
+ COPY . ./
12
+
13
+ RUN bundle -j4
14
+
15
+ CMD bash
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/openscap_parser`. To experiment with that code, run `bin/console` for an interactive prompt.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
6
-
7
5
  ## Installation
8
6
 
9
7
  Add this line to your application's Gemfile:
@@ -24,16 +22,30 @@ Or install it yourself as:
24
22
 
25
23
  ARF/XCCDF report goes IN - Ruby hash goes OUT
26
24
 
27
- {
28
- profile
29
- host
30
- score
31
- start_time
32
- end_time
33
- rule_result
25
+ ```rb
26
+ parser = OpenscapParser::Base.new(File.read('rhel7-xccdf_org.ssgproject.content_profile_standard.xml'))
27
+ parser.host # "rhel7-insights-client.virbr0.akofink-laptop"
28
+ parser.start_time # <DateTime: 2019-08-08T17:25:50+00:00 ((2458704j,62750s,0n),+0s,2299161j)>
29
+ parser.end_time # <DateTime: 2019-08-08T17:26:45+00:00 ((2458704j,62805s,0n),+0s,2299161j)>
30
+ parser.score # 80.833328
31
+ parser.profiles # {"xccdf_org.ssgproject.content_profile_standard"=>"Standard System Security Profile for Red Hat Enterprise Linux 7"}
32
+ parser.rules # [#<OpenscapParser::Rule:0x00005576e752db7 ... >, ...]
33
+ parser.rule_results # [#<OpenscapParser::RuleResult:0x00005576e8022f60 @id="xccdf_org.ssgproject.content_rule_package_rsh_removed", @result="notselected">, ...]
34
+
35
+ # and more!
36
+ ```
37
+
38
+ ### Fetching SCAP Security Guide Content
34
39
 
35
- }
40
+ This gem includes a rake task to sync content from the [ComplianceAsCode project](https://github.com/ComplianceAsCode/content). The following examples show how to download and exract datastream files from the released versions:
41
+
42
+ ```sh
43
+ rake ssg:sync DATASTREAMS=latest:fedora # fetch and extract the latest fedora datastream
44
+ rake ssg:sync DATASTREAMS=v0.1.45:fedora,v0.1.45:firefox # fetch and extract tag v0.1.45 for fedora and firefox datastreams
45
+ rake ssg:sync_rhel # fetch and extract the latest released versions of the RHEL 6, 7, and 8 datastreams
46
+ ```
36
47
 
48
+ An SSG version will be downloaded only once, even if it is specified multiple times for multiple datastreams.
37
49
 
38
50
  ## Development
39
51
 
@@ -41,6 +53,16 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
41
53
 
42
54
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
43
55
 
56
+ ### With Docker
57
+
58
+ A Dockerfile is provided to allow a containerized development environment:
59
+
60
+ ```
61
+ docker build . -t openscap_parser # build the container image
62
+ docker run -itv $PWD:/app:z openscap_parser rake # run tests
63
+ docker run -itv $PWD:/app:z openscap_parser pry --gem # console
64
+ ```
65
+
44
66
  ## Contributing
45
67
 
46
68
  Bug reports and pull requests are welcome on GitHub at https://github.com/elobato/openscap_parser. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
data/Rakefile CHANGED
@@ -1,6 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rake/testtask"
3
3
 
4
+ import "./lib/tasks/ssg.rake"
5
+
4
6
  Rake::TestTask.new(:test) do |t|
5
7
  t.libs << "test"
6
8
  t.libs << "lib"
@@ -1,44 +1,23 @@
1
1
  # frozen_string_literal: true
2
+
3
+ require 'openscap_parser/version'
4
+ require 'openscap_parser/util'
5
+ require 'openscap_parser/benchmarks'
6
+ require 'openscap_parser/test_results'
2
7
  require 'openscap_parser/profiles'
3
- require 'openscap_parser/rule'
4
- require 'openscap_parser/rule_result'
5
8
  require 'openscap_parser/rules'
6
- require 'openscap_parser/version'
7
- require 'openscap_parser/xml_report'
9
+ require 'openscap_parser/rule_results'
10
+ require 'openscap_parser/tailorings'
11
+
12
+ require 'openscap_parser/xml_file'
13
+ require 'openscap_parser/datastream_file'
14
+ require 'openscap_parser/test_result_file'
15
+ require 'openscap_parser/tailoring_file'
16
+ require 'openscap_parser/oval_report'
8
17
 
9
18
  require 'date'
19
+ require 'railtie' if defined?(Rails)
10
20
 
11
21
  module OpenscapParser
12
22
  class Error < StandardError; end
13
-
14
- class Base
15
- include OpenscapParser::XMLReport
16
- include OpenscapParser::Profiles
17
- include OpenscapParser::Rules
18
-
19
- def initialize(report)
20
- report_xml(report)
21
- end
22
-
23
- def score
24
- test_result_node.search('score').text.to_f
25
- end
26
-
27
- def start_time
28
- @start_time ||= DateTime.parse(test_result_node['start-time'])
29
- end
30
-
31
- def end_time
32
- @end_time ||= DateTime.parse(test_result_node['end-time'])
33
- end
34
-
35
- def rule_results
36
- @rule_results ||= test_result_node.search('rule-result').map do |rr|
37
- rule_result_oscap = RuleResult.new
38
- rule_result_oscap.id = rr.attributes['idref'].value
39
- rule_result_oscap.result = rr.search('result').first.text
40
- rule_result_oscap
41
- end
42
- end
43
- end
44
23
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openscap_parser/util'
4
+ require 'openscap_parser/xml_file'
5
+ require 'openscap_parser/rules'
6
+ require 'openscap_parser/profiles'
7
+ require 'openscap_parser/rule_references'
8
+
9
+ # Mimics openscap-ruby Benchmark interface
10
+ module OpenscapParser
11
+ class Benchmark < XmlNode
12
+ include OpenscapParser::Util
13
+ include OpenscapParser::Rules
14
+ include OpenscapParser::RuleReferences
15
+ include OpenscapParser::Profiles
16
+
17
+ def id
18
+ @id ||= @parsed_xml['id']
19
+ end
20
+
21
+ def title
22
+ @title ||= @parsed_xml.xpath('title') &&
23
+ @parsed_xml.xpath('title').text
24
+ end
25
+
26
+ def description
27
+ @description ||= newline_to_whitespace(
28
+ @parsed_xml.xpath('description') &&
29
+ @parsed_xml.xpath('description').text || ''
30
+ )
31
+ end
32
+
33
+ def version
34
+ @version ||= @parsed_xml.xpath('version') &&
35
+ @parsed_xml.xpath('version').text
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openscap_parser/benchmark'
4
+
5
+ module OpenscapParser
6
+ # Methods related to saving profiles and finding which hosts
7
+ # they belong to
8
+ module Benchmarks
9
+ def self.included(base)
10
+ base.class_eval do
11
+ def benchmark
12
+ @benchmark ||= OpenscapParser::Benchmark.new(parsed_xml: benchmark_node)
13
+ end
14
+
15
+ def benchmark_node(xpath = ".//Benchmark")
16
+ xpath_node(xpath)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ require 'openscap_parser/xml_file'
3
+ require 'openscap_parser/benchmarks'
4
+
5
+ module OpenscapParser
6
+ # A class to represent a datastream (-ds.xml) XmlFile
7
+ class DatastreamFile < XmlFile
8
+ include OpenscapParser::Benchmarks
9
+
10
+ def valid?
11
+ return true if @parsed_xml.root.name == 'data-stream-collection' && namespaces.keys.include?('xmlns:ds')
12
+ false
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+ require 'openscap_parser/xml_node'
3
+ require 'openscap_parser/subs'
4
+
5
+ module OpenscapParser
6
+ class Fix < XmlNode
7
+ include OpenscapParser::Subs
8
+
9
+ def id
10
+ @id ||= @parsed_xml['id']
11
+ end
12
+
13
+ def system
14
+ @system ||= @parsed_xml['system']
15
+ end
16
+
17
+ def complexity
18
+ @complexity ||= @parsed_xml['complexity']
19
+ end
20
+
21
+ def disruption
22
+ @disruption ||= @parsed_xml['disruption']
23
+ end
24
+
25
+ def strategy
26
+ @strategy ||= @parsed_xml['strategy']
27
+ end
28
+
29
+ def full_text(set_values)
30
+ full_text_lines(set_values).join('')
31
+ end
32
+
33
+ def full_text_lines(set_values)
34
+ map_child_nodes(set_values).map do |text_node|
35
+ text_node.respond_to?(:text) ? text_node.text : ''
36
+ end
37
+ end
38
+
39
+ def map_child_nodes(set_values = [])
40
+ map_sub_nodes @parsed_xml.children, set_values
41
+ end
42
+
43
+ def to_h
44
+ {
45
+ :id => id,
46
+ :system => system,
47
+ :complexity => complexity,
48
+ :disruption => disruption,
49
+ :strategy => strategy,
50
+ :text => text,
51
+ :subs => subs.map(&:to_h)
52
+ }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openscap_parser/fix'
4
+
5
+ module OpenscapParser
6
+ module Fixes
7
+ def self.included(base)
8
+ base.class_eval do
9
+ def fixes
10
+ @fixes ||= fix_nodes.map do |fix_node|
11
+ OpenscapParser::Fix.new(parsed_xml: fix_node)
12
+ end
13
+ end
14
+
15
+ def fix_nodes(xpath = ".//fix")
16
+ xpath_nodes(xpath)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ require 'openscap_parser/xml_file'
3
+ require 'oval/definition_result'
4
+ require 'oval/definition'
5
+
6
+ module OpenscapParser
7
+ class OvalReport < XmlFile
8
+ def definition_results
9
+ @definition_results ||= definition_result_nodes.map { |node| ::Oval::DefinitionResult.new parsed_xml: node }
10
+ end
11
+
12
+ def definition_result_nodes(xpath = "./oval_results/results/system/definitions/definition")
13
+ xpath_nodes(xpath)
14
+ end
15
+
16
+ def definitions
17
+ @definitions ||= definition_nodes.map { |node| Oval::Definition.new parsed_xml: node }
18
+ end
19
+
20
+ def definition_nodes(xpath = "./oval_results/oval_definitions/definitions/definition")
21
+ xpath_nodes(xpath)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ module OpenscapParser
2
+ class Profile < XmlNode
3
+ def id
4
+ @id ||= @parsed_xml['id']
5
+ end
6
+
7
+ def extends_profile_id
8
+ @extends ||= @parsed_xml['extends']
9
+ end
10
+
11
+ def title
12
+ @title ||= @parsed_xml.at_css('title') &&
13
+ @parsed_xml.at_css('title').text
14
+ end
15
+ alias :name :title
16
+
17
+ def description
18
+ @description ||= @parsed_xml.at_css('description') &&
19
+ @parsed_xml.at_css('description').text
20
+ end
21
+
22
+ def selected_rule_ids
23
+ @selected_rule_ids ||= @parsed_xml.xpath("select[@selected='true']/@idref") &&
24
+ @parsed_xml.xpath("select[@selected='true']/@idref").map(&:text)
25
+ end
26
+
27
+ def to_h
28
+ { :id => id, :title => title, :description => description }
29
+ end
30
+ end
31
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'openscap_parser/profile'
4
+
3
5
  module OpenscapParser
4
6
  # Methods related to saving profiles and finding which hosts
5
7
  # they belong to
@@ -7,20 +9,13 @@ module OpenscapParser
7
9
  def self.included(base)
8
10
  base.class_eval do
9
11
  def profiles
10
- @profiles ||= {
11
- profile_node['id'] => profile_node.at_css('title').text
12
- }
13
- end
14
-
15
- private
16
-
17
- def profile_node
18
- @report_xml.at_xpath(".//xmlns:Profile\
19
- [contains('#{test_result_node['id']}', @id)]")
12
+ @profiles ||= profile_nodes.map do |profile_node|
13
+ OpenscapParser::Profile.new(parsed_xml: profile_node)
14
+ end
20
15
  end
21
16
 
22
- def test_result_node
23
- @test_result_node ||= @report_xml.at_css('TestResult')
17
+ def profile_nodes(xpath = ".//Profile")
18
+ xpath_nodes(xpath)
24
19
  end
25
20
  end
26
21
  end
@@ -1,31 +1,72 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'openscap_parser/rule_identifier'
4
+ require 'openscap_parser/rule_references'
5
+ require 'openscap_parser/fixes'
6
+ require 'openscap_parser/xml_file'
7
+
3
8
  # Mimics openscap-ruby Rule interface
4
9
  module OpenscapParser
5
- class Rule
6
- def initialize(rule_xml: nil)
7
- @rule_xml = rule_xml
8
- end
10
+ class Rule < XmlNode
11
+ include OpenscapParser::Util
12
+ include OpenscapParser::RuleReferences
13
+ include OpenscapParser::Fixes
9
14
 
10
15
  def id
11
- @id ||= @rule_xml['id']
16
+ @id ||= parsed_xml['id']
17
+ end
18
+
19
+ def selected
20
+ @selected ||= parsed_xml['selected']
12
21
  end
13
22
 
14
23
  def severity
15
- @severity ||= @rule_xml['severity']
24
+ @severity ||= parsed_xml['severity']
16
25
  end
17
26
 
18
27
  def title
19
- @title ||= @rule_xml.at_css('title').children.first.text
28
+ @title ||= parsed_xml.at_css('title') &&
29
+ parsed_xml.at_css('title').text
20
30
  end
21
31
 
22
32
  def description
23
- @description ||= @rule_xml.at_css('description').text.delete("\n")
33
+ @description ||= newline_to_whitespace(
34
+ parsed_xml.at_css('description') &&
35
+ parsed_xml.at_css('description').text || ''
36
+ )
24
37
  end
25
38
 
26
39
  def rationale
27
- @rationale ||= @rule_xml.at_css('rationale').children.text.delete("\n")
40
+ @rationale ||= newline_to_whitespace(
41
+ parsed_xml.at_css('rationale') &&
42
+ parsed_xml.at_css('rationale').text || ''
43
+ )
44
+ end
45
+
46
+ alias :rule_reference_nodes_old :rule_reference_nodes
47
+ def rule_reference_nodes(xpath = "reference")
48
+ rule_reference_nodes_old(xpath)
49
+ end
50
+
51
+ def rule_identifier
52
+ @identifier ||= RuleIdentifier.new(parsed_xml: identifier_node)
53
+ end
54
+ alias :identifier :rule_identifier
55
+
56
+ def identifier_node
57
+ @identifier_node ||= parsed_xml.at_xpath('ident')
58
+ end
59
+
60
+ def to_h
61
+ {
62
+ :id => id,
63
+ :selected => selected,
64
+ :severity => severity,
65
+ :title => title,
66
+ :description => description,
67
+ :rationale => rationale,
68
+ :identifier => rule_identifier.to_h
69
+ }
28
70
  end
29
71
  end
30
72
  end
31
-