inspec-core 4.56.58 → 5.7.9

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