inspec-core 4.56.58 → 5.7.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +24 -9
  3. data/etc/deprecations.json +12 -11
  4. data/inspec-core.gemspec +3 -5
  5. data/lib/inspec/base_cli.rb +14 -2
  6. data/lib/inspec/cli.rb +16 -7
  7. data/lib/inspec/dependencies/dependency_set.rb +2 -6
  8. data/lib/inspec/dependency_installer.rb +74 -0
  9. data/lib/inspec/dependency_loader.rb +97 -0
  10. data/lib/inspec/dsl.rb +16 -23
  11. data/lib/inspec/env_printer.rb +1 -1
  12. data/lib/inspec/errors.rb +7 -0
  13. data/lib/inspec/fetcher/git.rb +28 -43
  14. data/lib/inspec/fetcher/url.rb +1 -1
  15. data/lib/inspec/formatters/base.rb +22 -0
  16. data/lib/inspec/metadata.rb +36 -0
  17. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
  18. data/lib/inspec/profile.rb +81 -29
  19. data/lib/inspec/reporters/automate.rb +1 -1
  20. data/lib/inspec/reporters/cli.rb +1 -1
  21. data/lib/inspec/reporters/json.rb +31 -11
  22. data/lib/inspec/resource.rb +6 -0
  23. data/lib/inspec/resources/cassandradb_session.rb +3 -4
  24. data/lib/inspec/resources/cron.rb +49 -0
  25. data/lib/inspec/resources/file.rb +1 -1
  26. data/lib/inspec/resources/ibmdb2_session.rb +3 -4
  27. data/lib/inspec/resources/ipfilter.rb +59 -0
  28. data/lib/inspec/resources/ipnat.rb +58 -0
  29. data/lib/inspec/resources/mongodb_session.rb +1 -7
  30. data/lib/inspec/resources/oracledb_session.rb +7 -20
  31. data/lib/inspec/resources/postgres_session.rb +5 -7
  32. data/lib/inspec/resources/processes.rb +4 -6
  33. data/lib/inspec/resources/service.rb +2 -4
  34. data/lib/inspec/resources.rb +3 -16
  35. data/lib/inspec/rule.rb +1 -1
  36. data/lib/inspec/runner.rb +18 -1
  37. data/lib/inspec/runner_rspec.rb +15 -0
  38. data/lib/inspec/schema/exec_json.rb +59 -58
  39. data/lib/inspec/schema/exec_json_min.rb +16 -16
  40. data/lib/inspec/schema/primitives.rb +68 -51
  41. data/lib/inspec/schema/profile_json.rb +27 -27
  42. data/lib/inspec/schema.rb +1 -0
  43. data/lib/inspec/secrets/yaml.rb +1 -7
  44. data/lib/inspec/ui.rb +1 -0
  45. data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
  46. data/lib/inspec/version.rb +1 -1
  47. data/lib/inspec.rb +3 -0
  48. data/lib/matchers/matchers.rb +1 -7
  49. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  50. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
  51. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
  52. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
  53. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
  54. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
  55. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
  56. data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
  57. data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
  58. data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
  59. data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
  60. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +6 -4
  61. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +4 -1
  62. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +2 -1
  63. data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
  64. data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
  65. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
  66. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
  67. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
  68. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
  69. metadata +24 -7
@@ -41,7 +41,6 @@ module Inspec::Fetcher
41
41
  @ref = opts[:ref]
42
42
  @remote_url = expand_local_path(remote_url)
43
43
  @repo_directory = nil
44
- @resolved_ref = nil
45
44
  @relative_path = opts[:relative_path] if opts[:relative_path] && !opts[:relative_path].empty?
46
45
  end
47
46
 
@@ -71,7 +70,7 @@ module Inspec::Fetcher
71
70
  if @relative_path
72
71
  perform_relative_path_fetch(destination_path, working_dir)
73
72
  else
74
- Inspec::Log.debug("Checkout of #{resolved_ref.nil? ? @remote_url : resolved_ref} successful. " \
73
+ Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
75
74
  "Moving checkout to #{destination_path}")
76
75
  FileUtils.cp_r(working_dir + "/.", destination_path)
77
76
  end
@@ -81,14 +80,14 @@ module Inspec::Fetcher
81
80
  end
82
81
 
83
82
  def perform_relative_path_fetch(destination_path, working_dir)
84
- Inspec::Log.debug("Checkout of #{resolved_ref.nil? ? @remote_url : resolved_ref} successful. " \
83
+ Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
85
84
  "Moving #{@relative_path} to #{destination_path}")
86
85
  unless File.exist?("#{working_dir}/#{@relative_path}")
87
86
  # Cleanup the destination path - otherwise we'll have an empty dir
88
87
  # in the cache, which is enough to confuse the cache reader
89
88
  # This is a courtesy, assuming we're writing to the cache; if we're
90
89
  # vendoring to something more complex, don't bother.
91
- FileUtils.rm_r(destination_path) if Dir.exist?(destination_path)
90
+ FileUtils.rmdir(destination_path) if Dir.empty?(destination_path)
92
91
 
93
92
  raise Inspec::FetcherFailure, "Cannot find relative path '#{@relative_path}' " \
94
93
  "within profile in git repo specified by '#{@remote_url}'"
@@ -97,16 +96,9 @@ module Inspec::Fetcher
97
96
  end
98
97
 
99
98
  def cache_key
100
- cache_key = if @relative_path && !resolved_ref.nil?
101
- OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
102
- elsif @relative_path && resolved_ref.nil?
103
- OpenSSL::Digest.hexdigest("SHA256", @remote_url + @relative_path)
104
- elsif resolved_ref.nil?
105
- OpenSSL::Digest.hexdigest("SHA256", @remote_url)
106
- else
107
- resolved_ref
108
- end
109
- cache_key
99
+ return resolved_ref unless @relative_path
100
+
101
+ OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
110
102
  end
111
103
 
112
104
  def archive_path
@@ -114,11 +106,7 @@ module Inspec::Fetcher
114
106
  end
115
107
 
116
108
  def resolved_source
117
- if resolved_ref.nil?
118
- source = { git: @remote_url }
119
- else
120
- source = { git: @remote_url, ref: resolved_ref }
121
- end
109
+ source = { git: @remote_url, ref: resolved_ref }
122
110
  source[:relative_path] = @relative_path if @relative_path
123
111
  source
124
112
  end
@@ -137,27 +125,33 @@ module Inspec::Fetcher
137
125
  elsif @tag
138
126
  resolve_ref(@tag)
139
127
  else
140
- resolve_ref
128
+ resolve_ref(default_ref)
141
129
  end
142
130
  end
143
131
 
144
- def resolve_ref(ref_name = nil)
145
- command_string = if ref_name.nil?
146
- # Running git ls-remote command helps to raise error if git URL is invalid and avoids cache_key creation
147
- "git ls-remote \"#{@remote_url}\""
148
- else
149
- "git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
150
- end
132
+ def default_ref
133
+ command_string = "git remote show #{@remote_url}"
151
134
  cmd = shellout(command_string)
152
- raise(Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - error running '#{command_string}': #{cmd.stderr}") unless cmd.exitstatus == 0
153
-
154
- if ref_name.nil?
155
- ref = nil
135
+ unless cmd.exitstatus == 0
136
+ raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{@remote_url} - error running '#{command_string}': #{cmd.stderr}")
156
137
  else
157
- ref = parse_ls_remote(cmd.stdout, ref_name)
138
+ ref = cmd.stdout.lines.detect { |l| l.include? "HEAD branch:" }&.split(":")&.last&.strip
158
139
  unless ref
159
- raise Inspec::FetcherFailure, "Profile git dependency failed - unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
140
+ raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{@remote_url} - error running '#{command_string}': NULL reference")
160
141
  end
142
+
143
+ ref
144
+ end
145
+ end
146
+
147
+ def resolve_ref(ref_name)
148
+ command_string = "git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
149
+ cmd = shellout(command_string)
150
+ raise(Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - error running '#{command_string}': #{cmd.stderr}") unless cmd.exitstatus == 0
151
+
152
+ ref = parse_ls_remote(cmd.stdout, ref_name)
153
+ unless ref
154
+ raise Inspec::FetcherFailure, "Profile git dependency failed - unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
161
155
  end
162
156
 
163
157
  ref
@@ -206,14 +200,7 @@ module Inspec::Fetcher
206
200
 
207
201
  def checkout(dir = @repo_directory)
208
202
  clone(dir)
209
- # In case of branch, tag or git reference is not provided by User the resolved_ref will always be nil
210
- # and will always checkout the default HEAD branch, else it will checkout specific branch, tag or git reference.
211
- if resolved_ref.nil?
212
- git_cmd("checkout", dir)
213
- else
214
- git_cmd("checkout #{resolved_ref}", dir)
215
- end
216
-
203
+ git_cmd("checkout #{resolved_ref}", dir)
217
204
  @repo_directory
218
205
  end
219
206
 
@@ -221,8 +208,6 @@ module Inspec::Fetcher
221
208
  cmd = shellout("git #{cmd}", cwd: dir)
222
209
  cmd.error!
223
210
  cmd.status
224
- rescue Mixlib::ShellOut::ShellCommandFailed => e
225
- raise Inspec::FetcherFailure, "Error while running git command. #{e.message} "
226
211
  rescue Errno::ENOENT
227
212
  raise Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - to use git sources, you must have git installed."
228
213
  end
@@ -262,7 +262,7 @@ module Inspec::Fetcher
262
262
 
263
263
  open(target, opts)
264
264
 
265
- rescue SocketError, Net::OpenTimeout, Errno::ECONNREFUSED, OpenURI::HTTPError => e
265
+ rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e
266
266
  raise Inspec::FetcherFailure, "Profile URL dependency #{target} could not be fetched: #{e.message}"
267
267
  end
268
268
 
@@ -15,6 +15,8 @@ module Inspec::Formatters
15
15
  @profiles = []
16
16
  @profiles_info = nil
17
17
  @backend = nil
18
+ @all_controls_count = nil
19
+ @control_checks_count_map = {}
18
20
  end
19
21
 
20
22
  # RSpec Override: #dump_summary
@@ -81,6 +83,26 @@ module Inspec::Formatters
81
83
  @profiles.push(profile)
82
84
  end
83
85
 
86
+ # These control count related methods are called via runner rspec library of inspec
87
+ # And these are used within streaming plugins to determine end of control
88
+ ######### Start of control count related methods
89
+ def set_controls_count(controls_count)
90
+ @all_controls_count = controls_count
91
+ end
92
+
93
+ def set_control_checks_count_map(mapping)
94
+ @control_checks_count_map = mapping
95
+ end
96
+
97
+ def get_controls_count
98
+ @all_controls_count
99
+ end
100
+
101
+ def get_control_checks_count_map
102
+ @control_checks_count_map
103
+ end
104
+ ######### end of control count related methods
105
+
84
106
  # Return all the collected output to the caller
85
107
  def results
86
108
  run_data
@@ -41,6 +41,7 @@ module Inspec
41
41
  description
42
42
  version
43
43
  inspec_version
44
+ entitlement_id
44
45
  }.each do |name|
45
46
  define_method name.to_sym do |arg|
46
47
  params[name.to_sym] = arg
@@ -51,6 +52,10 @@ module Inspec
51
52
  params[:depends] || []
52
53
  end
53
54
 
55
+ def gem_dependencies
56
+ params[:gem_dependencies] || []
57
+ end
58
+
54
59
  def supports(sth, version = nil)
55
60
  # Ignore supports with metadata.rb. This file is legacy and the way it
56
61
  # it handles `supports` deprecated. A deprecation warning will be printed
@@ -94,6 +99,10 @@ module Inspec
94
99
  errors.push("Version needs to be in SemVer format")
95
100
  end
96
101
 
102
+ if params[:entitlement_id] && params[:entitlement_id].strip.empty?
103
+ errors.push("Entitlement ID should not be blank.")
104
+ end
105
+
97
106
  unless supports_runtime?
98
107
  warnings.push("The current inspec version #{Inspec::VERSION} cannot satisfy profile inspec_version constraint #{params[:inspec_version]}")
99
108
  end
@@ -109,6 +118,33 @@ module Inspec
109
118
  warnings.push("License '#{params[:license]}' needs to be in SPDX format or marked as 'Proprietary'. See https://spdx.org/licenses/.")
110
119
  end
111
120
 
121
+ # If gem_dependencies is set, it must be an array of hashes with keys name and optional version
122
+ unless params[:gem_dependencies].nil?
123
+ list = params[:gem_dependencies]
124
+ if list.is_a?(Array) && list.all? { |e| e.is_a? Hash }
125
+ list.each do |entry|
126
+ errors.push("gem_dependencies entries must all have a 'name' field") unless entry.key?(:name)
127
+ if entry[:version]
128
+ orig = entry[:version]
129
+ begin
130
+ # Split on commas as we may have a complex dep
131
+ orig.split(",").map { |c| Gem::Requirement.parse(c) }
132
+ rescue Gem::Requirement::BadRequirementError
133
+ errors.push "Unparseable gem dependency '#{orig}' for #{entry[:name]}"
134
+ rescue Inspec::GemDependencyInstallError => e
135
+ errors.push e.message
136
+ end
137
+ end
138
+ extra = (entry.keys - %i{name version})
139
+ unless extra.empty?
140
+ warnings.push "Unknown gem_dependencies key(s) #{extra.join(",")} seen for entry '#{entry[:name]}'"
141
+ end
142
+ end
143
+ else
144
+ errors.push("gem_dependencies must be a List of Hashes")
145
+ end
146
+ end
147
+
112
148
  [errors, warnings]
113
149
  end
114
150
 
@@ -1,10 +1,53 @@
1
1
  module Inspec::Plugin::V2::PluginType
2
- class StreamingReporter < Inspec::Plugin::V2::PluginBase # TBD Superclass may need to change
2
+ class StreamingReporter < Inspec::Plugin::V2::PluginBase
3
3
  register_plugin_type(:streaming_reporter)
4
4
 
5
5
  #====================================================================#
6
6
  # StreamingReporter plugin type API
7
7
  #====================================================================#
8
8
  # Implementation classes must implement these methods.
9
+
10
+ def initialize_streaming_reporter
11
+ @running_controls_list = []
12
+ @control_checks_count_map = {}
13
+ @controls_count = nil
14
+ end
15
+
16
+ private
17
+
18
+ # method to identify when the control started running
19
+ # this will be useful in executing operations on control's level start
20
+ def control_started?(control_id)
21
+ if @running_controls_list.include? control_id
22
+ false
23
+ else
24
+ @running_controls_list.push(control_id)
25
+ true
26
+ end
27
+ end
28
+
29
+ # method to identify when the control ended running
30
+ # this will be useful in executing operations on control's level end
31
+ def control_ended?(control_id)
32
+ set_control_checks_count_map_value
33
+ unless @control_checks_count_map[control_id].nil?
34
+ @control_checks_count_map[control_id] -= 1
35
+ @control_checks_count_map[control_id] == 0
36
+ else
37
+ false
38
+ end
39
+ end
40
+
41
+ # method to identify total no. of controls
42
+ def controls_count
43
+ @controls_count ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_controls_count
44
+ end
45
+
46
+ # this method is used in the logic of determining end of control
47
+ def set_control_checks_count_map_value
48
+ if @control_checks_count_map.empty?
49
+ @control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map
50
+ end
51
+ end
9
52
  end
10
53
  end
@@ -13,6 +13,8 @@ require "inspec/dependencies/cache"
13
13
  require "inspec/dependencies/lockfile"
14
14
  require "inspec/dependencies/dependency_set"
15
15
  require "inspec/utils/json_profile_summary"
16
+ require "inspec/dependency_loader"
17
+ require "inspec/dependency_installer"
16
18
 
17
19
  module Inspec
18
20
  class Profile
@@ -103,7 +105,6 @@ module Inspec
103
105
  @check_mode = options[:check_mode] || false
104
106
  @parent_profile = options[:parent_profile]
105
107
  @legacy_profile_path = options[:profiles_path] || false
106
- @check_cookstyle = options[:with_cookstyle]
107
108
  Metadata.finalize(@source_reader.metadata, @profile_id, options)
108
109
 
109
110
  # if a backend has already been created, clone it so each profile has its own unique backend object
@@ -379,6 +380,66 @@ module Inspec
379
380
  @runner_context
380
381
  end
381
382
 
383
+ def collect_gem_dependencies(profile_context)
384
+ gem_dependencies = []
385
+ all_profiles = []
386
+ profile_context.dependencies.list.values.each do |requirement|
387
+ all_profiles << requirement.profile
388
+ end
389
+ all_profiles << self
390
+ all_profiles.each do |profile|
391
+ gem_dependencies << profile.metadata.gem_dependencies unless profile.metadata.gem_dependencies.empty?
392
+ end
393
+ gem_dependencies.flatten.uniq
394
+ end
395
+
396
+ # Loads the required gems specified in the Profile's metadata file from default inspec gems path i.e. ~/.inspec/gems
397
+ # else installs and loads them.
398
+ def load_gem_dependencies
399
+ gem_dependencies = collect_gem_dependencies(load_libraries)
400
+ gem_dependencies.each do |gem_data|
401
+ dependency_loader = DependencyLoader.new
402
+ if dependency_loader.gem_version_installed?(gem_data[:name], gem_data[:version]) ||
403
+ dependency_loader.gem_installed?(gem_data[:name])
404
+ load_gem_dependency(gem_data)
405
+ else
406
+ if Inspec::Config.cached[:auto_install_gems]
407
+ install_gem_dependency(gem_data)
408
+ load_gem_dependency(gem_data)
409
+ else
410
+ ui = Inspec::UI.new
411
+ gem_dependencies.each { |gem_dependency| ui.list_item("#{gem_dependency[:name]} #{gem_dependency[:version]}") }
412
+ choice = ui.prompt.select("Would you like to install profile gem dependencies listed above?", %w{Yes No})
413
+ if choice == "Yes"
414
+ Inspec::Config.cached[:auto_install_gems] = true
415
+ load_gem_dependencies
416
+ else
417
+ ui.error "Unable to resolve above listed profile gem dependencies."
418
+ Inspec::UI.new.exit(:gem_dependency_load_error)
419
+ end
420
+ end
421
+ end
422
+ end
423
+ end
424
+
425
+ # Requires gem_data as argument.
426
+ # gem_dta example: { name: "gem_name", version: "0.0.1"}
427
+ def load_gem_dependency(gem_data)
428
+ dependency_loader = DependencyLoader.new(nil, [gem_data])
429
+ dependency_loader.load
430
+ rescue Inspec::GemDependencyLoadError => e
431
+ raise e.message
432
+ end
433
+
434
+ # Requires gem_data as argument.
435
+ # gem_dta example: { name: "gem_name", version: "0.0.1"}
436
+ def install_gem_dependency(gem_data)
437
+ gem_dependency = DependencyInstaller.new(nil, [gem_data])
438
+ gem_dependency.install
439
+ rescue Inspec::GemDependencyInstallError => e
440
+ raise e.message
441
+ end
442
+
382
443
  def to_s
383
444
  "Inspec::Profile<#{name}>"
384
445
  end
@@ -594,13 +655,12 @@ module Inspec
594
655
  end
595
656
 
596
657
  # Running cookstyle to check for code offenses
597
- if @check_cookstyle
598
- cookstyle_linting_check.each do |lint_output|
599
- data = lint_output.split(":")
600
- msg = "#{data[-2]}:#{data[-1]}"
601
- offense.call(data[0], data[1], data[2], nil, msg)
602
- end
658
+ cookstyle_linting_check.each do |lint_output|
659
+ data = lint_output.split(":")
660
+ msg = "#{data[-2]}:#{data[-1]}"
661
+ offense.call(data[0], data[1], data[2], nil, msg)
603
662
  end
663
+
604
664
  # profile is valid if we could not find any error & offenses
605
665
  result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
606
666
 
@@ -617,6 +677,7 @@ module Inspec
617
677
  end
618
678
 
619
679
  # generates a archive of a folder profile
680
+ # assumes that the profile was checked before
620
681
  def archive(opts)
621
682
  # check if file exists otherwise overwrite the archive
622
683
  dst = archive_name(opts)
@@ -633,34 +694,31 @@ module Inspec
633
694
  # TODO ignore all .files, but add the files to debug output
634
695
 
635
696
  # Generate temporary inspec.json for archive
636
- if opts[:export]
637
- Inspec::Utils::JsonProfileSummary.produce_json(
638
- info: info, # TODO: conditionalize and call info_from_parse
639
- write_path: "#{root_path}inspec.json",
640
- suppress_output: true
641
- )
642
- end
697
+ Inspec::Utils::JsonProfileSummary.produce_json(
698
+ info: info,
699
+ write_path: "#{root_path}inspec.json",
700
+ suppress_output: true
701
+ )
643
702
 
644
703
  # display all files that will be part of the archive
645
704
  @logger.debug "Add the following files to archive:"
646
705
  files.each { |f| @logger.debug " " + f }
647
- @logger.debug " inspec.json" if opts[:export]
706
+ @logger.debug " inspec.json"
648
707
 
649
- archive_files = opts[:export] ? files.push("inspec.json") : files
650
708
  if opts[:zip]
651
709
  # generate zip archive
652
710
  require "inspec/archive/zip"
653
711
  zag = Inspec::Archive::ZipArchiveGenerator.new
654
- zag.archive(root_path, archive_files, dst)
712
+ zag.archive(root_path, files.push("inspec.json"), dst)
655
713
  else
656
714
  # generate tar archive
657
715
  require "inspec/archive/tar"
658
716
  tag = Inspec::Archive::TarArchiveGenerator.new
659
- tag.archive(root_path, archive_files, dst)
717
+ tag.archive(root_path, files.push("inspec.json"), dst)
660
718
  end
661
719
 
662
720
  # Cleanup
663
- FileUtils.rm_f("#{root_path}inspec.json") if opts[:export]
721
+ FileUtils.rm_f("#{root_path}inspec.json")
664
722
 
665
723
  @logger.info "Finished archive generation."
666
724
  true
@@ -758,12 +816,10 @@ module Inspec
758
816
  return Pathname.new(name)
759
817
  end
760
818
 
761
- # Using metadata to fetch basic info of name and version
762
- metadata = @source_reader.metadata.params
763
- name = metadata[:name] ||
819
+ name = params[:name] ||
764
820
  raise("Cannot create an archive without a profile name! Please "\
765
821
  "specify the name in metadata or use --output to create the archive.")
766
- version = metadata[:version] ||
822
+ version = params[:version] ||
767
823
  raise("Cannot create an archive without a profile version! Please "\
768
824
  "specify the version in metadata or use --output to create the archive.")
769
825
  ext = opts[:zip] ? "zip" : "tar.gz"
@@ -780,6 +836,7 @@ module Inspec
780
836
  end
781
837
 
782
838
  def load_checks_params(params)
839
+ load_gem_dependencies
783
840
  load_libraries
784
841
  tests = collect_tests
785
842
  params[:controls] = controls = {}
@@ -791,12 +848,7 @@ module Inspec
791
848
  f = load_rule_filepath(prefix, rule)
792
849
  load_rule(rule, f, controls, groups)
793
850
  end
794
- if @profile_id.nil?
795
- # identifying inputs using profile name
796
- params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(params[:name])
797
- else
798
- params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
799
- end
851
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
800
852
  params
801
853
  end
802
854
 
@@ -21,7 +21,7 @@ module Inspec::Reporters
21
21
  final_report[:type] = "inspec_report"
22
22
 
23
23
  final_report[:end_time] = Time.now.utc.strftime("%FT%TZ")
24
- final_report[:node_uuid] = report[:platform][:target_id] || @config["node_uuid"] || @config["target_id"]
24
+ final_report[:node_uuid] = @config["node_uuid"] || report[:platform][:target_id]
25
25
  raise Inspec::ReporterError, "Cannot find a UUID for your node. Please specify one via json-config." if final_report[:node_uuid].nil?
26
26
 
27
27
  final_report[:report_uuid] = @config["report_uuid"] || uuid_from_string(final_report[:end_time] + final_report[:node_uuid])
@@ -76,7 +76,7 @@ module Inspec::Reporters
76
76
  }
77
77
  header["Failure Message"] = profile[:status_message] if profile[:status] == "failed"
78
78
  header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil?
79
- header["Target ID"] = @config["target_id"] unless @config["target_id"].nil?
79
+ header["Target ID"] = run_data[:platform][:target_id] || ""
80
80
 
81
81
  pad = header.keys.max_by(&:length).length + 1
82
82
  header.each do |title, value|
@@ -29,28 +29,48 @@ module Inspec::Reporters
29
29
  {
30
30
  name: run_data[:platform][:name],
31
31
  release: run_data[:platform][:release],
32
- target_id: run_data[:platform][:target_id] || @config["target_id"],
32
+ target_id: run_data[:platform][:target_id] || "",
33
33
  }.reject { |_k, v| v.nil? }
34
34
  end
35
35
 
36
36
  def profile_results(control)
37
37
  (control[:results] || []).map { |r|
38
38
  {
39
- status: r[:status],
40
- code_desc: r[:code_desc],
41
- run_time: r[:run_time],
42
- start_time: r[:start_time],
43
- resource: r[:resource],
44
- skip_message: r[:skip_message],
45
- message: r[:message],
46
- exception: r[:exception],
47
- backtrace: r[:backtrace],
48
- resource_class: r[:resource_class],
39
+ status: r[:status],
40
+ code_desc: r[:code_desc],
41
+ run_time: r[:run_time],
42
+ start_time: r[:start_time],
43
+ resource: r[:resource],
44
+ skip_message: r[:skip_message],
45
+ message: r[:message],
46
+ exception: r[:exception],
47
+ backtrace: r[:backtrace],
48
+ resource_class: r[:resource_class],
49
49
  resource_params: r[:resource_params].to_s,
50
+ resource_id: extract_resource_id(r),
50
51
  }.reject { |_k, v| v.nil? }
51
52
  }
52
53
  end
53
54
 
55
+ def extract_resource_id(r)
56
+ # According to the RunData API, this is supposed to be an anonymous
57
+ # class that represents a resource, with embedded instance methods....
58
+ resource_obj = r[:resource_title]
59
+ return resource_obj.resource_id if resource_obj.respond_to?(:resource_id)
60
+
61
+ # But sometimes, it isn't, and has been collapsed into the to_s stringification of the resource.
62
+ if resource_obj.is_a?(String)
63
+ orig_str = resource_obj
64
+ # Try to trim off the resource class - eg "File /some/path" => "/some/path"
65
+ trimmed_str = orig_str.sub(/^#{r[:resource_class]}/i, "").strip
66
+ trimmed_str.empty? ? orig_str : trimmed_str
67
+ else
68
+ # Boo, InSpec is crazy, and we don't know what it possibly could be.
69
+ # Failsafe for resource_id is empty string.
70
+ ""
71
+ end
72
+ end
73
+
54
74
  def profiles
55
75
  run_data[:profiles].map do |p|
56
76
  res = {
@@ -34,6 +34,12 @@ module Inspec
34
34
  Inspec::Resource.support_registry[key].push(criteria)
35
35
  end
36
36
 
37
+ def resource_id(value = nil)
38
+ @resource_id = value if value
39
+ @resource_id = "" if @resource_id.nil?
40
+ @resource_id
41
+ end
42
+
37
43
  # TODO: this is pretty terrible and is only here to work around
38
44
  # the idea that we've trained resource authors to make initialize
39
45
  # methods w/o calling super.
@@ -1,11 +1,10 @@
1
1
  module Inspec::Resources
2
2
  class Lines
3
- attr_reader :output, :exit_status
3
+ attr_reader :output
4
4
 
5
- def initialize(raw, desc, exit_status)
5
+ def initialize(raw, desc)
6
6
  @output = raw
7
7
  @desc = desc
8
- @exit_status = exit_status
9
8
  end
10
9
 
11
10
  def to_s
@@ -41,7 +40,7 @@ module Inspec::Resources
41
40
  if cmd.exit_status != 0 || out =~ /Unable to connect to any servers/ || out.downcase =~ /^error:.*/
42
41
  raise Inspec::Exceptions::ResourceFailed, "Cassandra query with errors: #{out}"
43
42
  else
44
- Lines.new(cmd.stdout.strip, "Cassandra query: #{q}", cmd.exit_status)
43
+ Lines.new(cmd.stdout.strip, "Cassandra query: #{q}")
45
44
  end
46
45
  end
47
46
 
@@ -0,0 +1,49 @@
1
+ require "inspec/resources/crontab"
2
+
3
+ module Inspec::Resources
4
+ class Cron < Crontab
5
+ name "cron"
6
+ supports platform: "unix"
7
+ desc "Use the cron InSpec audit resource to test entires in the crontab file for a given user. This also can be used as alias to crontab resource."
8
+ example <<~EXAMPLE
9
+ describe cron do
10
+ it { should have_entry '* * * * * /usr/local/bin/foo' }
11
+ end
12
+
13
+ describe cron(user: "username") do
14
+ its(:table) { should match /you can use regexp/ }
15
+ end
16
+ EXAMPLE
17
+
18
+ def initialize(opts = nil)
19
+ super
20
+ @params = read_cron_contents
21
+ end
22
+
23
+ def read_cron_contents
24
+ result = inspec.command(crontab_cmd)
25
+ if result.exit_status == 0
26
+ result.stdout.lines.map { |l| parse_comment_line(l, comment_char: "#", standalone_comments: false)[0].strip }
27
+ else
28
+ error = result.stdout + "\n" + result.stderr
29
+ raise Inspec::Exceptions::ResourceFailed, "Error while executing #{crontab_cmd} command: #{error}"
30
+ end
31
+ end
32
+
33
+ def table
34
+ @params.reject(&:empty?).join("\n")
35
+ end
36
+
37
+ def has_entry?(rule)
38
+ @params.include?(rule)
39
+ end
40
+
41
+ def to_s
42
+ if is_user_crontab?
43
+ "cron for user #{@user}"
44
+ else
45
+ "cron for current user"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -65,7 +65,7 @@ module Inspec::Resources
65
65
  def user_permissions
66
66
  return {} unless exist?
67
67
 
68
- return skip_resource "`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
68
+ return skip_reource"`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
69
69
 
70
70
  @perms_provider.user_permissions(file)
71
71
  end