inspec-core 4.41.20 → 4.52.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/etc/deprecations.json +1 -1
- data/lib/bundles/inspec-supermarket/README.md +21 -2
- data/lib/bundles/inspec-supermarket/cli.rb +20 -3
- data/lib/bundles/inspec-supermarket/target.rb +3 -2
- data/lib/inspec/base_cli.rb +12 -0
- data/lib/inspec/cli.rb +21 -4
- data/lib/inspec/control_eval_context.rb +40 -39
- data/lib/inspec/dsl.rb +18 -3
- data/lib/inspec/globals.rb +5 -0
- data/lib/inspec/plugin/v1/registry.rb +1 -1
- data/lib/inspec/profile.rb +115 -2
- data/lib/inspec/resources/auditd.rb +5 -4
- data/lib/inspec/resources/cassandra.rb +64 -0
- data/lib/inspec/resources/cassandradb_conf.rb +47 -0
- data/lib/inspec/resources/cassandradb_session.rb +68 -0
- data/lib/inspec/resources/chrony_conf.rb +55 -0
- data/lib/inspec/resources/csv.rb +26 -3
- data/lib/inspec/resources/groups.rb +22 -3
- data/lib/inspec/resources/http.rb +135 -54
- data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
- data/lib/inspec/resources/ibmdb2_session.rb +69 -0
- data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
- data/lib/inspec/resources/opa.rb +4 -1
- data/lib/inspec/resources/oracle.rb +66 -0
- data/lib/inspec/resources/oracledb_conf.rb +40 -0
- data/lib/inspec/resources/oracledb_listener_conf.rb +123 -0
- data/lib/inspec/resources/oracledb_session.rb +25 -6
- data/lib/inspec/resources/packages.rb +21 -0
- data/lib/inspec/resources/postgres_session.rb +15 -4
- data/lib/inspec/resources/service.rb +59 -10
- data/lib/inspec/resources/ssl.rb +7 -0
- data/lib/inspec/resources/sybase_conf.rb +37 -0
- data/lib/inspec/resources/sybase_session.rb +111 -0
- data/lib/inspec/resources/users.rb +16 -2
- data/lib/inspec/resources/windows_firewall.rb +1 -1
- data/lib/inspec/resources.rb +9 -0
- data/lib/inspec/run_data/profile.rb +0 -2
- data/lib/inspec/version.rb +1 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5fc9c31a9983866ff40fb11326a3c2bac04342e1d473b845e81639bbca88a8c
|
4
|
+
data.tar.gz: 6fd55b54b5c1510572fa963d2d4833a8f3132ae5cbfb1871a4662f8b3da720d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9f356b30301430dbeeeb599b696aacfc4ec1caaadafd61e6af40462db60fa400cd2bf4add6a682fe379c65ed4adb0f926a858e6b072066f06296821725f7f92
|
7
|
+
data.tar.gz: 25381065952aa3d460cedd9c56a5b389625b2ed11841f2085047e81444c3aa1d788338468c640f5c27403b4074a5649b825a3289e0b63cdc96d70e8450cc664c
|
data/Gemfile
CHANGED
data/etc/deprecations.json
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
"groups": {
|
5
5
|
"attrs_value_replaces_default": {
|
6
6
|
"action": "warn",
|
7
|
-
"prefix": "The 'default' option for
|
7
|
+
"prefix": "The 'default' option for inputs is being replaced by 'value' - please use it instead."
|
8
8
|
},
|
9
9
|
"attrs_dsl": {
|
10
10
|
"action": "ignore",
|
@@ -8,8 +8,27 @@ To use the CLI, this InSpec add-on adds the following commands:
|
|
8
8
|
|
9
9
|
Compliance profiles from Supermarket can be executed in two ways:
|
10
10
|
|
11
|
-
- via supermarket exec:
|
12
|
-
|
11
|
+
- via supermarket exec:
|
12
|
+
|
13
|
+
**Public Supermarket**
|
14
|
+
|
15
|
+
`inspec supermarket exec nathenharvey/tmp-compliance-profile`
|
16
|
+
|
17
|
+
**Private Supermarket**
|
18
|
+
|
19
|
+
`inspec supermarket exec nathenharvey/tmp-compliance-profile --supermarket_url="PRIVATE_SUPERMARKET_URL"`
|
20
|
+
|
21
|
+
|
22
|
+
- via supermarket scheme:
|
23
|
+
|
24
|
+
**Public Supermarket**
|
25
|
+
|
26
|
+
`inspec exec supermarket://nathenharvey/tmp-compliance-profile`
|
27
|
+
|
28
|
+
**Private Supermarket**
|
29
|
+
|
30
|
+
`inspec exec supermarket://nathenharvey/tmp-compliance-profile --supermarket_url="PRIVATE_SUPERMARKET_URL"`
|
31
|
+
|
13
32
|
|
14
33
|
## Usage
|
15
34
|
|
@@ -15,10 +15,18 @@ module Supermarket
|
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "profiles", "list all available profiles in Chef Supermarket"
|
18
|
+
supermarket_options
|
18
19
|
def profiles
|
19
|
-
|
20
|
-
|
20
|
+
o = config
|
21
|
+
diagnose(o)
|
22
|
+
configure_logger(o)
|
21
23
|
|
24
|
+
# display profiles in format user/profile
|
25
|
+
supermarket_profiles = if o["supermarket_url"]
|
26
|
+
Supermarket::API.profiles(o["supermarket_url"])
|
27
|
+
else
|
28
|
+
Supermarket::API.profiles
|
29
|
+
end
|
22
30
|
headline("Available profiles:")
|
23
31
|
supermarket_profiles.each do |p|
|
24
32
|
li("#{p["tool_name"]} #{mark_text(p["tool_owner"] + "/" + p["slug"])}")
|
@@ -45,9 +53,18 @@ module Supermarket
|
|
45
53
|
end
|
46
54
|
|
47
55
|
desc "info PROFILE", "display Supermarket profile details"
|
56
|
+
supermarket_options
|
48
57
|
def info(profile)
|
58
|
+
o = config
|
59
|
+
diagnose(o)
|
60
|
+
configure_logger(o)
|
61
|
+
|
49
62
|
# check that the profile is available
|
50
|
-
supermarket_profiles =
|
63
|
+
supermarket_profiles = if o["supermarket_url"]
|
64
|
+
Supermarket::API.profiles(o["supermarket_url"])
|
65
|
+
else
|
66
|
+
Supermarket::API.profiles
|
67
|
+
end
|
51
68
|
found = supermarket_profiles.select do |p|
|
52
69
|
profile == "#{p["tool_owner"]}/#{p["slug"]}"
|
53
70
|
end
|
@@ -9,10 +9,11 @@ module Supermarket
|
|
9
9
|
priority 500
|
10
10
|
|
11
11
|
def self.resolve(target, opts = {})
|
12
|
+
supermarket_url = opts["supermarket_url"] || Supermarket::API::SUPERMARKET_URL
|
12
13
|
supermarket_uri, supermarket_server = if target.is_a?(String) && URI(target).scheme == "supermarket"
|
13
|
-
[target,
|
14
|
+
[target, supermarket_url]
|
14
15
|
elsif target.respond_to?(:key?) && target.key?(:supermarket)
|
15
|
-
supermarket_server = target[:supermarket_url] ||
|
16
|
+
supermarket_server = target[:supermarket_url] || supermarket_url
|
16
17
|
["supermarket://#{target[:supermarket]}", supermarket_server]
|
17
18
|
end
|
18
19
|
return nil unless supermarket_uri
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -126,6 +126,8 @@ module Inspec
|
|
126
126
|
desc: "Specify a shell type for winrm (eg. 'elevated' or 'powershell')"
|
127
127
|
option :docker_url, type: :string,
|
128
128
|
desc: "Provides path to Docker API endpoint (Docker)"
|
129
|
+
option :ssh_config_file, type: :array,
|
130
|
+
desc: "A list of paths to the ssh config file, e.g ~/.ssh/config or /etc/ssh/ssh_config"
|
129
131
|
end
|
130
132
|
|
131
133
|
def self.profile_options
|
@@ -135,9 +137,15 @@ module Inspec
|
|
135
137
|
desc: "Use the given path for caching dependencies. (default: ~/.inspec/cache)"
|
136
138
|
end
|
137
139
|
|
140
|
+
def self.supermarket_options
|
141
|
+
option :supermarket_url, type: :string,
|
142
|
+
desc: "Specify the URL of a private Chef Supermarket."
|
143
|
+
end
|
144
|
+
|
138
145
|
def self.exec_options
|
139
146
|
target_options
|
140
147
|
profile_options
|
148
|
+
supermarket_options
|
141
149
|
option :controls, type: :array,
|
142
150
|
desc: "A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests."
|
143
151
|
option :tags, type: :array,
|
@@ -174,6 +182,10 @@ module Inspec
|
|
174
182
|
desc: "After normal execution order, results are sorted by control ID, or by file (default), or randomly. None uses legacy unsorted mode."
|
175
183
|
option :filter_empty_profiles, type: :boolean, default: false,
|
176
184
|
desc: "Filter empty profiles (profiles without controls) from the report."
|
185
|
+
option :filter_waived_controls, type: :boolean,
|
186
|
+
desc: "Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores `run` setting of waiver file."
|
187
|
+
option :retain_waiver_data, type: :boolean,
|
188
|
+
desc: "EXPERIMENTAL: Only works in conjunction with --filter-waived-controls, retains waiver data about controls that were skipped"
|
177
189
|
option :command_timeout, type: :numeric,
|
178
190
|
desc: "Maximum seconds to allow commands to run during execution.",
|
179
191
|
long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
|
data/lib/inspec/cli.rb
CHANGED
@@ -93,7 +93,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
93
93
|
end
|
94
94
|
|
95
95
|
desc "check PATH", "verify all tests at the specified PATH"
|
96
|
-
option :format, type: :string
|
96
|
+
option :format, type: :string,
|
97
|
+
desc: "The output format to use doc (default), json. If valid format is not provided then it will use the default."
|
97
98
|
profile_options
|
98
99
|
def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
99
100
|
o = config
|
@@ -121,8 +122,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
121
122
|
end
|
122
123
|
puts
|
123
124
|
|
124
|
-
|
125
|
-
|
125
|
+
enable_offenses = !Inspec.locally_windows? # See 5723
|
126
|
+
if result[:errors].empty? && result[:warnings].empty? && result[:offenses].empty?
|
127
|
+
if enable_offenses
|
128
|
+
ui.plain_line("No errors, warnings, or offenses")
|
129
|
+
else
|
130
|
+
ui.plain_line("No errors or warnings")
|
131
|
+
end
|
126
132
|
else
|
127
133
|
item_msg = lambda { |item|
|
128
134
|
pos = [item[:file], item[:line], item[:column]].compact.join(":")
|
@@ -134,11 +140,22 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
134
140
|
|
135
141
|
puts
|
136
142
|
|
143
|
+
if enable_offenses && !result[:offenses].empty?
|
144
|
+
puts "Offenses:\n"
|
145
|
+
result[:offenses].each { |item| ui.cyan(" #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n\n") }
|
146
|
+
end
|
147
|
+
|
148
|
+
offenses = ui.cyan("#{result[:offenses].length} offenses", print: false)
|
137
149
|
errors = ui.red("#{result[:errors].length} errors", print: false)
|
138
150
|
warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
|
139
|
-
|
151
|
+
if enable_offenses
|
152
|
+
ui.plain_line("Summary: #{errors}, #{warnings}, #{offenses}")
|
153
|
+
else
|
154
|
+
ui.plain_line("Summary: #{errors}, #{warnings}")
|
155
|
+
end
|
140
156
|
end
|
141
157
|
end
|
158
|
+
|
142
159
|
ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
|
143
160
|
rescue StandardError => e
|
144
161
|
pretty_handle_exception(e)
|
@@ -18,6 +18,7 @@ module Inspec
|
|
18
18
|
attr_accessor :skip_file
|
19
19
|
attr_accessor :profile_context
|
20
20
|
attr_accessor :resources_dsl
|
21
|
+
attr_accessor :conf
|
21
22
|
|
22
23
|
def initialize(profile_context, resources_dsl, backend, conf, dependencies, require_loader, skip_only_if_eval)
|
23
24
|
@profile_context = profile_context
|
@@ -189,29 +190,24 @@ module Inspec
|
|
189
190
|
@skip_file = true
|
190
191
|
end
|
191
192
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
if
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
193
|
+
# Check if the given control exist in the --tags option
|
194
|
+
def tag_exist_in_control_tags?(tag_ids)
|
195
|
+
tag_option_matches_with_list = false
|
196
|
+
if !tag_ids.empty? && !tag_ids.nil? && profile_tag_config_exist?
|
197
|
+
tag_option_matches_with_list = !(tag_ids & @conf["profile"].include_tags_list).empty?
|
198
|
+
unless tag_option_matches_with_list
|
199
|
+
@conf["profile"].include_tags_list.any? do |inclusion|
|
200
|
+
# Try to see if the inclusion is a regex, and if it matches
|
201
|
+
if inclusion.is_a?(Regexp)
|
202
|
+
tag_ids.each do |id|
|
203
|
+
tag_option_matches_with_list = (inclusion =~ id)
|
204
|
+
break if tag_option_matches_with_list
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
200
209
|
end
|
201
|
-
|
202
|
-
|
203
|
-
# Returns true if configuration hash is not empty and it contains the list of controls is not empty
|
204
|
-
def profile_config_exist?
|
205
|
-
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_controls_list.empty?
|
206
|
-
end
|
207
|
-
|
208
|
-
def profile_tag_config_exist?
|
209
|
-
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_tags_list.empty?
|
210
|
-
end
|
211
|
-
|
212
|
-
# Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included
|
213
|
-
def controls_list_empty?
|
214
|
-
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
|
210
|
+
tag_option_matches_with_list
|
215
211
|
end
|
216
212
|
|
217
213
|
def tags_list_empty?
|
@@ -230,24 +226,29 @@ module Inspec
|
|
230
226
|
id_exist_in_list
|
231
227
|
end
|
232
228
|
|
233
|
-
#
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
229
|
+
# Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included
|
230
|
+
def controls_list_empty?
|
231
|
+
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def block_location(block, alternate_caller)
|
237
|
+
if block.nil?
|
238
|
+
alternate_caller[/^(.+:\d+):in .+$/, 1] || "unknown"
|
239
|
+
else
|
240
|
+
path, line = block.source_location
|
241
|
+
"#{File.basename(path)}:#{line}"
|
249
242
|
end
|
250
|
-
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns true if configuration hash is not empty and it contains the list of controls is not empty
|
246
|
+
def profile_config_exist?
|
247
|
+
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_controls_list.empty?
|
248
|
+
end
|
249
|
+
|
250
|
+
def profile_tag_config_exist?
|
251
|
+
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_tags_list.empty?
|
251
252
|
end
|
252
253
|
end
|
253
254
|
end
|
data/lib/inspec/dsl.rb
CHANGED
@@ -93,23 +93,38 @@ module Inspec::DSL
|
|
93
93
|
context = dep_entry.profile.runner_context
|
94
94
|
# if we don't want all the rules, then just make 1 pass to get all rule_IDs
|
95
95
|
# that we want to keep from the original
|
96
|
-
|
96
|
+
if !opts[:include_all] || !(opts[:conf]["profile"].include_tags_list.empty?) || !opts[:conf]["profile"].include_controls_list.empty?
|
97
|
+
filter_included_controls(context, dep_entry.profile, opts, &block)
|
98
|
+
end
|
97
99
|
# interpret the block and skip/modify as required
|
98
100
|
context.load(block) if block_given?
|
99
101
|
bind_context.add_subcontext(context)
|
100
102
|
end
|
101
103
|
|
102
|
-
def self.filter_included_controls(context, profile, &block)
|
104
|
+
def self.filter_included_controls(context, profile, opts, &block)
|
103
105
|
mock = Inspec::Backend.create(Inspec::Config.mock)
|
104
106
|
include_ctx = Inspec::ProfileContext.for_profile(profile, mock)
|
105
107
|
include_ctx.load(block) if block_given?
|
108
|
+
include_ctx.control_eval_context.conf = opts[:conf]
|
109
|
+
control_eval_ctx = include_ctx.control_eval_context
|
106
110
|
# remove all rules that were not registered
|
107
111
|
context.all_rules.each do |r|
|
108
112
|
id = Inspec::Rule.rule_id(r)
|
109
113
|
fid = Inspec::Rule.profile_id(r) + "/" + id
|
110
|
-
|
114
|
+
if !opts[:include_all] && !(include_ctx.rules[id] || include_ctx.rules[fid])
|
111
115
|
context.remove_rule(fid)
|
112
116
|
end
|
117
|
+
|
118
|
+
unless control_eval_ctx.controls_list_empty?
|
119
|
+
# filter the dependent profile controls which are not in the --controls options list
|
120
|
+
context.remove_rule(fid) unless control_eval_ctx.control_exist_in_controls_list?(id)
|
121
|
+
end
|
122
|
+
|
123
|
+
unless control_eval_ctx.tags_list_empty?
|
124
|
+
# filter included controls using --tags
|
125
|
+
tag_ids = control_eval_ctx.control_tags(r)
|
126
|
+
context.remove_rule(fid) unless control_eval_ctx.tag_exist_in_control_tags?(tag_ids)
|
127
|
+
end
|
113
128
|
end
|
114
129
|
end
|
115
130
|
end
|
data/lib/inspec/globals.rb
CHANGED
@@ -11,7 +11,7 @@ class PluginRegistry
|
|
11
11
|
# @return [Plugin] plugin instance if it can be resolved, nil otherwise
|
12
12
|
def resolve(target, opts = {})
|
13
13
|
modules.each do |m|
|
14
|
-
res = if Inspec::Fetcher::Url
|
14
|
+
res = if ["Inspec::Fetcher::Url", "Supermarket::Fetcher"].include? m.to_s
|
15
15
|
m.resolve(target, opts)
|
16
16
|
else
|
17
17
|
m.resolve(target)
|
data/lib/inspec/profile.rb
CHANGED
@@ -217,6 +217,9 @@ module Inspec
|
|
217
217
|
|
218
218
|
locked_dependencies.each(&:collect_tests)
|
219
219
|
|
220
|
+
tests = filter_waived_controls
|
221
|
+
|
222
|
+
# Collect tests
|
220
223
|
tests.each do |path, content|
|
221
224
|
next if content.nil? || content.empty?
|
222
225
|
|
@@ -233,6 +236,65 @@ module Inspec
|
|
233
236
|
@runner_context.all_rules
|
234
237
|
end
|
235
238
|
|
239
|
+
# Wipe out waived controls
|
240
|
+
def filter_waived_controls
|
241
|
+
cfg = Inspec::Config.cached
|
242
|
+
return tests unless cfg["filter_waived_controls"]
|
243
|
+
|
244
|
+
## Find the waivers file
|
245
|
+
# - TODO: cli_opts and instance_variable_get could be exposed
|
246
|
+
waiver_paths = cfg.instance_variable_get(:@cli_opts)["waiver_file"]
|
247
|
+
if waiver_paths.blank?
|
248
|
+
Inspec::Log.error "Must use --waiver-file with --filter-waived-controls"
|
249
|
+
Inspec::UI.new.exit(:usage_error)
|
250
|
+
end
|
251
|
+
|
252
|
+
# # Pull together waiver
|
253
|
+
waived_control_ids = []
|
254
|
+
waiver_paths.each do |waiver_path|
|
255
|
+
waiver_content = YAML.load_file(waiver_path)
|
256
|
+
unless waiver_content
|
257
|
+
# Note that we will have already issued a detailed warning
|
258
|
+
Inspec::Log.error "YAML parsing error in #{waiver_path}"
|
259
|
+
Inspec::UI.new.exit(:usage_error)
|
260
|
+
end
|
261
|
+
waived_control_ids << waiver_content.keys
|
262
|
+
end
|
263
|
+
waived_control_id_regex = "(#{waived_control_ids.join("|")})"
|
264
|
+
|
265
|
+
## Purge tests (this could be doone in next block for performance)
|
266
|
+
## TODO: implement earlier with pure AST and pure autocorrect AST
|
267
|
+
filtered_tests = {}
|
268
|
+
if cfg["retain_waiver_data"]
|
269
|
+
# VERY EXPERIMENTAL, but an empty describe block at the top level
|
270
|
+
# of the control blocks evaluation of ruby code until later-term
|
271
|
+
# waivers (behind the scenes this tells RSpec to hold on and use its internals to lazy load the code). This allows current waiver-data (e.g. skips) to still
|
272
|
+
# be processed and rendered
|
273
|
+
tests.each do |control_filename, source_code|
|
274
|
+
cleared_tests = source_code.scan(/control\s+['"].+?['"].+?(?=(?:control\s+['"].+?['"])|\z)/m).collect do |element|
|
275
|
+
next if element.blank?
|
276
|
+
|
277
|
+
if element&.match?(waived_control_id_regex)
|
278
|
+
splitlines = element.split("\n")
|
279
|
+
splitlines[0] + "\ndescribe '---' do\n" + splitlines[1..-1].join("\n") + "\nend\n"
|
280
|
+
else
|
281
|
+
element
|
282
|
+
end
|
283
|
+
end.join("")
|
284
|
+
filtered_tests[control_filename] = cleared_tests
|
285
|
+
end
|
286
|
+
else
|
287
|
+
tests.each do |control_filename, source_code|
|
288
|
+
cleared_tests = source_code.scan(/control\s+['"].+?['"].+?(?=(?:control\s+['"].+?['"])|\z)/m).select do |control_code|
|
289
|
+
!control_code.match?(waived_control_id_regex)
|
290
|
+
end.join("")
|
291
|
+
|
292
|
+
filtered_tests[control_filename] = cleared_tests
|
293
|
+
end
|
294
|
+
end
|
295
|
+
filtered_tests
|
296
|
+
end
|
297
|
+
|
236
298
|
# This creates the list of controls provided in the --controls options which need to be include
|
237
299
|
# for evaluation.
|
238
300
|
def include_controls_list
|
@@ -386,6 +448,45 @@ module Inspec
|
|
386
448
|
res
|
387
449
|
end
|
388
450
|
|
451
|
+
def cookstyle_linting_check
|
452
|
+
msgs = []
|
453
|
+
return msgs if Inspec.locally_windows? # See #5723
|
454
|
+
|
455
|
+
output = cookstyle_rake_output.split("Offenses:").last
|
456
|
+
msgs = output.split("\n").select { |x| x =~ /[A-Z]:/ } unless output.nil?
|
457
|
+
msgs
|
458
|
+
end
|
459
|
+
|
460
|
+
# Cookstyle linting rake run output
|
461
|
+
def cookstyle_rake_output
|
462
|
+
require "cookstyle"
|
463
|
+
require "rubocop/rake_task"
|
464
|
+
begin
|
465
|
+
RuboCop::RakeTask.new(:cookstyle_lint) do |spec|
|
466
|
+
spec.options += [
|
467
|
+
"--display-cop-names",
|
468
|
+
"--parallel",
|
469
|
+
"--only=InSpec/Deprecations",
|
470
|
+
]
|
471
|
+
spec.patterns += Dir.glob("#{@target}/**/*.rb").reject { |f| File.directory?(f) }
|
472
|
+
spec.fail_on_error = false
|
473
|
+
end
|
474
|
+
rescue LoadError
|
475
|
+
puts "Rubocop is not available. Install the rubocop gem to run the lint tests."
|
476
|
+
end
|
477
|
+
begin
|
478
|
+
stdout = StringIO.new
|
479
|
+
$stdout = stdout
|
480
|
+
Rake::Task["cookstyle_lint"].invoke
|
481
|
+
$stdout = STDOUT
|
482
|
+
Rake.application["cookstyle_lint"].reenable
|
483
|
+
stdout.string
|
484
|
+
rescue => e
|
485
|
+
puts "Cookstyle lint checks could not be performed. Error while running cookstyle - #{e}"
|
486
|
+
""
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
389
490
|
# Check if the profile is internally well-structured. The logger will be
|
390
491
|
# used to print information on errors and warnings which are found.
|
391
492
|
#
|
@@ -402,6 +503,7 @@ module Inspec
|
|
402
503
|
},
|
403
504
|
errors: [],
|
404
505
|
warnings: [],
|
506
|
+
offenses: [],
|
405
507
|
}
|
406
508
|
|
407
509
|
entry = lambda { |file, line, column, control, msg|
|
@@ -424,6 +526,10 @@ module Inspec
|
|
424
526
|
result[:errors].push(entry.call(file, line, column, control, msg))
|
425
527
|
}
|
426
528
|
|
529
|
+
offense = lambda { |file, line, column, control, msg|
|
530
|
+
result[:offenses].push(entry.call(file, line, column, control, msg))
|
531
|
+
}
|
532
|
+
|
427
533
|
@logger.info "Checking profile in #{@target}"
|
428
534
|
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
429
535
|
|
@@ -486,8 +592,15 @@ module Inspec
|
|
486
592
|
warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? || control[:checks].empty?
|
487
593
|
end
|
488
594
|
|
489
|
-
#
|
490
|
-
|
595
|
+
# Running cookstyle to check for code offenses
|
596
|
+
cookstyle_linting_check.each do |lint_output|
|
597
|
+
data = lint_output.split(":")
|
598
|
+
msg = "#{data[-2]}:#{data[-1]}"
|
599
|
+
offense.call(data[0], data[1], data[2], nil, msg)
|
600
|
+
end
|
601
|
+
|
602
|
+
# profile is valid if we could not find any error & offenses
|
603
|
+
result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
|
491
604
|
|
492
605
|
@logger.info "Control definitions OK." if result[:warnings].empty?
|
493
606
|
result
|
@@ -28,12 +28,13 @@ module Inspec::Resources
|
|
28
28
|
EXAMPLE
|
29
29
|
|
30
30
|
def initialize
|
31
|
-
|
31
|
+
@auditctl_cmd_str = inspec.os.name.eql?("alpine") ? "/usr/sbin/auditctl" : "/sbin/auditctl"
|
32
|
+
unless inspec.command(@auditctl_cmd_str).exist?
|
32
33
|
raise Inspec::Exceptions::ResourceFailed,
|
33
|
-
"Command
|
34
|
+
"Command `#{@auditctl_cmd_str}` does not exist"
|
34
35
|
end
|
35
36
|
|
36
|
-
auditctl_cmd = "
|
37
|
+
auditctl_cmd = "#{@auditctl_cmd_str} -l"
|
37
38
|
result = inspec.command(auditctl_cmd)
|
38
39
|
|
39
40
|
if result.exit_status != 0
|
@@ -68,7 +69,7 @@ module Inspec::Resources
|
|
68
69
|
filter.install_filter_methods_on_resource(self, :params)
|
69
70
|
|
70
71
|
def status(name = nil)
|
71
|
-
@status_content ||= inspec.command("
|
72
|
+
@status_content ||= inspec.command("#{@auditctl_cmd_str} -s").stdout.chomp
|
72
73
|
|
73
74
|
# See: https://github.com/inspec/inspec/issues/3113
|
74
75
|
if @status_content =~ /^AUDIT_STATUS/
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Cassandra < Inspec.resource(1)
|
3
|
+
name "cassandra"
|
4
|
+
supports platform: "unix"
|
5
|
+
supports platform: "windows"
|
6
|
+
|
7
|
+
desc "The 'cassandra' resource is a helper for the 'cql_conf'"
|
8
|
+
|
9
|
+
attr_reader :conf_path
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
case inspec.os[:family]
|
13
|
+
when "debian", "redhat", "linux", "suse"
|
14
|
+
determine_conf_dir_and_path_in_linux
|
15
|
+
when "windows"
|
16
|
+
determine_conf_dir_and_path_in_windows
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"CassandraDB"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def determine_conf_dir_and_path_in_linux
|
27
|
+
cassandra_home = inspec.os_env("CASSANDRA_HOME").content
|
28
|
+
|
29
|
+
if cassandra_home.nil? || cassandra_home.empty?
|
30
|
+
warn "$CASSANDRA_HOME environment variable not set in the system"
|
31
|
+
nil
|
32
|
+
else
|
33
|
+
conf_path = "#{cassandra_home}/cassandra.yaml"
|
34
|
+
if !inspec.file(conf_path).exist?
|
35
|
+
warn "Cassandra conf file not found in #{cassandra_home} directory."
|
36
|
+
nil
|
37
|
+
else
|
38
|
+
@conf_path = conf_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
fail_resource "Errors reading cassandra conf file: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def determine_conf_dir_and_path_in_windows
|
46
|
+
cassandra_home = inspec.os_env("CASSANDRA_HOME").content
|
47
|
+
|
48
|
+
if cassandra_home.nil? || cassandra_home.empty?
|
49
|
+
warn "CASSANDRA_HOME environment variable not set in the system"
|
50
|
+
nil
|
51
|
+
else
|
52
|
+
conf_path = "#{cassandra_home}\\conf\\cassandra.yaml"
|
53
|
+
if !inspec.file(conf_path).exist?
|
54
|
+
warn "Cassandra conf file not found in #{cassandra_home}\\conf directory."
|
55
|
+
nil
|
56
|
+
else
|
57
|
+
@conf_path = conf_path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
fail_resource "Errors reading cassandra conf file: #{e}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "inspec/resources/json"
|
2
|
+
require "inspec/resources/cassandra"
|
3
|
+
|
4
|
+
module Inspec::Resources
|
5
|
+
class CassandradbConf < JsonConfig
|
6
|
+
name "cassandradb_conf"
|
7
|
+
supports platform: "unix"
|
8
|
+
supports platform: "windows"
|
9
|
+
desc "Use the cql_conf InSpec audit resource to test the contents of the configuration file for Cassandra DB"
|
10
|
+
example <<~EXAMPLE
|
11
|
+
describe cassandradb_conf do
|
12
|
+
its('listen_address') { should eq '0.0.0.0' }
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
def initialize(conf_path = nil)
|
17
|
+
cassandra = nil
|
18
|
+
if conf_path.nil?
|
19
|
+
cassandra = inspec.cassandra
|
20
|
+
@conf_path = cassandra.conf_path
|
21
|
+
else
|
22
|
+
@conf_path = conf_path
|
23
|
+
end
|
24
|
+
|
25
|
+
if cassandra && cassandra.resource_failed?
|
26
|
+
raise cassandra.resource_exception_message
|
27
|
+
elsif @conf_path.nil?
|
28
|
+
return skip_resource "Cassandra db conf path is not set"
|
29
|
+
end
|
30
|
+
|
31
|
+
super(@conf_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse(content)
|
37
|
+
YAML.load(content)
|
38
|
+
rescue => e
|
39
|
+
raise Inspec::Exceptions::ResourceFailed, "Unable to parse `cassandra.yaml` file: #{e.message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_base_name
|
43
|
+
"Cassandra Configuration"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|