smart_proxy_openscap 0.7.3 → 0.9.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 +4 -4
- data/Gemfile +1 -0
- data/bin/smart-proxy-openscap-send +5 -1
- data/lib/smart_proxy_openscap/arf_parser.rb +74 -17
- data/lib/smart_proxy_openscap/content_parser.rb +19 -25
- data/lib/smart_proxy_openscap/fetch_scap_file.rb +45 -0
- data/lib/smart_proxy_openscap/foreman_arf_forwarder.rb +15 -0
- data/lib/smart_proxy_openscap/foreman_forwarder.rb +19 -16
- data/lib/smart_proxy_openscap/foreman_oval_forwarder.rb +19 -0
- data/lib/smart_proxy_openscap/openscap_api.rb +59 -28
- data/lib/smart_proxy_openscap/openscap_exception.rb +1 -0
- data/lib/smart_proxy_openscap/openscap_html_generator.rb +1 -1
- data/lib/smart_proxy_openscap/openscap_import_api.rb +3 -3
- data/lib/smart_proxy_openscap/openscap_lib.rb +5 -3
- data/lib/smart_proxy_openscap/openscap_plugin.rb +2 -1
- data/lib/smart_proxy_openscap/oval_report_parser.rb +54 -0
- data/lib/smart_proxy_openscap/oval_report_storage_fs.rb +26 -0
- data/lib/smart_proxy_openscap/profiles_parser.rb +22 -23
- data/lib/smart_proxy_openscap/spool_forwarder.rb +4 -4
- data/lib/smart_proxy_openscap/storage.rb +0 -2
- data/lib/smart_proxy_openscap/storage_fs.rb +7 -4
- data/lib/smart_proxy_openscap/storage_fs_common.rb +42 -0
- data/lib/smart_proxy_openscap/version.rb +1 -1
- data/settings.d/openscap.yml.example +3 -0
- data/smart_proxy_openscap.gemspec +2 -0
- data/test/data/oval-results.xml.bz2 +0 -0
- data/test/data/rhel-7-including-unpatched.oval.xml.bz2 +0 -0
- data/test/fetch_oval_content_api_test.rb +38 -0
- data/test/fetch_scap_api_test.rb +1 -1
- data/test/oval_report_parser_test.rb +14 -0
- data/test/post_oval_report_api_test.rb +30 -0
- data/test/post_report_api_test.rb +2 -2
- data/test/scap_content_parser_api_test.rb +1 -1
- data/test/script_class_test.rb +0 -58
- metadata +29 -11
- data/bin/smart-proxy-arf-json +0 -7
- data/bin/smart-proxy-scap-profiles +0 -7
- data/bin/smart-proxy-scap-validation +0 -7
- data/lib/smart_proxy_openscap/arf_json.rb +0 -114
- data/lib/smart_proxy_openscap/fetch_scap_content.rb +0 -17
- data/lib/smart_proxy_openscap/fetch_tailoring_file.rb +0 -17
- data/lib/smart_proxy_openscap/scap_profiles.rb +0 -52
- data/lib/smart_proxy_openscap/scap_validation.rb +0 -35
@@ -30,7 +30,7 @@ module Proxy
|
|
30
30
|
|
31
31
|
def file_path_in_storage
|
32
32
|
path_to_dir = Proxy::OpenSCAP::Plugin.settings.reportsdir
|
33
|
-
storage = Proxy::OpenSCAP::
|
33
|
+
storage = Proxy::OpenSCAP::StorageFs.new(path_to_dir, @cname, @id, @date)
|
34
34
|
storage.get_path(@digest)
|
35
35
|
end
|
36
36
|
end
|
@@ -14,15 +14,15 @@ module Proxy::OpenSCAP
|
|
14
14
|
|
15
15
|
post_to_foreman = ForemanForwarder.new.post_arf_report(cn, policy, date, request.body.string, Proxy::OpenSCAP::Plugin.settings.timeout)
|
16
16
|
begin
|
17
|
-
Proxy::OpenSCAP::
|
17
|
+
Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, cn, post_to_foreman['id'], date).store_archive(request.body.string)
|
18
18
|
rescue Proxy::OpenSCAP::StoreReportError => e
|
19
|
-
Proxy::OpenSCAP::
|
19
|
+
Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.failed_dir, cn, post_to_foreman['id'], date).store_failed(request.body.string)
|
20
20
|
logger.error "Failed to save Report in reports directory (#{Proxy::OpenSCAP::Plugin.settings.reportsdir}). Failed with: #{e.message}.
|
21
21
|
Saving file in #{Proxy::OpenSCAP::Plugin.settings.failed_dir}. Please copy manually to #{Proxy::OpenSCAP::Plugin.settings.reportsdir}"
|
22
22
|
rescue *HTTP_ERRORS => e
|
23
23
|
### If the upload to foreman fails then store it in the spooldir
|
24
24
|
logger.error "Failed to upload to Foreman, saving in spool. Failed with: #{e.message}"
|
25
|
-
Proxy::OpenSCAP::
|
25
|
+
Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.spooldir, cn, policy, date).store_spool(request.body.string)
|
26
26
|
rescue Proxy::OpenSCAP::StoreSpoolError => e
|
27
27
|
log_halt 500, e.message
|
28
28
|
end
|
@@ -16,16 +16,18 @@ require 'proxy/error'
|
|
16
16
|
require 'yaml'
|
17
17
|
require 'ostruct'
|
18
18
|
require 'proxy/request'
|
19
|
-
require 'smart_proxy_openscap/
|
20
|
-
require 'smart_proxy_openscap/
|
19
|
+
require 'smart_proxy_openscap/foreman_arf_forwarder'
|
20
|
+
require 'smart_proxy_openscap/foreman_oval_forwarder'
|
21
21
|
require 'smart_proxy_openscap/content_parser'
|
22
22
|
require 'smart_proxy_openscap/openscap_exception'
|
23
23
|
require 'smart_proxy_openscap/arf_parser'
|
24
24
|
require 'smart_proxy_openscap/spool_forwarder'
|
25
25
|
require 'smart_proxy_openscap/openscap_html_generator'
|
26
|
-
require 'smart_proxy_openscap/fetch_tailoring_file'
|
27
26
|
require 'smart_proxy_openscap/policy_parser'
|
28
27
|
require 'smart_proxy_openscap/profiles_parser'
|
28
|
+
require 'smart_proxy_openscap/oval_report_storage_fs'
|
29
|
+
require 'smart_proxy_openscap/oval_report_parser'
|
30
|
+
require 'smart_proxy_openscap/fetch_scap_file'
|
29
31
|
|
30
32
|
module Proxy::OpenSCAP
|
31
33
|
extend ::Proxy::Log
|
@@ -22,6 +22,7 @@ module Proxy::OpenSCAP
|
|
22
22
|
:contentdir => File.join(APP_ROOT, 'openscap/content'),
|
23
23
|
:reportsdir => File.join(APP_ROOT, 'openscap/reports'),
|
24
24
|
:failed_dir => File.join(APP_ROOT, 'openscap/failed'),
|
25
|
-
:tailoring_dir => File.join(APP_ROOT, 'openscap/tailoring')
|
25
|
+
:tailoring_dir => File.join(APP_ROOT, 'openscap/tailoring'),
|
26
|
+
:oval_content_dir => File.join(APP_ROOT, 'openscap/oval_content')
|
26
27
|
end
|
27
28
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'smart_proxy_openscap/openscap_exception'
|
2
|
+
require 'openscap_parser/oval_report'
|
3
|
+
|
4
|
+
module Proxy::OpenSCAP
|
5
|
+
class OvalReportParser
|
6
|
+
include Proxy::Log
|
7
|
+
|
8
|
+
def parse_cves(report_data)
|
9
|
+
report = oval_report report_data
|
10
|
+
results = report.definition_results.reduce({}) do |memo, result|
|
11
|
+
memo.tap { |acc| acc[result.definition_id] = parse_cve_res result }
|
12
|
+
end
|
13
|
+
|
14
|
+
report.definitions.map do |definition|
|
15
|
+
results[definition.id].merge(parse_cve_def definition)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def parse_cve_def(definition)
|
22
|
+
refs = definition.references.reduce([]) do |memo, ref|
|
23
|
+
memo.tap { |acc| acc << { :ref_id => ref.ref_id, :ref_url => ref.ref_url } }
|
24
|
+
end
|
25
|
+
|
26
|
+
{ :references => refs, :definition_id => definition.id }
|
27
|
+
end
|
28
|
+
|
29
|
+
def parse_cve_res(result)
|
30
|
+
{ :result => result.result }
|
31
|
+
end
|
32
|
+
|
33
|
+
def oval_report(report_data)
|
34
|
+
decompressed = decompress report_data
|
35
|
+
::OpenscapParser::OvalReport.new(decompressed)
|
36
|
+
end
|
37
|
+
|
38
|
+
def decompress(report_data)
|
39
|
+
begin
|
40
|
+
file = Tempfile.new
|
41
|
+
file.write report_data
|
42
|
+
file.rewind
|
43
|
+
decompressed = `bunzip2 -dc #{file.path}`
|
44
|
+
rescue => e
|
45
|
+
logger.error e
|
46
|
+
raise Proxy::OpenSCAP::ReportDecompressError, "Failed to decompress received report bzip, cause: #{e.message}"
|
47
|
+
ensure
|
48
|
+
file.close
|
49
|
+
file.unlink
|
50
|
+
end
|
51
|
+
decompressed
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'smart_proxy_openscap/storage_fs_common'
|
2
|
+
require 'smart_proxy_openscap/openscap_exception'
|
3
|
+
|
4
|
+
module Proxy::OpenSCAP
|
5
|
+
class OvalReportStorageFs
|
6
|
+
include StorageFsCommon
|
7
|
+
|
8
|
+
def initialize(path_to_dir, oval_policy_id, cname, reported_at)
|
9
|
+
@namespace = 'oval'
|
10
|
+
@reported_at = reported_at
|
11
|
+
@path = "#{path_to_dir}/#{@namespace}/#{oval_policy_id}/#{cname}/"
|
12
|
+
end
|
13
|
+
|
14
|
+
def store_report(report_data)
|
15
|
+
store(report_data, StoreReportError)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def store_file(path_to_store, report_data)
|
21
|
+
target_path = "#{path_to_store}#{@reported_at}"
|
22
|
+
File.open(target_path, 'w') { |f| f.write(report_data) }
|
23
|
+
target_path
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,31 +1,30 @@
|
|
1
|
-
require '
|
1
|
+
require 'openscap_parser/datastream_file'
|
2
|
+
require 'openscap_parser/tailoring_file'
|
2
3
|
|
3
4
|
module Proxy
|
4
5
|
module OpenSCAP
|
5
|
-
class ProfilesParser
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
end
|
6
|
+
class ProfilesParser
|
7
|
+
def profiles(file_type, scap_file)
|
8
|
+
profiles = []
|
9
|
+
error_msg = 'Failed to parse profiles'
|
10
|
+
begin
|
11
|
+
case file_type
|
12
|
+
when 'scap_content'
|
13
|
+
profiles = ::OpenscapParser::DatastreamFile.new(scap_file).benchmark.profiles
|
14
|
+
when 'tailoring_file'
|
15
|
+
profiles = ::OpenscapParser::TailoringFile.new(scap_file).tailoring.profiles
|
16
|
+
else
|
17
|
+
raise OpenSCAPException, "Unknown file type, expected 'scap_content' or 'tailoring_file'"
|
18
|
+
end
|
19
|
+
rescue Nokogiri::XML::SyntaxError
|
20
|
+
raise OpenSCAPException, error_msg
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
"Failure when running script which extracts profiles from scap file"
|
25
|
-
end
|
23
|
+
raise OpenSCAPException, error_msg if profiles.empty?
|
26
24
|
|
27
|
-
|
28
|
-
|
25
|
+
result = profiles.reduce({}) do |memo, profile|
|
26
|
+
memo.tap { |acc| acc[profile.id] = profile.title }
|
27
|
+
end.to_json
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
@@ -52,13 +52,13 @@ module Proxy::OpenSCAP
|
|
52
52
|
|
53
53
|
def forward_arf_file(cname, policy_id, date, arf_file_path)
|
54
54
|
data = File.open(arf_file_path, 'rb') { |io| io.read }
|
55
|
-
post_to_foreman =
|
56
|
-
Proxy::OpenSCAP::
|
55
|
+
post_to_foreman = ForemanArfForwarder.new.post_report(cname, policy_id, date, data, @loaded_settings.timeout)
|
56
|
+
Proxy::OpenSCAP::StorageFs.new(@loaded_settings.reportsdir, cname, post_to_foreman['id'], date).store_archive(data)
|
57
57
|
File.delete arf_file_path
|
58
|
-
rescue Proxy::OpenSCAP::
|
58
|
+
rescue Nokogiri::XML::SyntaxError, Proxy::OpenSCAP::ReportDecompressError => e
|
59
59
|
logger.error "Failed to parse Arf Report at #{arf_file_path}, moving to #{@loaded_settings.corrupted_dir}"
|
60
60
|
|
61
|
-
Proxy::OpenSCAP::
|
61
|
+
Proxy::OpenSCAP::StorageFs.new(@loaded_settings.corrupted_dir, cname, policy_id, date).
|
62
62
|
move_corrupted(arf_file_path.split('/').last, @loaded_settings.spooldir)
|
63
63
|
rescue Proxy::OpenSCAP::ReportUploadError => e
|
64
64
|
logger.error "Failed to upload Arf Report at #{arf_file_path}, cause: #{e.message}, the report will be deleted."
|
@@ -1,8 +1,11 @@
|
|
1
1
|
require 'pathname'
|
2
2
|
require 'smart_proxy_openscap/storage'
|
3
|
+
require 'smart_proxy_openscap/storage_fs_common'
|
3
4
|
|
4
5
|
module Proxy::OpenSCAP
|
5
|
-
class
|
6
|
+
class StorageFs < Storage
|
7
|
+
include StorageFsCommon
|
8
|
+
|
6
9
|
def store_archive(data)
|
7
10
|
store(data, StoreReportError)
|
8
11
|
end
|
@@ -57,9 +60,9 @@ module Proxy::OpenSCAP
|
|
57
60
|
|
58
61
|
private
|
59
62
|
|
60
|
-
def
|
63
|
+
def store_file(path_to_store, data)
|
61
64
|
filename = Digest::SHA256.hexdigest data
|
62
|
-
target_path =
|
65
|
+
target_path = path_to_store + filename
|
63
66
|
File.open(target_path,'w') { |f| f.write(data) }
|
64
67
|
target_path
|
65
68
|
end
|
@@ -91,7 +94,7 @@ module Proxy::OpenSCAP
|
|
91
94
|
end
|
92
95
|
|
93
96
|
begin
|
94
|
-
target_path =
|
97
|
+
target_path = store_file(@path, data)
|
95
98
|
rescue StandardError => e
|
96
99
|
raise error_type, "Could not store file: #{e.message}"
|
97
100
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Proxy::OpenSCAP
|
2
|
+
module StorageFsCommon
|
3
|
+
include ::Proxy::Log
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def create_directory
|
8
|
+
begin
|
9
|
+
FileUtils.mkdir_p @path
|
10
|
+
rescue StandardError => e
|
11
|
+
logger.error "Could not create '#{@path}' directory: #{e.message}"
|
12
|
+
raise e
|
13
|
+
end
|
14
|
+
@path
|
15
|
+
end
|
16
|
+
|
17
|
+
def move(source, error_type)
|
18
|
+
begin
|
19
|
+
create_directory
|
20
|
+
FileUtils.mv source, @path
|
21
|
+
rescue StandardError => e
|
22
|
+
raise error_type, "Could not move file: #{e.message}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def store(data, error_type)
|
27
|
+
begin
|
28
|
+
create_directory
|
29
|
+
rescue StandardError => e
|
30
|
+
raise error_type, "Could not fulfill request: #{e.message}"
|
31
|
+
end
|
32
|
+
|
33
|
+
begin
|
34
|
+
target_path = store_file(@path, data)
|
35
|
+
rescue StandardError => e
|
36
|
+
raise error_type, "Could not store file: #{e.message}"
|
37
|
+
end
|
38
|
+
|
39
|
+
logger.debug "File #{target_path} stored in reports dir."
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -31,3 +31,6 @@
|
|
31
31
|
# Affects sending reports to Foreman (directly and from spool) and fetching scap content or tailoring file
|
32
32
|
# for distribution to clients
|
33
33
|
#:timeout: 60
|
34
|
+
|
35
|
+
# Directory where OpenSCAP OVAL content bzipped XML are stored
|
36
|
+
#:oval_content_dir: /var/lib/openscap/oval_content
|
@@ -14,10 +14,12 @@ Gem::Specification.new do |s|
|
|
14
14
|
|
15
15
|
s.files = `git ls-files`.split("\n") - ['.gitignore']
|
16
16
|
s.executables = ['smart-proxy-openscap-send']
|
17
|
+
s.requirements = 'bzip2'
|
17
18
|
|
18
19
|
s.add_development_dependency('rake')
|
19
20
|
s.add_development_dependency('rack-test')
|
20
21
|
s.add_development_dependency('mocha')
|
21
22
|
s.add_development_dependency('webmock')
|
22
23
|
s.add_dependency 'openscap', '~> 0.4.7'
|
24
|
+
s.add_dependency 'openscap_parser', '~> 1.0.2'
|
23
25
|
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'smart_proxy_openscap'
|
3
|
+
require 'smart_proxy_openscap/openscap_api'
|
4
|
+
|
5
|
+
ENV['RACK_ENV'] = 'test'
|
6
|
+
|
7
|
+
class FetchOvalContentApiTest < Test::Unit::TestCase
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@foreman_url = 'https://foreman.example.com'
|
12
|
+
@fixture_path = "/test/data/rhel-7-including-unpatched.oval.xml.bz2"
|
13
|
+
@fixture_full_path = File.join(Dir.getwd, @fixture_path)
|
14
|
+
Proxy::SETTINGS.stubs(:foreman_url).returns(@foreman_url)
|
15
|
+
@results_path = ("#{Dir.getwd}/test/test_run_files")
|
16
|
+
FileUtils.mkdir_p(@results_path)
|
17
|
+
Proxy::OpenSCAP::Plugin.settings.stubs(:oval_content_dir).returns(@results_path)
|
18
|
+
@oval_content = File.new(@fixture_full_path).read
|
19
|
+
@digest = Digest::SHA256.hexdigest @oval_content
|
20
|
+
@policy_id = 1
|
21
|
+
end
|
22
|
+
|
23
|
+
def teardown
|
24
|
+
FileUtils.rm_rf(Dir.glob("#{@results_path}/*"))
|
25
|
+
end
|
26
|
+
|
27
|
+
def app
|
28
|
+
::Proxy::OpenSCAP::Api.new
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_get_oval_content_from_file
|
32
|
+
FileUtils.mkdir("#{@results_path}/#{@policy_id}")
|
33
|
+
FileUtils.cp(@fixture_full_path, "#{@results_path}/#{@policy_id}/#{@digest}.oval.xml.bz2")
|
34
|
+
get "/oval_policies/#{@policy_id}/oval_content/#{@digest}"
|
35
|
+
assert_equal("application/x-bzip2", last_response.header["Content-Type"], "Response header should be application/x-bzip2")
|
36
|
+
assert(last_response.successful?, "Response should be success")
|
37
|
+
end
|
38
|
+
end
|
data/test/fetch_scap_api_test.rb
CHANGED
@@ -54,7 +54,7 @@ class FetchScapApiTest < Test::Unit::TestCase
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_get_scap_content_permissions
|
57
|
-
Proxy::OpenSCAP::
|
57
|
+
Proxy::OpenSCAP::FetchScapFile.any_instance.stubs(:fetch).raises(Errno::EACCES)
|
58
58
|
stub_request(:get, "#{@foreman_url}/api/v2/compliance/policies/#{@policy_id}/content").to_return(:body => @scap_content)
|
59
59
|
get "/policies/#{@policy_id}/content/#{@digest}"
|
60
60
|
assert_equal(500, last_response.status, "No permissions should raise error 500")
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'smart_proxy_openscap'
|
3
|
+
require 'smart_proxy_openscap/oval_report_parser'
|
4
|
+
|
5
|
+
class OvalReportParserTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_oval_report_parsing
|
8
|
+
oval_report = File.open("#{Dir.getwd}/test/data/oval-results.xml.bz2").read
|
9
|
+
res = Proxy::OpenSCAP::OvalReportParser.new.parse_cves oval_report
|
10
|
+
refute res.empty?
|
11
|
+
assert res.first[:result]
|
12
|
+
refute res.first[:references].empty?
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'smart_proxy_openscap'
|
3
|
+
require 'smart_proxy_openscap/openscap_api'
|
4
|
+
|
5
|
+
ENV['RACK_ENV'] = 'test'
|
6
|
+
|
7
|
+
class PostOvalReportApiTest < Test::Unit::TestCase
|
8
|
+
include Rack::Test::Methods
|
9
|
+
|
10
|
+
setup do
|
11
|
+
@foreman_url = 'https://foreman.example.com'
|
12
|
+
Proxy::SETTINGS.stubs(:foreman_url).returns(@foreman_url)
|
13
|
+
@oval_report = File.open("#{Dir.getwd}/test/data/oval-results.xml.bz2").read
|
14
|
+
@cname = 'node.example.org'
|
15
|
+
@date = Time.now.to_i
|
16
|
+
@policy_id = 1
|
17
|
+
Proxy::OpenSCAP.stubs(:common_name).returns(@cname)
|
18
|
+
end
|
19
|
+
|
20
|
+
def app
|
21
|
+
::Proxy::OpenSCAP::Api.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_post_oval_report_to_foreman
|
25
|
+
stub_request(:post, "#{@foreman_url}/api/v2/compliance/oval_reports/#{@cname}/#{@policy_id}/#{@date}")
|
26
|
+
.to_return(:status => 200, :body => '{ "result": "ok" }')
|
27
|
+
post "/oval_reports/#{@policy_id}", @oval_report, 'CONTENT_TYPE' => 'text/xml', 'CONTENT_ENCODING' => 'x-bzip2'
|
28
|
+
assert(last_response.successful?, "Should be a success")
|
29
|
+
end
|
30
|
+
end
|