inspec 0.18.0 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +35 -2
- data/README.md +27 -2
- data/docs/resources.rst +49 -3
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/README.md +2 -1
- data/lib/bundles/inspec-compliance/api.rb +60 -102
- data/lib/bundles/inspec-compliance/cli.rb +133 -14
- data/lib/bundles/inspec-compliance/configuration.rb +43 -1
- data/lib/bundles/inspec-compliance/http.rb +80 -0
- data/lib/bundles/inspec-compliance.rb +1 -0
- data/lib/inspec/metadata.rb +40 -27
- data/lib/inspec/objects/test.rb +2 -1
- data/lib/inspec/resource.rb +1 -0
- data/lib/inspec/rspec_json_formatter.rb +1 -1
- data/lib/inspec/runner.rb +19 -13
- data/lib/inspec/runner_rspec.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +32 -13
- data/lib/resources/grub_conf.rb +186 -0
- data/lib/resources/json.rb +1 -1
- data/lib/resources/service.rb +9 -3
- data/lib/utils/base_cli.rb +2 -1
- data/lib/utils/hash_map.rb +37 -0
- data/test/functional/inspec_compliance_test.rb +60 -0
- data/test/functional/inspec_exec_test.rb +49 -10
- data/test/helper.rb +3 -0
- data/test/integration/default/compare_matcher_spec.rb +21 -0
- data/test/unit/metadata_test.rb +49 -23
- data/test/unit/mock/cmd/systemctl-show-all-dbus +6 -0
- data/test/unit/mock/files/grub.conf +21 -0
- data/test/unit/mock/profiles/resource-tiny/inspec.yml +10 -0
- data/test/unit/mock/profiles/resource-tiny/libraries/resource.rb +3 -0
- data/test/unit/mock/profiles/supported_inspec/inspec.yml +2 -0
- data/test/unit/mock/profiles/unsupported_inspec/inspec.yml +2 -0
- data/test/unit/profile_test.rb +1 -1
- data/test/unit/resources/grub_conf_test.rb +29 -0
- data/test/unit/resources/service_test.rb +9 -0
- data/test/unit/utils/hash_map_test.rb +63 -0
- metadata +26 -5
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Christoph Hartmann
|
3
|
+
# author: Dominik Richter
|
4
|
+
|
5
|
+
require 'net/http'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module Compliance
|
9
|
+
# implements a simple http abstraction on top of Net::HTTP
|
10
|
+
class HTTP
|
11
|
+
# generic get requires
|
12
|
+
def self.get(url, token, insecure, basic_auth = false)
|
13
|
+
uri = URI.parse(url)
|
14
|
+
req = Net::HTTP::Get.new(uri.path)
|
15
|
+
|
16
|
+
return send_request(uri, req, insecure) if token.nil?
|
17
|
+
|
18
|
+
if basic_auth
|
19
|
+
req.basic_auth(token, '')
|
20
|
+
else
|
21
|
+
req['Authorization'] = "Bearer #{token}"
|
22
|
+
end
|
23
|
+
send_request(uri, req, insecure)
|
24
|
+
end
|
25
|
+
|
26
|
+
# generic post request
|
27
|
+
def self.post(url, token, insecure, basic_auth = false)
|
28
|
+
# form request
|
29
|
+
uri = URI.parse(url)
|
30
|
+
req = Net::HTTP::Post.new(uri.path)
|
31
|
+
if basic_auth
|
32
|
+
req.basic_auth token, ''
|
33
|
+
else
|
34
|
+
req['Authorization'] = "Bearer #{token}"
|
35
|
+
end
|
36
|
+
req.form_data={}
|
37
|
+
|
38
|
+
send_request(uri, req, insecure)
|
39
|
+
end
|
40
|
+
|
41
|
+
# post a file
|
42
|
+
def self.post_file(url, token, file_path, insecure, basic_auth = false)
|
43
|
+
uri = URI.parse(url)
|
44
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
45
|
+
|
46
|
+
# set connection flags
|
47
|
+
http.use_ssl = (uri.scheme == 'https')
|
48
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE if insecure
|
49
|
+
|
50
|
+
req = Net::HTTP::Post.new(uri.path)
|
51
|
+
if basic_auth
|
52
|
+
req.basic_auth token, ''
|
53
|
+
else
|
54
|
+
req['Authorization'] = "Bearer #{token}"
|
55
|
+
end
|
56
|
+
|
57
|
+
req.body_stream=File.open(file_path, 'rb')
|
58
|
+
req.add_field('Content-Length', File.size(file_path))
|
59
|
+
req.add_field('Content-Type', 'application/x-gtar')
|
60
|
+
|
61
|
+
boundary = 'INSPEC-PROFILE-UPLOAD'
|
62
|
+
req.add_field('session', boundary)
|
63
|
+
res=http.request(req)
|
64
|
+
res
|
65
|
+
end
|
66
|
+
|
67
|
+
# sends a http requests
|
68
|
+
def self.send_request(uri, req, insecure)
|
69
|
+
opts = {
|
70
|
+
use_ssl: uri.scheme == 'https',
|
71
|
+
}
|
72
|
+
opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if insecure
|
73
|
+
|
74
|
+
res = Net::HTTP.start(uri.host, uri.port, opts) {|http|
|
75
|
+
http.request(req)
|
76
|
+
}
|
77
|
+
res
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/inspec/metadata.rb
CHANGED
@@ -4,6 +4,8 @@
|
|
4
4
|
# author: Christoph Hartmann
|
5
5
|
|
6
6
|
require 'logger'
|
7
|
+
require 'rubygems/version'
|
8
|
+
require 'rubygems/requirement'
|
7
9
|
|
8
10
|
module Inspec
|
9
11
|
# Extract metadata.rb information
|
@@ -40,8 +42,10 @@ module Inspec
|
|
40
42
|
# already.
|
41
43
|
end
|
42
44
|
|
43
|
-
def is_supported(os, entry)
|
44
|
-
name
|
45
|
+
def is_supported?(os, entry)
|
46
|
+
name = entry[:'os-name'] || entry[:os]
|
47
|
+
family = entry[:'os-family']
|
48
|
+
release = entry[:release]
|
45
49
|
|
46
50
|
# return true if the backend matches the supported OS's
|
47
51
|
# fields act as masks, i.e. any value configured for os-name, os-family,
|
@@ -66,34 +70,24 @@ module Inspec
|
|
66
70
|
name_ok && family_ok && release_ok
|
67
71
|
end
|
68
72
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
family = try_support[:'os-family']
|
74
|
-
release = try_support[:release]
|
75
|
-
elsif entry.is_a?(String)
|
76
|
-
@logger.warn(
|
77
|
-
"Do not use deprecated `supports: #{entry}` syntax. Instead use "\
|
78
|
-
"`supports: {os-family: #{entry}}`.")
|
79
|
-
family = entry
|
80
|
-
end
|
73
|
+
def inspec_requirement
|
74
|
+
inspec = params[:supports].find { |x| !x[:inspec].nil? } || {}
|
75
|
+
Gem::Requirement.create(inspec[:inspec])
|
76
|
+
end
|
81
77
|
|
82
|
-
|
78
|
+
def supports_runtime?
|
79
|
+
running = Gem::Version.new(Inspec::VERSION)
|
80
|
+
inspec_requirement.satisfied_by?(running)
|
83
81
|
end
|
84
82
|
|
85
83
|
def supports_transport?(backend)
|
86
|
-
# make sure the supports field is always an array
|
87
|
-
supp = params[:supports]
|
88
|
-
supp = supp.is_a?(Hash) ? [supp] : Array(supp)
|
89
|
-
|
90
84
|
# with no supports specified, always return true, as there are no
|
91
85
|
# constraints on the supported backend; it is equivalent to putting
|
92
86
|
# all fields into accept-all mode
|
93
|
-
return true if
|
87
|
+
return true if params[:supports].empty?
|
94
88
|
|
95
|
-
found =
|
96
|
-
is_supported(backend.os, entry)
|
89
|
+
found = params[:supports].find do |entry|
|
90
|
+
is_supported?(backend.os, entry)
|
97
91
|
end
|
98
92
|
|
99
93
|
# finally, if we found a supported entry, we are good to go
|
@@ -132,32 +126,51 @@ module Inspec
|
|
132
126
|
@missing_methods
|
133
127
|
end
|
134
128
|
|
135
|
-
def self.symbolize_keys(
|
136
|
-
|
129
|
+
def self.symbolize_keys(obj)
|
130
|
+
return obj.map { |i| symbolize_keys(i) } if obj.is_a?(Array)
|
131
|
+
return obj unless obj.is_a?(Hash)
|
132
|
+
|
133
|
+
obj.each_with_object({}) {|(k, v), h|
|
137
134
|
v = symbolize_keys(v) if v.is_a?(Hash)
|
135
|
+
v = symbolize_keys(v) if v.is_a?(Array)
|
138
136
|
h[k.to_sym] = v
|
139
137
|
}
|
140
138
|
end
|
141
139
|
|
142
|
-
def self.finalize(metadata, profile_id)
|
140
|
+
def self.finalize(metadata, profile_id, logger = nil)
|
143
141
|
return nil if metadata.nil?
|
144
142
|
param = metadata.params || {}
|
145
143
|
param['name'] = profile_id.to_s unless profile_id.to_s.empty?
|
146
144
|
param['version'] = param['version'].to_s unless param['version'].nil?
|
147
145
|
metadata.params = symbolize_keys(param)
|
146
|
+
|
147
|
+
# consolidate supports field with legacy mode
|
148
|
+
metadata.params[:supports] =
|
149
|
+
case x = metadata.params[:supports]
|
150
|
+
when Hash then [x]
|
151
|
+
when Array then x
|
152
|
+
when nil then []
|
153
|
+
else
|
154
|
+
logger ||= Logger.new(nil)
|
155
|
+
logger.warn(
|
156
|
+
"Do not use deprecated `supports: #{x}` syntax. Instead use "\
|
157
|
+
"`supports: {os-family: #{x}}`.")
|
158
|
+
[{ :'os-family' => x }]
|
159
|
+
end
|
160
|
+
|
148
161
|
metadata
|
149
162
|
end
|
150
163
|
|
151
164
|
def self.from_yaml(ref, contents, profile_id, logger = nil)
|
152
165
|
res = Metadata.new(ref, logger)
|
153
166
|
res.params = YAML.load(contents)
|
154
|
-
finalize(res, profile_id)
|
167
|
+
finalize(res, profile_id, logger)
|
155
168
|
end
|
156
169
|
|
157
170
|
def self.from_ruby(ref, contents, profile_id, logger = nil)
|
158
171
|
res = Metadata.new(ref, logger)
|
159
172
|
res.instance_eval(contents, ref, 1)
|
160
|
-
finalize(res, profile_id)
|
173
|
+
finalize(res, profile_id, logger)
|
161
174
|
end
|
162
175
|
|
163
176
|
def self.from_ref(ref, contents, profile_id, logger = nil)
|
data/lib/inspec/objects/test.rb
CHANGED
@@ -48,7 +48,8 @@ module Inspec
|
|
48
48
|
|
49
49
|
if @qualifier.length > 1
|
50
50
|
last = @qualifier[-1]
|
51
|
-
|
51
|
+
# preventing its(:to_i) as the value returned is always 0
|
52
|
+
if last.length == 1 && last[0] != 'to_i'
|
52
53
|
xres = last[0]
|
53
54
|
else
|
54
55
|
res += '.' + ruby_qualifier(last)
|
data/lib/inspec/resource.rb
CHANGED
@@ -38,7 +38,7 @@ class InspecRspecFormatter < RSpec::Core::Formatters::JsonFormatter
|
|
38
38
|
|
39
39
|
def dump_summary(summary)
|
40
40
|
super(summary)
|
41
|
-
@output_hash[:profiles] = @profiles.map do |profile|
|
41
|
+
@output_hash[:profiles] = Array(@profiles).map do |profile|
|
42
42
|
r = profile.params.dup
|
43
43
|
r.delete(:rules)
|
44
44
|
r
|
data/lib/inspec/runner.rb
CHANGED
@@ -52,7 +52,26 @@ module Inspec
|
|
52
52
|
add_profile(profile, options)
|
53
53
|
end
|
54
54
|
|
55
|
+
def supports_profile?(profile)
|
56
|
+
return true if profile.metadata.nil?
|
57
|
+
|
58
|
+
if !profile.metadata.supports_runtime?
|
59
|
+
fail 'This profile requires InSpec version '\
|
60
|
+
"#{profile.metadata.inspec_requirement}. You are running "\
|
61
|
+
"InSpec v#{Inspec::VERSION}.\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
if !profile.metadata.supports_transport?(@backend)
|
65
|
+
os_info = @backend.os[:family].to_s
|
66
|
+
fail "This OS/platform (#{os_info}) is not supported by this profile."
|
67
|
+
end
|
68
|
+
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
55
72
|
def add_profile(profile, options = {})
|
73
|
+
return if !options[:ignore_supports] && !supports_profile?(profile)
|
74
|
+
|
56
75
|
@test_collector.add_profile(profile)
|
57
76
|
options[:metadata] = profile.metadata
|
58
77
|
|
@@ -86,19 +105,6 @@ module Inspec
|
|
86
105
|
tests = [tests] unless tests.is_a? Array
|
87
106
|
tests.each { |t| add_test_to_context(t, ctx) }
|
88
107
|
|
89
|
-
# skip based on support checks in metadata
|
90
|
-
meta = options[:metadata]
|
91
|
-
if !options[:ignore_supports] && !meta.nil? &&
|
92
|
-
!meta.supports_transport?(@backend)
|
93
|
-
os_info = @backend.os[:family].to_s
|
94
|
-
ctx.rules.values.each do |ctrl|
|
95
|
-
::Inspec::Rule.set_skip_rule(
|
96
|
-
ctrl,
|
97
|
-
"This OS/platform (#{os_info}) is not supported by this profile.",
|
98
|
-
)
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
108
|
# process the resulting rules
|
103
109
|
filter_controls(ctx.rules, options[:controls]).each do |rule_id, rule|
|
104
110
|
register_rule(rule_id, rule)
|
data/lib/inspec/runner_rspec.rb
CHANGED
data/lib/inspec/version.rb
CHANGED
data/lib/matchers/matchers.rb
CHANGED
@@ -226,7 +226,7 @@ end
|
|
226
226
|
# - compare strings case-insensitive
|
227
227
|
# - you expect a number (strings will be converted if possible)
|
228
228
|
#
|
229
|
-
RSpec::Matchers.define :cmp do |
|
229
|
+
RSpec::Matchers.define :cmp do |first_expected|
|
230
230
|
|
231
231
|
def integer?(value)
|
232
232
|
!(value =~ /\A\d+\Z/).nil?
|
@@ -243,33 +243,52 @@ RSpec::Matchers.define :cmp do |expected|
|
|
243
243
|
!(value =~ /\A0+\d+\Z/).nil?
|
244
244
|
end
|
245
245
|
|
246
|
-
|
247
|
-
actual = actual[0] if actual.is_a?(Array) && !expected.is_a?(Array) && actual.length == 1
|
246
|
+
def try_match(actual, op, expected) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
248
247
|
# if actual and expected are strings
|
249
248
|
if expected.is_a?(String) && actual.is_a?(String)
|
250
|
-
actual.casecmp(expected) == 0
|
249
|
+
return actual.casecmp(expected) == 0 if op == :==
|
251
250
|
elsif expected.is_a?(String) && integer?(expected) && actual.is_a?(Integer)
|
252
|
-
expected.to_i
|
251
|
+
return actual.method(op).call(expected.to_i)
|
253
252
|
elsif expected.is_a?(Integer) && integer?(actual)
|
254
|
-
|
253
|
+
return actual.to_i.method(op).call(expected)
|
255
254
|
elsif expected.is_a?(Float) && float?(actual)
|
256
|
-
|
255
|
+
return actual.to_f.method(op).call(expected)
|
257
256
|
elsif octal?(expected) && actual.is_a?(Integer)
|
258
|
-
expected.to_i(8)
|
259
|
-
|
260
|
-
|
261
|
-
|
257
|
+
return actual.method(op).call(expected.to_i(8))
|
258
|
+
end
|
259
|
+
|
260
|
+
# fallback to simple operation
|
261
|
+
actual.method(op).call(expected)
|
262
|
+
|
263
|
+
rescue NameError => _
|
264
|
+
false
|
265
|
+
rescue ArgumentError
|
266
|
+
false
|
267
|
+
end
|
268
|
+
|
269
|
+
match do |actual|
|
270
|
+
@operation ||= :==
|
271
|
+
@expected ||= first_expected
|
272
|
+
return actual === @expected if @operation == :=== # rubocop:disable Style/CaseEquality
|
273
|
+
actual = actual[0] if actual.is_a?(Array) && !@expected.is_a?(Array) && actual.length == 1
|
274
|
+
try_match(actual, @operation, @expected)
|
275
|
+
end
|
276
|
+
|
277
|
+
[:==, :<, :<=, :>=, :>, :===, :=~].each do |op|
|
278
|
+
chain(op) do |x|
|
279
|
+
@operation = op
|
280
|
+
@expected = x
|
262
281
|
end
|
263
282
|
end
|
264
283
|
|
265
284
|
failure_message do |actual|
|
266
285
|
actual = '0' + actual.to_s(8) if octal?(expected)
|
267
|
-
"\nexpected: #{expected}\n got: #{actual}\n\n(compared using `cmp` matcher)\n"
|
286
|
+
"\nexpected: value #{@operation} #{expected}\n got: #{actual}\n\n(compared using `cmp` matcher)\n"
|
268
287
|
end
|
269
288
|
|
270
289
|
failure_message_when_negated do |actual|
|
271
290
|
actual = '0' + actual.to_s(8) if octal?(expected)
|
272
|
-
"\nexpected: value
|
291
|
+
"\nexpected: value ! #{@operation} #{expected}\n got: #{actual}\n\n(compared using `cmp` matcher)\n"
|
273
292
|
end
|
274
293
|
end
|
275
294
|
|
@@ -0,0 +1,186 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Thomas Cate
|
3
|
+
# license: All rights reserved
|
4
|
+
|
5
|
+
require 'utils/simpleconfig'
|
6
|
+
|
7
|
+
class GrubConfig < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
|
8
|
+
name 'grub_conf'
|
9
|
+
desc 'Use the grub_conf InSpec audit resource to test the boot config of Linux systems that use Grub.'
|
10
|
+
example "
|
11
|
+
describe grub_conf('/etc/grub.conf', 'default') do
|
12
|
+
its('kernel') { should include '/vmlinuz-2.6.32-573.7.1.el6.x86_64' }
|
13
|
+
its('initrd') { should include '/initramfs-2.6.32-573.el6.x86_64.img=1' }
|
14
|
+
its('default') { should_not eq '1' }
|
15
|
+
its('timeout') { should eq '5' }
|
16
|
+
end
|
17
|
+
|
18
|
+
also check specific kernels
|
19
|
+
describe grub_conf('/etc/grub.conf', 'CentOS (2.6.32-573.12.1.el6.x86_64)') do
|
20
|
+
its('kernel') { should include 'audit=1' }
|
21
|
+
end
|
22
|
+
"
|
23
|
+
|
24
|
+
def initialize(path = nil, kernel = nil)
|
25
|
+
family = inspec.os[:family]
|
26
|
+
case family
|
27
|
+
when 'redhat', 'fedora', 'centos'
|
28
|
+
release = inspec.os[:release].to_f
|
29
|
+
supported = true
|
30
|
+
if release < 7
|
31
|
+
@conf_path = path || '/etc/grub.conf'
|
32
|
+
@version = 'legacy'
|
33
|
+
else
|
34
|
+
@conf_path = path || '/boot/grub/grub.cfg'
|
35
|
+
@defaults_path = '/etc/default/grub'
|
36
|
+
@version = 'grub2'
|
37
|
+
end
|
38
|
+
when 'ubuntu'
|
39
|
+
@conf_path = path || '/boot/grub/grub.cfg'
|
40
|
+
@defaults_path = '/etc/default/grub'
|
41
|
+
@version = 'grub2'
|
42
|
+
supported = true
|
43
|
+
end
|
44
|
+
@kernel = kernel || 'default'
|
45
|
+
return skip_resource 'The `grub_config` resource is not supported on your OS yet.' if supported.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(name)
|
49
|
+
read_params[name.to_s]
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_s
|
53
|
+
'Grub Config'
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
######################################################################
|
59
|
+
# Grub2 This is used by all supported versions of Ubuntu and Rhel 7+ #
|
60
|
+
######################################################################
|
61
|
+
|
62
|
+
def grub2_parse_kernel_lines(content, conf)
|
63
|
+
# Find all "menuentry" lines and then parse them into arrays
|
64
|
+
menu_entry = 0
|
65
|
+
lines = content.split("\n")
|
66
|
+
kernel_opts = {}
|
67
|
+
kernel_opts['insmod'] = []
|
68
|
+
lines.each_with_index do |file_line, index|
|
69
|
+
next unless file_line =~ /(^|\s)menuentry\s.*/
|
70
|
+
lines.drop(index+1).each do |kernel_line|
|
71
|
+
next if kernel_line =~ /(^|\s)(menu|}).*/
|
72
|
+
if menu_entry == conf['GRUB_DEFAULT'].to_i && @kernel == 'default'
|
73
|
+
if kernel_line =~ /(^|\s)initrd.*/
|
74
|
+
kernel_opts['initrd'] = kernel_line.split(' ')[1]
|
75
|
+
end
|
76
|
+
if kernel_line =~ /(^|\s)linux.*/
|
77
|
+
kernel_opts['kernel'] = kernel_line.split
|
78
|
+
end
|
79
|
+
if kernel_line =~ /(^|\s)set root=.*/
|
80
|
+
kernel_opts['root'] = kernel_line.split('=')[1].tr('\'', '')
|
81
|
+
end
|
82
|
+
if kernel_line =~ /(^|\s)insmod.*/
|
83
|
+
kernel_opts['insmod'].push(kernel_line.split(' ')[1])
|
84
|
+
end
|
85
|
+
else
|
86
|
+
menu_entry += 1
|
87
|
+
break
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
kernel_opts
|
92
|
+
end
|
93
|
+
|
94
|
+
###################################################################
|
95
|
+
# Grub1 aka legacy-grub config. Primarily used by Centos/Rhel 6.x #
|
96
|
+
###################################################################
|
97
|
+
|
98
|
+
def parse_kernel_lines(content, conf)
|
99
|
+
# Find all "title" lines and then parse them into arrays
|
100
|
+
menu_entry = 0
|
101
|
+
lines = content.split("\n")
|
102
|
+
kernel_opts = {}
|
103
|
+
lines.each_with_index do |file_line, index|
|
104
|
+
next unless file_line =~ /^title.*/
|
105
|
+
current_kernel = file_line.split(' ', 2)[1]
|
106
|
+
lines.drop(index+1).each do |kernel_line|
|
107
|
+
if kernel_line =~ /^\s.*/
|
108
|
+
option_type = kernel_line.split(' ')[0]
|
109
|
+
line_options = kernel_line.split(' ').drop(1)
|
110
|
+
if (menu_entry == conf['default'].to_i && @kernel == 'default') || current_kernel == @kernel
|
111
|
+
if option_type == 'kernel'
|
112
|
+
kernel_opts['kernel'] = line_options
|
113
|
+
else
|
114
|
+
kernel_opts[option_type] = line_options[0]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
else
|
118
|
+
menu_entry += 1
|
119
|
+
break
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
kernel_opts
|
124
|
+
end
|
125
|
+
|
126
|
+
def read_file(config_file)
|
127
|
+
file = inspec.file(config_file)
|
128
|
+
|
129
|
+
if !file.file? && !file.symlink?
|
130
|
+
skip_resource "Can't find file '#{@conf_path}'"
|
131
|
+
return @params = {}
|
132
|
+
end
|
133
|
+
|
134
|
+
content = file.content
|
135
|
+
|
136
|
+
if content.empty? && file.size > 0
|
137
|
+
skip_resource "Can't read file '#{@conf_path}'"
|
138
|
+
return @params = {}
|
139
|
+
end
|
140
|
+
|
141
|
+
content
|
142
|
+
end
|
143
|
+
|
144
|
+
def read_params
|
145
|
+
return @params if defined?(@params)
|
146
|
+
|
147
|
+
content = read_file(@conf_path)
|
148
|
+
|
149
|
+
if @version == 'legacy'
|
150
|
+
# parse the file
|
151
|
+
conf = SimpleConfig.new(
|
152
|
+
content,
|
153
|
+
multiple_values: true,
|
154
|
+
).params
|
155
|
+
# convert single entry arrays into strings
|
156
|
+
conf.each do |key, value|
|
157
|
+
if value.size == 1
|
158
|
+
conf[key] = conf[key][0].to_s
|
159
|
+
end
|
160
|
+
end
|
161
|
+
kernel_opts = parse_kernel_lines(content, conf)
|
162
|
+
@params = conf.merge(kernel_opts)
|
163
|
+
end
|
164
|
+
|
165
|
+
if @version == 'grub2'
|
166
|
+
# read defaults
|
167
|
+
defaults = read_file(@defaults_path)
|
168
|
+
|
169
|
+
conf = SimpleConfig.new(
|
170
|
+
defaults,
|
171
|
+
multiple_values: true,
|
172
|
+
).params
|
173
|
+
|
174
|
+
# convert single entry arrays into strings
|
175
|
+
conf.each do |key, value|
|
176
|
+
if value.size == 1
|
177
|
+
conf[key] = conf[key][0].to_s
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
kernel_opts = grub2_parse_kernel_lines(content, conf)
|
182
|
+
@params = conf.merge(kernel_opts)
|
183
|
+
end
|
184
|
+
@params
|
185
|
+
end
|
186
|
+
end
|
data/lib/resources/json.rb
CHANGED
@@ -10,7 +10,7 @@ module Inspec::Resources
|
|
10
10
|
desc 'Use the json InSpec audit resource to test data in a JSON file.'
|
11
11
|
example "
|
12
12
|
describe json('policyfile.lock.json') do
|
13
|
-
its('cookbook_locks
|
13
|
+
its(['cookbook_locks','omnibus','version']) { should eq('2.2.0') }
|
14
14
|
end
|
15
15
|
"
|
16
16
|
|
data/lib/resources/service.rb
CHANGED
@@ -94,7 +94,7 @@ module Inspec::Resources
|
|
94
94
|
return skip_resource 'The `service` resource is not supported on your OS yet.' if @service_mgmt.nil?
|
95
95
|
end
|
96
96
|
|
97
|
-
def select_service_mgmt # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
97
|
+
def select_service_mgmt # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/MethodLength
|
98
98
|
os = inspec.os
|
99
99
|
family = os[:family]
|
100
100
|
|
@@ -135,8 +135,14 @@ module Inspec::Resources
|
|
135
135
|
WindowsSrv.new(inspec)
|
136
136
|
elsif %w{freebsd}.include?(family)
|
137
137
|
BSDInit.new(inspec, service_ctl)
|
138
|
-
elsif %w{arch
|
138
|
+
elsif %w{arch}.include?(family)
|
139
139
|
Systemd.new(inspec, service_ctl)
|
140
|
+
elsif %w{suse opensuse}.include?(family)
|
141
|
+
if inspec.os[:release].to_i >= 12
|
142
|
+
Systemd.new(inspec, service_ctl)
|
143
|
+
else
|
144
|
+
SysV.new(inspec, service_ctl || '/sbin/service')
|
145
|
+
end
|
140
146
|
elsif %w{aix}.include?(family)
|
141
147
|
SrcMstr.new(inspec)
|
142
148
|
elsif %w{amazon}.include?(family)
|
@@ -214,7 +220,7 @@ module Inspec::Resources
|
|
214
220
|
running = params['SubState'] == 'running'
|
215
221
|
# test via systemctl --quiet is-enabled
|
216
222
|
# ActiveState values eg.g inactive, active
|
217
|
-
enabled = params['UnitFileState']
|
223
|
+
enabled = %w{enabled static}.include? params['UnitFileState']
|
218
224
|
|
219
225
|
{
|
220
226
|
name: params['Id'],
|
data/lib/utils/base_cli.rb
CHANGED
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# author: Dominik Richter
|
3
|
+
# author: Christoph Hartmann
|
4
|
+
|
5
|
+
class HashMap
|
6
|
+
class << self
|
7
|
+
def [](hash, *keys)
|
8
|
+
return hash if keys.empty? || hash.nil?
|
9
|
+
key = keys.shift
|
10
|
+
if hash.is_a?(Array)
|
11
|
+
map = hash.map { |i| [i, key] }
|
12
|
+
else
|
13
|
+
map = hash[key]
|
14
|
+
end
|
15
|
+
[map, *keys]
|
16
|
+
rescue NoMethodError => _
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class StringMap
|
23
|
+
class << self
|
24
|
+
def [](hash, *keys)
|
25
|
+
return hash if keys.empty? || hash.nil?
|
26
|
+
key = keys.shift
|
27
|
+
if hash.is_a?(Array)
|
28
|
+
map = hash.map { |i| [i, key] }
|
29
|
+
else
|
30
|
+
map = hash[key]
|
31
|
+
end
|
32
|
+
[map, *keys]
|
33
|
+
rescue NoMethodError => _
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|