smart_proxy_openscap 0.8.1 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. checksums.yaml +4 -4
  2. data/lib/smart_proxy_openscap/content_parser.rb +5 -2
  3. data/lib/smart_proxy_openscap/fetch_scap_file.rb +45 -0
  4. data/lib/smart_proxy_openscap/foreman_arf_forwarder.rb +15 -0
  5. data/lib/smart_proxy_openscap/foreman_forwarder.rb +19 -16
  6. data/lib/smart_proxy_openscap/foreman_oval_forwarder.rb +19 -0
  7. data/lib/smart_proxy_openscap/openscap_api.rb +48 -24
  8. data/lib/smart_proxy_openscap/openscap_html_generator.rb +1 -1
  9. data/lib/smart_proxy_openscap/openscap_import_api.rb +3 -3
  10. data/lib/smart_proxy_openscap/openscap_lib.rb +5 -3
  11. data/lib/smart_proxy_openscap/openscap_plugin.rb +2 -1
  12. data/lib/smart_proxy_openscap/oval_report_parser.rb +54 -0
  13. data/lib/smart_proxy_openscap/oval_report_storage_fs.rb +26 -0
  14. data/lib/smart_proxy_openscap/spool_forwarder.rb +3 -3
  15. data/lib/smart_proxy_openscap/storage.rb +0 -2
  16. data/lib/smart_proxy_openscap/storage_fs.rb +7 -4
  17. data/lib/smart_proxy_openscap/storage_fs_common.rb +42 -0
  18. data/lib/smart_proxy_openscap/version.rb +1 -1
  19. data/settings.d/openscap.yml.example +3 -0
  20. data/smart_proxy_openscap.gemspec +1 -2
  21. data/test/data/oval-results.xml.bz2 +0 -0
  22. data/test/data/rhel-7-including-unpatched.oval.xml.bz2 +0 -0
  23. data/test/fetch_oval_content_api_test.rb +38 -0
  24. data/test/fetch_scap_api_test.rb +1 -1
  25. data/test/oval_report_parser_test.rb +14 -0
  26. data/test/post_oval_report_api_test.rb +30 -0
  27. data/test/post_report_api_test.rb +2 -2
  28. metadata +19 -10
  29. data/lib/smart_proxy_openscap/fetch_scap_content.rb +0 -17
  30. data/lib/smart_proxy_openscap/fetch_tailoring_file.rb +0 -17
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57a129c56c494eaf735088a59fedaaeddd7a0af1c8e4aed8424e76c94415e515
4
- data.tar.gz: 9e9b31ccd266403eae40237a47ab8852b1799ea520684a4facbc2229595e4566
3
+ metadata.gz: a5a3c7008ebc9f55a9fcd647cbfdb606873a3f552d1a24aa5a0e757799105a33
4
+ data.tar.gz: e4bcfac7b8139a743af163706a38473217b93fa6e56f11161de0f35ee7b794c2
5
5
  SHA512:
6
- metadata.gz: dc046f2f2ff1d9f78a0bffbadb1c671b731e5ac0457eff5620d01dde5cf0a299cc0b0ba475abc02e70e64ad919edf721610a9c010aade74ff54ab0ff574241dd
7
- data.tar.gz: 20c7e65b5452fb3f1b5107e90522b7403c4ed00c2564969f85858a0ddaab14cc57aa41bd31de4e968dbee52e8756ec30c2f1c01ad19248b15d69f76db6df20c8
6
+ metadata.gz: bcf32ad8d12435f95129e2c07648bcc81d6a0dd3d752f722ef7e042f222861676b728ced19eed64e7d1479306894e49865691bdd843443543c07eb199c596283
7
+ data.tar.gz: fcb0f9f37c84202ee094c4d5810a7687559b72d3bd593cbb40e5cfe6757ba9e9200dea534e4a7f09e75b84b347c162112a3cca677887b717e56adb7474b1fd0d
@@ -3,8 +3,10 @@ require 'openscap_parser/tailoring_file'
3
3
 
4
4
  module Proxy::OpenSCAP
5
5
  class ContentParser
6
+ include ::Proxy::Log
7
+
6
8
  def validate(file_type, scap_file)
7
- msg = 'Invalid SCAP file type'
9
+ msg = 'Invalid XML format'
8
10
  errors = []
9
11
  file = nil
10
12
  begin
@@ -14,8 +16,9 @@ module Proxy::OpenSCAP
14
16
  when 'tailoring_file'
15
17
  file = ::OpenscapParser::TailoringFile.new(scap_file)
16
18
  end
17
- errors << msg unless file.valid?
18
19
  rescue Nokogiri::XML::SyntaxError => e
20
+ logger.error msg
21
+ logger.error e.backtrace.join("\n")
19
22
  errors << msg
20
23
  end
21
24
  { errors: errors }
@@ -0,0 +1,45 @@
1
+ require 'smart_proxy_openscap/fetch_file'
2
+
3
+ module Proxy::OpenSCAP
4
+ class FetchScapFile < FetchFile
5
+ def initialize(type)
6
+ raise "Expected one of the following symbols: #{allowed_types.join(', ')}, got: #{type}" unless allowed_types.include? type
7
+ @type = type
8
+ end
9
+
10
+ def fetch(policy_id, digest, content_dir)
11
+ store_dir = File.join(Proxy::OpenSCAP.fullpath(content_dir), policy_id.to_s)
12
+ scap_file = File.join(store_dir, file_name(policy_id, digest))
13
+
14
+ file_download_path = download_path.sub(':policy_id', policy_id)
15
+ create_store_dir store_dir
16
+ file = policy_content_file scap_file
17
+ clean_store_folder store_dir unless file
18
+ file ||= save_or_serve_scap_file scap_file, file_download_path
19
+ end
20
+
21
+ def download_path
22
+ case @type
23
+ when :scap_content
24
+ "api/v2/compliance/policies/:policy_id/content"
25
+ when :tailoring_file
26
+ "api/v2/compliance/policies/:policy_id/tailoring"
27
+ when :oval_content
28
+ "api/v2/compliance/oval_policies/:policy_id/oval_content"
29
+ end
30
+ end
31
+
32
+ def file_name(policy_id, digest)
33
+ case @type
34
+ when :scap_content, :tailoring_file
35
+ "#{policy_id}_#{digest}.xml"
36
+ when :oval_content
37
+ "#{digest}.oval.xml.bz2"
38
+ end
39
+ end
40
+
41
+ def allowed_types
42
+ [:scap_content, :tailoring_file, :oval_content]
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,15 @@
1
+ require 'smart_proxy_openscap/foreman_forwarder'
2
+
3
+ module Proxy::OpenSCAP
4
+ class ForemanArfForwarder < ForemanForwarder
5
+ private
6
+
7
+ def parse_report(cname, policy_id, date, report_data)
8
+ Proxy::OpenSCAP::ArfParser.new(cname, policy_id, date).as_json(report_data)
9
+ end
10
+
11
+ def report_upload_path(cname, policy_id, date)
12
+ upload_path "arf_reports", cname, policy_id, date
13
+ end
14
+ end
15
+ end
@@ -4,26 +4,29 @@ module Proxy::OpenSCAP
4
4
  class ForemanForwarder < Proxy::HttpRequest::ForemanRequest
5
5
  include ::Proxy::Log
6
6
 
7
- def post_arf_report(cname, policy_id, date, data, timeout)
8
- begin
9
- foreman_api_path = upload_path(cname, policy_id, date)
10
- json = Proxy::OpenSCAP::ArfParser.new(cname, policy_id, date).as_json(data)
11
- response = send_request(foreman_api_path, json, timeout)
12
- # Raise an HTTP error if the response is not 2xx (success).
13
- response.value
14
- JSON.parse(response.body)
15
- rescue Net::HTTPServerException => e
16
- logger.debug "Received response: #{response.code} #{response.msg}"
17
- logger.debug response.body
18
- raise ReportUploadError, e.message if response.code.to_i == 422
19
- raise e
20
- end
7
+ def post_report(cname, policy_id, date, data, timeout)
8
+ foreman_api_path = report_upload_path(cname, policy_id, date)
9
+
10
+ json = parse_report(cname, policy_id, date, data)
11
+ response = send_request(foreman_api_path, json, timeout)
12
+ # Raise an HTTP error if the response is not 2xx (success).
13
+ response.value
14
+ JSON.parse(response.body)
15
+ rescue Net::HTTPServerException => e
16
+ logger.debug "Received response: #{response.code} #{response.msg}"
17
+ logger.debug response.body
18
+ raise ReportUploadError, e.message if response.code.to_i == 422
19
+ raise e
21
20
  end
22
21
 
23
22
  private
24
23
 
25
- def upload_path(cname, policy_id, date)
26
- "/api/v2/compliance/arf_reports/#{cname}/#{policy_id}/#{date}"
24
+ def upload_path(resource, cname, policy_id, date)
25
+ "/api/v2/compliance/#{resource}/#{cname}/#{policy_id}/#{date}"
26
+ end
27
+
28
+ def parse_report(cname, policy_id, date, data)
29
+ raise NotImplementedError
27
30
  end
28
31
 
29
32
  def send_request(path, body, timeout)
@@ -0,0 +1,19 @@
1
+ require 'smart_proxy_openscap/foreman_forwarder'
2
+
3
+ module Proxy::OpenSCAP
4
+ class ForemanOvalForwarder < ForemanForwarder
5
+ private
6
+
7
+ def parse_report(cname, policy_id, date, report_data)
8
+ {
9
+ :oval_results => OvalReportParser.new.parse_cves(report_data),
10
+ :oval_policy_id => policy_id,
11
+ :cname => cname
12
+ }.to_json
13
+ end
14
+
15
+ def report_upload_path(cname, policy_id, date)
16
+ upload_path "oval_reports", cname, policy_id, date
17
+ end
18
+ end
19
+ end
@@ -25,7 +25,7 @@ module Proxy::OpenSCAP
25
25
  include ::Proxy::Log
26
26
  helpers ::Proxy::Helpers
27
27
  authorize_with_ssl_client
28
- CLIENT_PATHS = Regexp.compile(%r{^(/arf/\d+|/policies/\d+/content/|/policies/\d+/tailoring/)})
28
+ CLIENT_PATHS = Regexp.compile(%r{^(/arf/\d+|/policies/\d+/content/|/policies/\d+/tailoring/|/oval_reports|/oval_policies)})
29
29
 
30
30
  # authorize via trusted hosts but let client paths in without such authorization
31
31
  before do
@@ -33,35 +33,37 @@ module Proxy::OpenSCAP
33
33
  do_authorize_with_trusted_hosts
34
34
  end
35
35
 
36
- post "/arf/:policy" do
37
- # first let's verify client's certificate
36
+ before '(/arf/*|/oval_reports/*)' do
38
37
  begin
39
- cn = Proxy::OpenSCAP::common_name request
38
+ @cn = Proxy::OpenSCAP::common_name request
40
39
  rescue Proxy::Error::Unauthorized => e
41
40
  log_halt 403, "Client authentication failed: #{e.message}"
42
41
  end
43
- date = Time.now.to_i
42
+ @reported_at = Time.now.to_i
43
+ end
44
+
45
+ post "/arf/:policy" do
44
46
  policy = params[:policy]
45
47
 
46
48
  begin
47
- post_to_foreman = ForemanForwarder.new.post_arf_report(cn, policy, date, request.body.string, Proxy::OpenSCAP::Plugin.settings.timeout)
48
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, cn, post_to_foreman['id'], date).store_archive(request.body.string)
49
+ post_to_foreman = ForemanArfForwarder.new.post_report(@cn, policy, @reported_at, request.body.string, Proxy::OpenSCAP::Plugin.settings.timeout)
50
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, @cn, post_to_foreman['id'], @reported_at).store_archive(request.body.string)
49
51
  post_to_foreman.to_json
50
52
  rescue Proxy::OpenSCAP::StoreReportError => e
51
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.failed_dir, cn, post_to_foreman['id'], date).store_failed(request.body.string)
53
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.failed_dir, @cn, post_to_foreman['id'], @reported_at).store_failed(request.body.string)
52
54
  logger.error "Failed to save Report in reports directory (#{Proxy::OpenSCAP::Plugin.settings.reportsdir}). Failed with: #{e.message}.
53
55
  Saving file in #{Proxy::OpenSCAP::Plugin.settings.failed_dir}. Please copy manually to #{Proxy::OpenSCAP::Plugin.settings.reportsdir}"
54
56
  { :result => 'Storage failure on proxy, see proxy logs for details' }.to_json
55
57
  rescue Nokogiri::XML::SyntaxError => e
56
58
  error = "Failed to parse Arf Report, moving to #{Proxy::OpenSCAP::Plugin.settings.corrupted_dir}"
57
59
  logger.error error
58
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.corrupted_dir, cn, policy, date).store_corrupted(request.body.string)
60
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.corrupted_dir, @cn, policy, @reported_at).store_corrupted(request.body.string)
59
61
  { :result => (error << ' on proxy') }.to_json
60
62
  rescue *HTTP_ERRORS => e
61
63
  ### If the upload to foreman fails then store it in the spooldir
62
64
  msg = "Failed to upload to Foreman, saving in spool. Failed with: #{e.message}"
63
65
  logger.error msg
64
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.spooldir, cn, policy, date).store_spool(request.body.string)
66
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.spooldir, @cn, policy, @reported_at).store_spool(request.body.string)
65
67
  { :result => msg }.to_json
66
68
  rescue Proxy::OpenSCAP::StoreSpoolError => e
67
69
  log_halt 500, e.message
@@ -70,10 +72,26 @@ module Proxy::OpenSCAP
70
72
  end
71
73
  end
72
74
 
75
+ post "/oval_reports/:oval_policy_id" do
76
+ ForemanOvalForwarder.new.post_report(@cn, params[:oval_policy_id], @reported_at, request.body.string, Plugin.settings.timeout)
77
+
78
+ { :reported_at => Time.at(@reported_at) }.to_json
79
+ rescue *HTTP_ERRORS => e
80
+ msg = "Failed to upload to Foreman, failed with: #{e.message}"
81
+ logger.error e
82
+ { :result => msg }.to_json
83
+ rescue Nokogiri::XML::SyntaxError => e
84
+ logger.error e
85
+ { :result => 'Failed to parse OVAL report, see proxy logs for details' }.to_json
86
+ rescue Proxy::OpenSCAP::ReportUploadError, Proxy::OpenSCAP::ReportDecompressError => e
87
+ { :result => e.message }.to_json
88
+ end
89
+
90
+
73
91
  get "/arf/:id/:cname/:date/:digest/xml" do
74
92
  content_type 'application/x-bzip2'
75
93
  begin
76
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, params[:cname], params[:id], params[:date]).get_arf_xml(params[:digest])
94
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, params[:cname], params[:id], params[:date]).get_arf_xml(params[:digest])
77
95
  rescue FileNotFound => e
78
96
  log_halt 500, "Could not find requested file, #{e.message}"
79
97
  end
@@ -81,7 +99,7 @@ module Proxy::OpenSCAP
81
99
 
82
100
  delete "/arf/:id/:cname/:date/:digest" do
83
101
  begin
84
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, params[:cname], params[:id], params[:date]).delete_arf_file
102
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, params[:cname], params[:id], params[:date]).delete_arf_file
85
103
  rescue FileNotFound => e
86
104
  logger.debug "Could not find requested file, #{e.message} - Assuming deleted"
87
105
  end
@@ -100,32 +118,34 @@ module Proxy::OpenSCAP
100
118
  get "/policies/:policy_id/content/:digest" do
101
119
  content_type 'application/xml'
102
120
  begin
103
- Proxy::OpenSCAP::FetchScapContent.new.get_policy_content(params[:policy_id], params[:digest])
121
+ Proxy::OpenSCAP::FetchScapFile.new(:scap_content)
122
+ .fetch(params[:policy_id], params[:digest], Proxy::OpenSCAP::Plugin.settings.contentdir)
104
123
  rescue *HTTP_ERRORS => e
105
- log_halt e.response.code.to_i, "File not found on Foreman. Wrong policy id?"
124
+ log_halt e.response.code.to_i, file_not_found_msg
106
125
  rescue StandardError => e
107
126
  log_halt 500, "Error occurred: #{e.message}"
108
127
  end
109
128
  end
110
129
 
111
- get "/policies/:policy_id/content" do
130
+ get "/policies/:policy_id/tailoring/:digest" do
112
131
  content_type 'application/xml'
113
- logger.warn 'DEPRECATION WARNING: /policies/:policy_id/content/:digest should be used, please update foreman_openscap'
114
132
  begin
115
- Proxy::OpenSCAP::FetchScapContent.new.get_policy_content(params[:policy_id], 'scap_content')
133
+ Proxy::OpenSCAP::FetchScapFile.new(:tailoring_file)
134
+ .fetch(params[:policy_id], params[:digest], Proxy::OpenSCAP::Plugin.settings.tailoring_dir)
116
135
  rescue *HTTP_ERRORS => e
117
- log_halt e.response.code.to_i, "File not found on Foreman. Wrong policy id?"
136
+ log_halt e.response.code.to_i, file_not_found_msg
118
137
  rescue StandardError => e
119
138
  log_halt 500, "Error occurred: #{e.message}"
120
139
  end
121
140
  end
122
141
 
123
- get "/policies/:policy_id/tailoring/:digest" do
124
- content_type 'application/xml'
142
+ get "/oval_policies/:oval_policy_id/oval_content/:digest" do
143
+ content_type 'application/x-bzip2'
125
144
  begin
126
- Proxy::OpenSCAP::FetchTailoringFile.new.get_tailoring_file(params[:policy_id], params[:digest])
127
- rescue *HTTP_ERRORS => e
128
- log_halt e.response.code.to_i, "File not found on Foreman. Wrong policy id?"
145
+ Proxy::OpenSCAP::FetchScapFile.new(:oval_content)
146
+ .fetch(params[:oval_policy_id], params[:digest], Proxy::OpenSCAP::Plugin.settings.oval_content_dir)
147
+ rescue *HTTP => e
148
+ log_halt e.response.code.to_i, file_not_found_msg
129
149
  rescue StandardError => e
130
150
  log_halt 500, "Error occurred: #{e.message}"
131
151
  end
@@ -173,7 +193,7 @@ module Proxy::OpenSCAP
173
193
 
174
194
  get "/spool_errors" do
175
195
  begin
176
- Proxy::OpenSCAP::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.corrupted_dir, nil, nil, nil).spool_errors.to_json
196
+ Proxy::OpenSCAP::StorageFs.new(Proxy::OpenSCAP::Plugin.settings.corrupted_dir, nil, nil, nil).spool_errors.to_json
177
197
  rescue StandardError => e
178
198
  log_halt 500, "Error occurred: #{e.message}"
179
199
  end
@@ -190,5 +210,9 @@ module Proxy::OpenSCAP
190
210
  log_halt 500, "Error occurred: #{e.message}"
191
211
  end
192
212
  end
213
+
214
+ def file_not_found_msg
215
+ "File not found on Foreman. Wrong policy id?"
216
+ end
193
217
  end
194
218
  end
@@ -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::StorageFS.new(path_to_dir, @cname, @id, @date)
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::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.reportsdir, cn, post_to_foreman['id'], date).store_archive(request.body.string)
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::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.failed_dir, cn, post_to_foreman['id'], date).store_failed(request.body.string)
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::StorageFS.new(Proxy::OpenSCAP::Plugin.settings.spooldir, cn, policy, date).store_spool(request.body.string)
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/fetch_scap_content'
20
- require 'smart_proxy_openscap/foreman_forwarder'
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
@@ -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 = ForemanForwarder.new.post_arf_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)
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
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::StorageFS.new(@loaded_settings.corrupted_dir, cname, policy_id, date).
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."
@@ -2,8 +2,6 @@ require 'smart_proxy_openscap/openscap_exception'
2
2
 
3
3
  module Proxy::OpenSCAP
4
4
  class Storage
5
- include ::Proxy::Log
6
-
7
5
  def initialize(path_to_dir, cname, id, date)
8
6
  @namespace = 'arf'
9
7
  @cname = cname
@@ -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 StorageFS < Storage
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 store_arf(spool_arf_dir, data)
63
+ def store_file(path_to_store, data)
61
64
  filename = Digest::SHA256.hexdigest data
62
- target_path = spool_arf_dir + filename
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 = store_arf(@path, data)
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
@@ -10,6 +10,6 @@
10
10
 
11
11
  module Proxy
12
12
  module OpenSCAP
13
- VERSION = '0.8.1'
13
+ VERSION = '0.9.2'
14
14
  end
15
15
  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
@@ -21,6 +21,5 @@ Gem::Specification.new do |s|
21
21
  s.add_development_dependency('mocha')
22
22
  s.add_development_dependency('webmock')
23
23
  s.add_dependency 'openscap', '~> 0.4.7'
24
-
25
- s.add_dependency 'openscap_parser', '~> 1.0.1'
24
+ s.add_dependency 'openscap_parser', '~> 1.0.2'
26
25
  end
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
@@ -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::FetchScapContent.any_instance.stubs(:get_policy_content).raises(Errno::EACCES)
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
@@ -58,7 +58,7 @@ class OpenSCAPApiTest < Test::Unit::TestCase
58
58
  def test_fail_save_file_should_raise_error
59
59
  @policy_id = 2
60
60
  stub_request(:post, "#{@foreman_url}/api/v2/compliance/arf_reports/#{@cname}/#{@policy_id}/#{@date}").to_return(:status => 500, :body => "{\"result\":\"server error\"}")
61
- Proxy::OpenSCAP::StorageFS.any_instance.stubs(:create_directory).raises(StandardError)
61
+ Proxy::OpenSCAP::StorageFs.any_instance.stubs(:create_directory).raises(StandardError)
62
62
  post "/arf/#{@policy_id}", @arf_report, 'CONTENT_TYPE' => 'text/xml', 'CONTENT_ENCODING' => 'x-bzip2'
63
63
  assert(last_response.server_error?, "Should return 500")
64
64
  refute(File.file?("#{@results_path}/spool/arf/#{@cname}/#{@policy_id}/#{@date}/#{@filename}"), "File should be saved in spool directory")
@@ -67,7 +67,7 @@ class OpenSCAPApiTest < Test::Unit::TestCase
67
67
  def test_success_post_fail_save_should_save_spool
68
68
  stub_request(:post, "#{@foreman_url}/api/v2/compliance/arf_reports/#{@cname}/#{@policy_id}/#{@date}")
69
69
  .to_return(:status => 200, :body => "{\"result\":\"OK\",\"id\":\"#{@arf_id}\"}")
70
- Proxy::OpenSCAP::StorageFS.any_instance.stubs(:store_archive).raises(Proxy::OpenSCAP::StoreReportError)
70
+ Proxy::OpenSCAP::StorageFs.any_instance.stubs(:store_archive).raises(Proxy::OpenSCAP::StoreReportError)
71
71
  post "/arf/#{@policy_id}", @arf_report, 'CONTENT_TYPE' => 'text/xml', 'CONTENT_ENCODING' => 'x-bzip2'
72
72
  refute(File.file?("#{@results_path}/spool/arf/#{@cname}/#{@policy_id}/#{@date}/#{@filename}"), "File should not be in spool directory")
73
73
  refute(File.file?("#{@results_path}/reports/arf/#{@cname}/#{@arf_id}/#{@date}/#{@filename}"), "File should not be in Reports directory")
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_openscap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Šimon Lukašík
8
8
  - Shlomi Zadok
9
9
  - Marek Hulan
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2021-04-22 00:00:00.000000000 Z
13
+ date: 2022-04-05 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rake
@@ -88,14 +88,14 @@ dependencies:
88
88
  requirements:
89
89
  - - "~>"
90
90
  - !ruby/object:Gem::Version
91
- version: 1.0.1
91
+ version: 1.0.2
92
92
  type: :runtime
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
96
  - - "~>"
97
97
  - !ruby/object:Gem::Version
98
- version: 1.0.1
98
+ version: 1.0.2
99
99
  description: |-
100
100
  A plug-in to the Foreman's smart-proxy which receives
101
101
  bzip2ed ARF files and forwards them to the Foreman.
@@ -124,9 +124,10 @@ files:
124
124
  - lib/smart_proxy_openscap/arf_parser.rb
125
125
  - lib/smart_proxy_openscap/content_parser.rb
126
126
  - lib/smart_proxy_openscap/fetch_file.rb
127
- - lib/smart_proxy_openscap/fetch_scap_content.rb
128
- - lib/smart_proxy_openscap/fetch_tailoring_file.rb
127
+ - lib/smart_proxy_openscap/fetch_scap_file.rb
128
+ - lib/smart_proxy_openscap/foreman_arf_forwarder.rb
129
129
  - lib/smart_proxy_openscap/foreman_forwarder.rb
130
+ - lib/smart_proxy_openscap/foreman_oval_forwarder.rb
130
131
  - lib/smart_proxy_openscap/http_config.ru
131
132
  - lib/smart_proxy_openscap/openscap_api.rb
132
133
  - lib/smart_proxy_openscap/openscap_exception.rb
@@ -134,6 +135,8 @@ files:
134
135
  - lib/smart_proxy_openscap/openscap_import_api.rb
135
136
  - lib/smart_proxy_openscap/openscap_lib.rb
136
137
  - lib/smart_proxy_openscap/openscap_plugin.rb
138
+ - lib/smart_proxy_openscap/oval_report_parser.rb
139
+ - lib/smart_proxy_openscap/oval_report_storage_fs.rb
137
140
  - lib/smart_proxy_openscap/policy_guide.rb
138
141
  - lib/smart_proxy_openscap/policy_parser.rb
139
142
  - lib/smart_proxy_openscap/profiles_parser.rb
@@ -141,11 +144,14 @@ files:
141
144
  - lib/smart_proxy_openscap/spool_forwarder.rb
142
145
  - lib/smart_proxy_openscap/storage.rb
143
146
  - lib/smart_proxy_openscap/storage_fs.rb
147
+ - lib/smart_proxy_openscap/storage_fs_common.rb
144
148
  - lib/smart_proxy_openscap/version.rb
145
149
  - settings.d/openscap.yml.example
146
150
  - smart_proxy_openscap.gemspec
147
151
  - test/data/arf_report
148
152
  - test/data/corrupted_arf_report
153
+ - test/data/oval-results.xml.bz2
154
+ - test/data/rhel-7-including-unpatched.oval.xml.bz2
149
155
  - test/data/spool/cleanup_spool/arf/2c101b95-033f-4b15-b490-f50bf9090dae/1/1484313035/fa2f68ffb944c917332a284dc63ec7f8fa76990cb815ddcad3318b5d9457f8a1
150
156
  - test/data/spool/cleanup_spool/arf/e20b9695-f655-401a-9dda-8cca7a47a8c0/1/1484309984/fa2f68ffb944c917332a284dc63ec7f8fa76990cb815ddcad3318b5d9457f8a1
151
157
  - test/data/spool/corrupted_spool/arf/e20b9695-f655-401a-9dda-8cca7a47a8c0/1/1484309984/a4dfba5db27b21795e6fa401b8dce7a70faeb25b7963891f07f6f4baaf052afb
@@ -154,9 +160,12 @@ files:
154
160
  - test/data/spool/valid_spool/arf/e20b9695-f655-401a-9dda-8cca7a47a8c0/1/1484313035/fa2f68ffb944c917332a284dc63ec7f8fa76990cb815ddcad3318b5d9457f8a1
155
161
  - test/data/ssg-rhel7-ds.xml
156
162
  - test/data/tailoring.xml
163
+ - test/fetch_oval_content_api_test.rb
157
164
  - test/fetch_scap_api_test.rb
158
165
  - test/fetch_tailoring_api_test.rb
159
166
  - test/get_report_xml_html_test.rb
167
+ - test/oval_report_parser_test.rb
168
+ - test/post_oval_report_api_test.rb
160
169
  - test/post_report_api_test.rb
161
170
  - test/scap_content_parser_api_test.rb
162
171
  - test/script_class_test.rb
@@ -166,7 +175,7 @@ homepage: https://github.com/theforeman/smart_proxy_openscap
166
175
  licenses:
167
176
  - GPL-3.0-or-later
168
177
  metadata: {}
169
- post_install_message:
178
+ post_install_message:
170
179
  rdoc_options: []
171
180
  require_paths:
172
181
  - lib
@@ -182,8 +191,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
182
191
  version: '0'
183
192
  requirements:
184
193
  - bzip2
185
- rubygems_version: 3.1.2
186
- signing_key:
194
+ rubygems_version: 3.0.3
195
+ signing_key:
187
196
  specification_version: 4
188
197
  summary: OpenSCAP plug-in for Foreman's smart-proxy.
189
198
  test_files: []
@@ -1,17 +0,0 @@
1
- require 'smart_proxy_openscap/fetch_file'
2
-
3
- module Proxy::OpenSCAP
4
- class FetchScapContent < FetchFile
5
- def get_policy_content(policy_id, digest)
6
- policy_store_dir = File.join(Proxy::OpenSCAP.fullpath(Proxy::OpenSCAP::Plugin.settings.contentdir), policy_id.to_s)
7
- policy_scap_file = File.join(policy_store_dir, "#{policy_id}_#{digest}.xml")
8
- file_download_path = "api/v2/compliance/policies/#{policy_id}/content"
9
-
10
- create_store_dir policy_store_dir
11
-
12
- scap_file = policy_content_file(policy_scap_file)
13
- clean_store_folder(policy_store_dir) unless scap_file
14
- scap_file ||= save_or_serve_scap_file(policy_scap_file, file_download_path)
15
- end
16
- end
17
- end
@@ -1,17 +0,0 @@
1
- require 'smart_proxy_openscap/fetch_file'
2
-
3
- module Proxy::OpenSCAP
4
- class FetchTailoringFile < FetchFile
5
- def get_tailoring_file(policy_id, digest)
6
- store_dir = File.join(Proxy::OpenSCAP.fullpath(Proxy::OpenSCAP::Plugin.settings.tailoring_dir), policy_id.to_s)
7
- policy_tailoring_file = File.join(store_dir, "#{policy_id}_#{digest}.xml")
8
- file_download_path = "api/v2/compliance/policies/#{policy_id}/tailoring"
9
-
10
- create_store_dir store_dir
11
-
12
- scap_file = policy_content_file(policy_tailoring_file)
13
- clean_store_folder(store_dir) unless scap_file
14
- scap_file ||= save_or_serve_scap_file(policy_tailoring_file, file_download_path)
15
- end
16
- end
17
- end