smart_proxy_openscap 0.8.1 → 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/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 +47 -23
- 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/spool_forwarder.rb +3 -3
- 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 +1 -2
- 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
- metadata +15 -6
- data/lib/smart_proxy_openscap/fetch_scap_content.rb +0 -17
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4d1a52c4299cb9828c2cb492622a4cc13f9cd3d234fc6a9940dc9768840761b
|
4
|
+
data.tar.gz: bfa0d9e1444579127bec7bf5f32fe37d73577c99ab803dfd07b59c0ed6f1e15a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49c8ece151fb60cdf31a3c396b22df455d3f653648640f5e6d05505c67c386128bf09970bd9d08b82e94710d493c3ba8dd003d47d584e05819204cd32987ffc3
|
7
|
+
data.tar.gz: 18acfdafceecc4845da3fce3703f7e670d0c991ddda0d6cb542c00ccb6d399cd4090b63cf44dbeb2fef5aa99cfa0fb7710a2a54cf5b6d4b1678f98b70d01678b
|
@@ -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
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
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
|
@@ -33,35 +33,37 @@ module Proxy::OpenSCAP
|
|
33
33
|
do_authorize_with_trusted_hosts
|
34
34
|
end
|
35
35
|
|
36
|
-
|
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
|
-
|
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 =
|
48
|
-
Proxy::OpenSCAP::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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::
|
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,
|
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/
|
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::
|
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,
|
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 "/
|
124
|
-
content_type 'application/
|
142
|
+
get "/oval_policies/:oval_policy_id/oval_content/:digest" do
|
143
|
+
content_type 'application/x-bzip2'
|
125
144
|
begin
|
126
|
-
Proxy::OpenSCAP::
|
127
|
-
|
128
|
-
|
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::
|
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::
|
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
|
@@ -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
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
|
@@ -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
|
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
|
@@ -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::
|
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::
|
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,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_openscap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Šimon Lukašík
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2021-
|
13
|
+
date: 2021-05-18 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.
|
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.
|
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/
|
128
|
-
- lib/smart_proxy_openscap/
|
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
|
@@ -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
|