inspec-core 4.41.20 → 4.52.9

Sign up to get free protection for your applications and to get access to all the features.
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