openscap_parser 0.1.0 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
-