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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Dockerfile +15 -0
- data/README.md +32 -10
- data/Rakefile +2 -0
- data/lib/openscap_parser.rb +14 -35
- data/lib/openscap_parser/benchmark.rb +38 -0
- data/lib/openscap_parser/benchmarks.rb +21 -0
- data/lib/openscap_parser/datastream_file.rb +15 -0
- data/lib/openscap_parser/fix.rb +55 -0
- data/lib/openscap_parser/fixes.rb +21 -0
- data/lib/openscap_parser/oval_report.rb +24 -0
- data/lib/openscap_parser/profile.rb +31 -0
- data/lib/openscap_parser/profiles.rb +7 -12
- data/lib/openscap_parser/rule.rb +51 -10
- data/lib/openscap_parser/rule_identifier.rb +21 -0
- data/lib/openscap_parser/rule_reference.rb +14 -0
- data/lib/openscap_parser/rule_references.rb +32 -0
- data/lib/openscap_parser/rule_result.rb +31 -2
- data/lib/openscap_parser/rule_results.rb +21 -0
- data/lib/openscap_parser/rules.rb +9 -8
- data/lib/openscap_parser/selectors.rb +9 -0
- data/lib/openscap_parser/set_value.rb +18 -0
- data/lib/openscap_parser/set_values.rb +21 -0
- data/lib/openscap_parser/sub.rb +18 -0
- data/lib/openscap_parser/subs.rb +38 -0
- data/lib/openscap_parser/tailoring.rb +27 -0
- data/lib/openscap_parser/tailoring_file.rb +15 -0
- data/lib/openscap_parser/tailorings.rb +22 -0
- data/lib/openscap_parser/test_result.rb +62 -0
- data/lib/openscap_parser/test_result_file.rb +12 -0
- data/lib/openscap_parser/test_results.rb +19 -0
- data/lib/openscap_parser/util.rb +10 -0
- data/lib/openscap_parser/version.rb +1 -1
- data/lib/openscap_parser/xml_file.rb +13 -0
- data/lib/openscap_parser/xml_node.rb +36 -0
- data/lib/oval/definition.rb +47 -0
- data/lib/oval/definition_result.rb +17 -0
- data/lib/oval/reference.rb +21 -0
- data/lib/railtie.rb +15 -0
- data/lib/ssg.rb +5 -0
- data/lib/ssg/downloader.rb +94 -0
- data/lib/ssg/unarchiver.rb +34 -0
- data/lib/tasks/ssg.rake +33 -0
- data/openscap_parser.gemspec +13 -10
- metadata +94 -14
- data/lib/openscap_parser/xml_report.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65cd8f383c3d40c907ecab37235ac6c50eeddced32dc9173b7b4b8e7526c869a
|
4
|
+
data.tar.gz: a1150af6f99b020a31f174b83a1b405b1375dd0e37300ced57daa633988a8be1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 696960d718a5eefbd435af227822ce48144c07be20df1a85ec2a93c1328bba7d8f92e823b57feafe38f479a3c3b0a3f26d365595cf247cf13de3f2160b11ccda
|
7
|
+
data.tar.gz: a40766e4211f052ad1ab60c1e41dbb2bb70ab26ee221aeaa5eca664287a86b5cd1cf199f1fe47a3be23372b5723c11cf394c68db13203333abf3e0639dd1f841
|
data/.gitignore
CHANGED
data/Dockerfile
ADDED
@@ -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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
data/lib/openscap_parser.rb
CHANGED
@@ -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/
|
7
|
-
require 'openscap_parser/
|
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
|
-
|
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
|
23
|
-
|
17
|
+
def profile_nodes(xpath = ".//Profile")
|
18
|
+
xpath_nodes(xpath)
|
24
19
|
end
|
25
20
|
end
|
26
21
|
end
|
data/lib/openscap_parser/rule.rb
CHANGED
@@ -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
|
-
|
7
|
-
|
8
|
-
|
10
|
+
class Rule < XmlNode
|
11
|
+
include OpenscapParser::Util
|
12
|
+
include OpenscapParser::RuleReferences
|
13
|
+
include OpenscapParser::Fixes
|
9
14
|
|
10
15
|
def id
|
11
|
-
@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 ||=
|
24
|
+
@severity ||= parsed_xml['severity']
|
16
25
|
end
|
17
26
|
|
18
27
|
def title
|
19
|
-
@title ||=
|
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 ||=
|
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 ||=
|
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
|
-
|