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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -0
  3. data/etc/deprecations.json +1 -1
  4. data/lib/bundles/inspec-supermarket/README.md +21 -2
  5. data/lib/bundles/inspec-supermarket/cli.rb +20 -3
  6. data/lib/bundles/inspec-supermarket/target.rb +3 -2
  7. data/lib/inspec/base_cli.rb +12 -0
  8. data/lib/inspec/cli.rb +21 -4
  9. data/lib/inspec/control_eval_context.rb +40 -39
  10. data/lib/inspec/dsl.rb +18 -3
  11. data/lib/inspec/globals.rb +5 -0
  12. data/lib/inspec/plugin/v1/registry.rb +1 -1
  13. data/lib/inspec/profile.rb +115 -2
  14. data/lib/inspec/resources/auditd.rb +5 -4
  15. data/lib/inspec/resources/cassandra.rb +64 -0
  16. data/lib/inspec/resources/cassandradb_conf.rb +47 -0
  17. data/lib/inspec/resources/cassandradb_session.rb +68 -0
  18. data/lib/inspec/resources/chrony_conf.rb +55 -0
  19. data/lib/inspec/resources/csv.rb +26 -3
  20. data/lib/inspec/resources/groups.rb +22 -3
  21. data/lib/inspec/resources/http.rb +135 -54
  22. data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
  23. data/lib/inspec/resources/ibmdb2_session.rb +69 -0
  24. data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
  25. data/lib/inspec/resources/opa.rb +4 -1
  26. data/lib/inspec/resources/oracle.rb +66 -0
  27. data/lib/inspec/resources/oracledb_conf.rb +40 -0
  28. data/lib/inspec/resources/oracledb_listener_conf.rb +123 -0
  29. data/lib/inspec/resources/oracledb_session.rb +25 -6
  30. data/lib/inspec/resources/packages.rb +21 -0
  31. data/lib/inspec/resources/postgres_session.rb +15 -4
  32. data/lib/inspec/resources/service.rb +59 -10
  33. data/lib/inspec/resources/ssl.rb +7 -0
  34. data/lib/inspec/resources/sybase_conf.rb +37 -0
  35. data/lib/inspec/resources/sybase_session.rb +111 -0
  36. data/lib/inspec/resources/users.rb +16 -2
  37. data/lib/inspec/resources/windows_firewall.rb +1 -1
  38. data/lib/inspec/resources.rb +9 -0
  39. data/lib/inspec/run_data/profile.rb +0 -2
  40. data/lib/inspec/version.rb +1 -1
  41. metadata +14 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26a893a79810f4d3e7aeb90032bd625636c045c8edc11274dddef456d504d278
4
- data.tar.gz: 05c34cf8764889bc585a0175cfd7d7c63ef54a4c45c30d182f599de1b3ef8a56
3
+ metadata.gz: a5fc9c31a9983866ff40fb11326a3c2bac04342e1d473b845e81639bbca88a8c
4
+ data.tar.gz: 6fd55b54b5c1510572fa963d2d4833a8f3132ae5cbfb1871a4662f8b3da720d9
5
5
  SHA512:
6
- metadata.gz: 6d04b25d7423b4fec6be048b8f131bfc28e90266a631829270930b8aa96a520b220c19a0d8fe463689441f67cb8d567d6163df7f56fcc27786ef72f25508dbfc
7
- data.tar.gz: f798eee0dafda728d169d34ac8a73fef9803f3a70b1b1342d395d3954420644cef3fedcc202c1dc30204bdf1b2098741831af0200b1b3758c03511f38874e9ca
6
+ metadata.gz: d9f356b30301430dbeeeb599b696aacfc4ec1caaadafd61e6af40462db60fa400cd2bf4add6a682fe379c65ed4adb0f926a858e6b072066f06296821725f7f92
7
+ data.tar.gz: 25381065952aa3d460cedd9c56a5b389625b2ed11841f2085047e81444c3aa1d788338468c640f5c27403b4074a5649b825a3289e0b63cdc96d70e8450cc664c
data/Gemfile CHANGED
@@ -66,3 +66,7 @@ if Gem.ruby_version >= Gem::Version.new("2.7.0")
66
66
  gem "git"
67
67
  end
68
68
  end
69
+
70
+ if Gem.ruby_version < Gem::Version.new("2.7.0")
71
+ gem "activesupport", "6.1.4.4"
72
+ end
@@ -4,7 +4,7 @@
4
4
  "groups": {
5
5
  "attrs_value_replaces_default": {
6
6
  "action": "warn",
7
- "prefix": "The 'default' option for attributes is being replaced by 'value' - please use it instead."
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: `inspec supermarket exec nathenharvey/tmp-compliance-profile`
12
- - via supermarket scheme: `inspec exec supermarket://nathenharvey/tmp-compliance-profile`
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
- # display profiles in format user/profile
20
- supermarket_profiles = Supermarket::API.profiles
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 = Supermarket::API.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, Supermarket::API::SUPERMARKET_URL]
14
+ [target, supermarket_url]
14
15
  elsif target.respond_to?(:key?) && target.key?(:supermarket)
15
- supermarket_server = target[:supermarket_url] || Supermarket::API::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
@@ -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
- if result[:errors].empty? && result[:warnings].empty?
125
- ui.plain_line("No errors or warnings")
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
- ui.plain_line("Summary: #{errors}, #{warnings}")
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
- private
193
-
194
- def block_location(block, alternate_caller)
195
- if block.nil?
196
- alternate_caller[/^(.+:\d+):in .+$/, 1] || "unknown"
197
- else
198
- path, line = block.source_location
199
- "#{File.basename(path)}:#{line}"
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
- end
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
- # Check if the given control exist in the --tags option
234
- def tag_exist_in_control_tags?(tag_ids)
235
- tag_option_matches_with_list = false
236
- if !tag_ids.empty? && !tag_ids.nil? && profile_tag_config_exist?
237
- tag_option_matches_with_list = !(tag_ids & @conf["profile"].include_tags_list).empty?
238
- unless tag_option_matches_with_list
239
- @conf["profile"].include_tags_list.any? do |inclusion|
240
- # Try to see if the inclusion is a regex, and if it matches
241
- if inclusion.is_a?(Regexp)
242
- tag_ids.each do |id|
243
- tag_option_matches_with_list = (inclusion =~ id)
244
- break if tag_option_matches_with_list
245
- end
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
- tag_option_matches_with_list
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
- filter_included_controls(context, dep_entry.profile, &block) unless opts[:include_all]
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
- unless include_ctx.rules[id] || include_ctx.rules[fid]
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
@@ -18,4 +18,9 @@ module Inspec
18
18
  require "etc" unless defined?(Etc)
19
19
  Etc.getpwuid.dir
20
20
  end
21
+
22
+ def self.locally_windows?
23
+ require "rbconfig" unless defined?(RbConfig)
24
+ RbConfig::CONFIG["host_os"] =~ /mswin|mingw|cygwin/
25
+ end
21
26
  end
@@ -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 == m
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)
@@ -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
- # profile is valid if we could not find any error
490
- result[:summary][:valid] = result[:errors].empty?
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
- unless inspec.command("/sbin/auditctl").exist?
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 `/sbin/auditctl` does not exist"
34
+ "Command `#{@auditctl_cmd_str}` does not exist"
34
35
  end
35
36
 
36
- auditctl_cmd = "/sbin/auditctl -l"
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("/sbin/auditctl -s").stdout.chomp
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