inspec-core 4.56.19 → 5.12.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/etc/deprecations.json +12 -16
  3. data/inspec-core.gemspec +1 -1
  4. data/lib/inspec/base_cli.rb +14 -2
  5. data/lib/inspec/cli.rb +15 -0
  6. data/lib/inspec/dependency_installer.rb +74 -0
  7. data/lib/inspec/dependency_loader.rb +97 -0
  8. data/lib/inspec/dsl.rb +11 -2
  9. data/lib/inspec/errors.rb +7 -0
  10. data/lib/inspec/formatters/base.rb +23 -0
  11. data/lib/inspec/metadata.rb +36 -0
  12. data/lib/inspec/plugin/v2/installer.rb +9 -2
  13. data/lib/inspec/plugin/v2/loader.rb +13 -0
  14. data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
  15. data/lib/inspec/plugin/v2/status.rb +2 -1
  16. data/lib/inspec/profile.rb +63 -0
  17. data/lib/inspec/reporters/automate.rb +1 -1
  18. data/lib/inspec/reporters/cli.rb +1 -1
  19. data/lib/inspec/reporters/json.rb +31 -11
  20. data/lib/inspec/resource.rb +6 -0
  21. data/lib/inspec/resources/apt.rb +12 -6
  22. data/lib/inspec/resources/cgroup.rb +101 -0
  23. data/lib/inspec/resources/cron.rb +49 -0
  24. data/lib/inspec/resources/docker_container.rb +21 -0
  25. data/lib/inspec/resources/docker_image.rb +53 -0
  26. data/lib/inspec/resources/ipfilter.rb +59 -0
  27. data/lib/inspec/resources/ipnat.rb +58 -0
  28. data/lib/inspec/resources/lxc.rb +57 -0
  29. data/lib/inspec/resources/mail_alias.rb +46 -0
  30. data/lib/inspec/resources/routing_table.rb +137 -0
  31. data/lib/inspec/resources/service.rb +14 -1
  32. data/lib/inspec/resources/user.rb +12 -0
  33. data/lib/inspec/resources/users.rb +79 -14
  34. data/lib/inspec/resources/virtualization.rb +9 -3
  35. data/lib/inspec/resources.rb +3 -16
  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/ui.rb +10 -0
  44. data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
  45. data/lib/inspec/version.rb +1 -1
  46. data/lib/inspec.rb +3 -0
  47. data/lib/plugins/inspec-artifact/inspec-artifact.gemspec +9 -0
  48. data/lib/plugins/inspec-compliance/inspec-compliance.gemspec +9 -0
  49. data/lib/plugins/inspec-habitat/inspec-habitat.gemspec +9 -0
  50. data/lib/plugins/inspec-init/inspec-init.gemspec +9 -0
  51. data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
  52. data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
  53. data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
  54. data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
  55. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
  56. data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
  57. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
  58. data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
  59. data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
  60. data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
  61. data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
  62. data/lib/plugins/inspec-plugin-manager-cli/inspec-plugin-manager-cli.gemspec +10 -0
  63. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +15 -11
  64. data/lib/plugins/inspec-reporter-html2/inspec-reporter-html2.gemspec +9 -0
  65. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +2 -0
  66. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +3 -0
  67. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +1 -0
  68. data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
  69. data/lib/plugins/inspec-reporter-json-min/inspec-reporter-json-min.gemspec +9 -0
  70. data/lib/plugins/inspec-reporter-junit/inspec-reporter-junit.gemspec +9 -0
  71. data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
  72. data/lib/plugins/inspec-streaming-reporter-progress-bar/inspec-streaming-reporter-progress-bar.gemspec +9 -0
  73. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
  74. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +123 -0
  75. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
  76. data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
  77. metadata +33 -3
@@ -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
@@ -378,6 +380,66 @@ module Inspec
378
380
  @runner_context
379
381
  end
380
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
+
381
443
  def to_s
382
444
  "Inspec::Profile<#{name}>"
383
445
  end
@@ -774,6 +836,7 @@ module Inspec
774
836
  end
775
837
 
776
838
  def load_checks_params(params)
839
+ load_gem_dependencies
777
840
  load_libraries
778
841
  tests = collect_tests
779
842
  params[:controls] = controls = {}
@@ -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.
@@ -135,19 +135,25 @@ module Inspec::Resources
135
135
 
136
136
  class PpaRepository < AptRepository
137
137
  name "ppa"
138
+ desc "Use the ppa InSpec audit resource to verify PPA repositories on the Debian-based linux platforms."
139
+ example <<~EXAMPLE
140
+ describe ppa('ubuntu-wine/ppa') do
141
+ it { should exist }
142
+ it { should be_enabled }
143
+ end
144
+
145
+ describe ppa('ppa:ubuntu-wine/ppa') do
146
+ it { should exist }
147
+ it { should be_enabled }
148
+ end
149
+ EXAMPLE
138
150
 
139
151
  def exists?
140
- deprecated
141
152
  super()
142
153
  end
143
154
 
144
155
  def enabled?
145
- deprecated
146
156
  super()
147
157
  end
148
-
149
- def deprecated
150
- Inspec.deprecate(:resource_ppa, "The `ppa` resource is deprecated. Please use `apt`")
151
- end
152
158
  end
153
159
  end
@@ -0,0 +1,101 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class Cgroup < Inspec.resource(1)
4
+ name "cgroup"
5
+ # Restrict to only run on the below platform
6
+ supports platform: "linux"
7
+ desc "Use the cgroup InSpec audit resource to test cgroup subsytem's parameters."
8
+
9
+ example <<~EXAMPLE
10
+ describe cgroup("foo") do
11
+ its("cpuset.cpus") { should eq 0 }
12
+ its("memory.limit_in_bytes") { should eq 499712 }
13
+ its("memory.limit_in_bytes") { should be <= 500000 }
14
+ its("memory.numa_stat") { should match /total=0/ }
15
+ end
16
+ EXAMPLE
17
+
18
+ # Resource initialization.
19
+ def initialize(cgroup_name)
20
+ raise Inspec::Exceptions::ResourceSkipped, "The `cgroup` resource is not supported on your OS yet." unless inspec.os.linux?
21
+
22
+ @cgroup_name = cgroup_name
23
+ @valid_queries, @valid_queries_split = [], []
24
+ find_valid_queries
25
+ # Used to track the method calls in an "its" query
26
+ @cgroup_info_query = []
27
+ end
28
+
29
+ def resource_id
30
+ @cgroup_name
31
+ end
32
+
33
+ def to_s
34
+ "cgroup #{resource_id}"
35
+ end
36
+
37
+ def method_missing(param)
38
+ # Add the latest param we've seen to the list and form the query with all the params we've seen so far.
39
+ @cgroup_info_query << param.to_s
40
+ query = @cgroup_info_query.join(".")
41
+
42
+ # The ith level param must match with atleast one row's ith column of @valid_queries_split
43
+ # Else there is no way, we would find any valid query in further iteration, so raise exception.
44
+ if @valid_queries_split.map { |e| e[@cgroup_info_query.length - 1] }.include?(param.to_s)
45
+ # If the query form so far is part of @valid_queries, we are good to trigger find_cgroup_info
46
+ # else go for next level of param
47
+ if @valid_queries.include?(query)
48
+ @cgroup_info_query = []
49
+ find_cgroup_info(query)
50
+ else
51
+ self
52
+ end
53
+ else
54
+ @cgroup_info_query = []
55
+
56
+ raise Inspec::Exceptions::ResourceFailed, "The query #{query} does not appear to be valid."
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ # Method to find cgget tool
63
+ def find_cgget_or_error
64
+ %w{/usr/sbin/cgget /sbin/cgget cgget}.each do |cmd|
65
+ return cmd if inspec.command(cmd).exist?
66
+ end
67
+
68
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `cgget`"
69
+ end
70
+
71
+ # find the cgroup info of the query which is given as input by the user
72
+ def find_cgroup_info(query)
73
+ bin = find_cgget_or_error
74
+ cgget_cmd = "#{bin} -n -r #{query} #{@cgroup_name}"
75
+ cmd = inspec.command(cgget_cmd)
76
+
77
+ raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
78
+
79
+ # For complex returns the user must use match /the_regex/
80
+ param_value = cmd.stdout.split(":")
81
+ return nil if param_value.nil? || param_value.empty?
82
+
83
+ param_value = param_value[1].strip.split("\t").join
84
+ param_value.match(/^\d+$/) ? param_value.to_i : param_value
85
+ end
86
+
87
+ # find all the information about all relevant controllers for the current cgroup
88
+ def find_valid_queries
89
+ bin = find_cgget_or_error
90
+ cgget_all_cmd = "#{bin} -n -a #{@cgroup_name}"
91
+ cmd = inspec.command(cgget_all_cmd)
92
+
93
+ raise Inspec::Exceptions::ResourceFailed, "Executing cgget failed: #{cmd.stderr}" if cmd.exit_status.to_i != 0
94
+
95
+ queries = cmd.stdout.to_s.gsub(/:.*/, "").gsub(/^\s+.*/, "").split("\n")
96
+ # store the relevant controller parameters in @valid_queries and the dot splitted paramters into @valid_queries_split
97
+ @valid_queries = queries.map { |q| q if q.length > 0 }.compact
98
+ @valid_queries_split = @valid_queries.map { |q| q.split(".") }.compact
99
+ end
100
+ end
101
+ end
@@ -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
@@ -43,6 +43,19 @@ module Inspec::Resources
43
43
  status.downcase.start_with?("up") if object_info.entries.length == 1
44
44
  end
45
45
 
46
+ # has_volume? matcher checks if the volume specified in source path of host is mounted in destination path of docker
47
+ def has_volume?(destination, source)
48
+ # volume_info is the hash which contains the low-level information about the container
49
+ # if Mounts key is not present or is nil; raise exception
50
+ raise Inspec::Exceptions::ResourceFailed, "Could not find any mounted volumes for your container" unless volume_info.Mounts[0]
51
+
52
+ # Iterate through the list of mounted volumes and check if it matches with the given destination and source
53
+ # is_mounted flag is used to handle to return explict boolean values of true or false
54
+ is_mounted = false
55
+ volume_info.Mounts.detect { |mount| is_mounted = mount.Destination == destination && mount.Source == source }
56
+ is_mounted
57
+ end
58
+
46
59
  def status
47
60
  object_info.status[0] if object_info.entries.length == 1
48
61
  end
@@ -87,5 +100,13 @@ module Inspec::Resources
87
100
  opts = @opts
88
101
  @info = inspec.docker.containers.where { names == opts[:name] || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id]))) }
89
102
  end
103
+
104
+ # volume_info returns the low-level information obtained on docker inspect [container_name/id]
105
+ def volume_info
106
+ return @mount_info if defined?(@mount_info)
107
+
108
+ # Check for either docker inspect [container_name] or docker inspect [container_id]
109
+ @mount_info = inspec.docker.object(@opts[:name] || @opts[:id])
110
+ end
90
111
  end
91
112
  end
@@ -48,6 +48,25 @@ module Inspec::Resources
48
48
  object_info.tags[0] if object_info.entries.size == 1
49
49
  end
50
50
 
51
+ # method_missing handles when hash_keys are invoked to check information obtained on docker inspect [image_name]
52
+ def method_missing(*hash_keys)
53
+ # User can test the low-level inspect information in three ways:
54
+ # Way 1: Serverspec style: its(['Config.Cmd']) { should include some_value }
55
+ # here, the value for hash_keys recieved is [:[], "Config.Cmd"]
56
+ # Way 2: InSpec style: its(['Config','Cmd']) { should include some_value }
57
+ # here, the value for hash_keys recieved is [:[], "Config", "Cmd"]
58
+ # Way 3: Mix of both: its(['GraphDriver.Data','MergedDir']) { should include some_value }
59
+ # here, the value for hash_keys recieved is [:[], "GraphDriver.Data", "MergedDir"]
60
+
61
+ # hash_keys are passed to this method to evaluate the value
62
+ image_hash_inspection(hash_keys)
63
+ end
64
+
65
+ # inspection property allows to test any of the hash key-value pairs as part of the image_inspect_info
66
+ def inspection
67
+ image_inspect_info
68
+ end
69
+
51
70
  def to_s
52
71
  img = @opts[:image] || @opts[:id]
53
72
  "Docker Image #{img}"
@@ -80,5 +99,39 @@ module Inspec::Resources
80
99
  (repository == opts[:repo] && tag == opts[:tag]) || (!id.nil? && !opts[:id].nil? && (id == opts[:id] || id.start_with?(opts[:id])))
81
100
  end
82
101
  end
102
+
103
+ # image_inspect_info returns the complete inspect hash_values of the image
104
+ def image_inspect_info
105
+ return @inspect_info if defined?(@inspect_info)
106
+
107
+ @inspect_info = inspec.docker.object(@opts[:image] || (!@opts[:id].nil? && @opts[:id]))
108
+ end
109
+
110
+ # image_hash_inspection formats the input hash_keys and checks if any value exists for such keys in @inspect_info(image_inspect_info)
111
+ def image_hash_inspection(hash_keys)
112
+ # The hash_keys recieved are in three formats as mentioned in method_missing
113
+ # The hash_keys recieved must be in array format [] and the zeroth index must be :[]
114
+ # Check for the conditions and remove the zeroth element from the hash_keys
115
+
116
+ hash_keys.shift if hash_keys.is_a?(Array) && hash_keys[0] == :[]
117
+
118
+ # When received hash_keys in Serverspec style or mix of both
119
+ # The hash_keys are to be splitted at '.' (dot) and flatten it so that it doesn't become array of arrays
120
+ # After splitting and flattening is done, hash_keys is now an array with individual keys
121
+ hash_keys = hash_keys.map { |key| key.split(".") }.flatten
122
+
123
+ # image_inspect_info returns the complete inspect hash_values of the image
124
+ # dig() finds the nested value specified by the sequence of the key object by calling dig at each step.
125
+ # hash_keys is the key object. If one of the key is bad, value will be nil.
126
+ hash_value = image_inspect_info.dig(*hash_keys)
127
+
128
+ # If one of the key is bad, hash_value will be nil, so raise exception which throws it in rescue block
129
+ # else return hash_value
130
+ raise Inspec::Exceptions::ResourceFailed if hash_value.nil?
131
+
132
+ hash_value
133
+ rescue
134
+ raise Inspec::Exceptions::ResourceFailed, "#{hash_keys.join(".")} is not a valid key for your image or has nil value."
135
+ end
83
136
  end
84
137
  end
@@ -0,0 +1,59 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class IpFilter < Inspec.resource(1)
4
+ name "ipfilter"
5
+ supports platform: "bsd"
6
+ supports platform: "solaris"
7
+ desc "Use the ipfilter InSpec audit resource to test rules that are defined for ipfilter, which maintains the IP rule set"
8
+ example <<~EXAMPLE
9
+ describe ipfilter do
10
+ it { should have_rule("pass in quick on lo0 all") }
11
+ end
12
+ EXAMPLE
13
+
14
+ def initialize
15
+ # checks if the instance is either bsd or solaris
16
+ return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
17
+
18
+ # ensures, all calls are aborted for non-supported os
19
+ @ipfilter_cache = []
20
+ skip_resource "The `ipfilter` resource is not supported on your OS yet."
21
+ end
22
+
23
+ def has_rule?(rule = nil)
24
+ # checks if the rule is part of the ruleset
25
+ retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
26
+ end
27
+
28
+ def retrieve_rules
29
+ # this would be true if the OS family was not bsd/solaris when checked in initliaze
30
+ return @ipfilter_cache if defined?(@ipfilter_cache)
31
+
32
+ # construct ipfstat command to read all rules
33
+ bin = find_ipfstat_or_error
34
+ ipfstat_cmd = "#{bin} -io"
35
+ cmd = inspec.command(ipfstat_cmd)
36
+
37
+ # Return empty array when command is not executed successfully
38
+ # or there is no output since no rules are active
39
+ return [] if cmd.exit_status.to_i != 0 || cmd.stdout == ""
40
+
41
+ # split rules, returns array or rules
42
+ @ipfilter_cache = cmd.stdout.split("\n").map(&:strip)
43
+ end
44
+
45
+ def to_s
46
+ "Ipfilter"
47
+ end
48
+
49
+ private
50
+
51
+ def find_ipfstat_or_error
52
+ %w{/usr/sbin/ipfstat /sbin/ipfstat ipfstat}.each do |cmd|
53
+ return cmd if inspec.command(cmd).exist?
54
+ end
55
+
56
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `ipfstat`"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,58 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class IpNat < Inspec.resource(1)
4
+ name "ipnat"
5
+ supports platform: "bsd"
6
+ supports platform: "solaris"
7
+ desc "Use the ipnat InSpec audit resource to test rules that are defined for IP NAT"
8
+ example <<~EXAMPLE
9
+ describe ipnat do
10
+ it { should have_rule("map net1 192.168.0.0/24 -> 0/32") }
11
+ end
12
+ EXAMPLE
13
+
14
+ def initialize
15
+ # checks if the instance is either bsd or solaris
16
+ return if (inspec.os.bsd? && !inspec.os.darwin?) || inspec.os.solaris?
17
+
18
+ # ensures, all calls are aborted for non-supported os
19
+ @ipnat_cache = []
20
+ skip_resource "The `ipnat` resource is not supported on your OS yet."
21
+ end
22
+
23
+ def has_rule?(rule = nil)
24
+ # checks if the rule is part of the ruleset
25
+ retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
26
+ end
27
+
28
+ def retrieve_rules
29
+ # this would be true if the OS family was not bsd/solaris when checked in initliaze
30
+ return @ipnat_cache if defined?(@ipnat_cache)
31
+
32
+ # construct ipnat command to show the list of current IP NAT table entry mappings
33
+ bin = find_ipnat_or_error
34
+ ipnat_cmd = "#{bin} -l"
35
+ cmd = inspec.command(ipnat_cmd)
36
+
37
+ # Return empty array when command is not executed successfully
38
+ return [] if cmd.exit_status.to_i != 0
39
+
40
+ # split rules, returns array or rules
41
+ @ipnat_cache = cmd.stdout.split("\n").map(&:strip)
42
+ end
43
+
44
+ def to_s
45
+ "Ipnat"
46
+ end
47
+
48
+ private
49
+
50
+ def find_ipnat_or_error
51
+ %w{/usr/sbin/ipnat /sbin/ipnat ipnat}.each do |cmd|
52
+ return cmd if inspec.command(cmd).exist?
53
+ end
54
+
55
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `ipnat`"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,57 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class Lxc < Inspec.resource(1)
4
+ name "lxc"
5
+ # Restrict to only run on the below platforms
6
+ supports platform: "linux"
7
+ desc "Use the lxc InSpec audit resource to test if container exists and/or is running for linux container"
8
+ example <<~EXAMPLE
9
+ describe lxc("ubuntu-container") do
10
+ it { should exist }
11
+ it { should be_running }
12
+ end
13
+ EXAMPLE
14
+
15
+ # Resource initialization.
16
+ def initialize(container_name)
17
+ @container_name = container_name
18
+
19
+ raise Inspec::Exceptions::ResourceSkipped, "The `lxc` resource is not supported on your OS yet." unless inspec.os.linux?
20
+ end
21
+
22
+ def resource_id
23
+ @container_name
24
+ end
25
+
26
+ def to_s
27
+ "lxc #{resource_id}"
28
+ end
29
+
30
+ def exists?
31
+ lxc_info_cmd.exit_status.to_i == 0
32
+ end
33
+
34
+ def running?
35
+ container_info = lxc_info_cmd.stdout.split(":").map(&:strip)
36
+ container_info[0] == "Status" && container_info[1] == "Running"
37
+ end
38
+
39
+ private
40
+
41
+ # Method to find lxc
42
+ def find_lxc_or_error
43
+ %w{/usr/sbin/lxc /sbin/lxc lxc}.each do |cmd|
44
+ return cmd if inspec.command(cmd).exist?
45
+ end
46
+
47
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `lxc`"
48
+ end
49
+
50
+ def lxc_info_cmd
51
+ bin = find_lxc_or_error
52
+ info_cmd = "info #{@container_name} | grep -i Status"
53
+ lxc_cmd = format("%s %s", bin, info_cmd).strip
54
+ inspec.command(lxc_cmd)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,46 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class Mailalias < Inspec.resource(1)
4
+ # resource internal name.
5
+ name "mail_alias"
6
+
7
+ # Restrict to only run on the below platforms (if none were given,
8
+ # all OS's and cloud API's supported)
9
+ supports platform: "unix"
10
+
11
+ desc "Use the mail_alias InSpec audit resource to test mail alias present in the aliases file"
12
+
13
+ example <<~EXAMPLE
14
+ describe mail_alias("toor") do
15
+ it { should be_aliased_to "root" }
16
+ end
17
+ EXAMPLE
18
+
19
+ def initialize(alias_key)
20
+ skip_resource "The `mail_alias` resource is not yet available on your OS." unless inspec.os.unix?
21
+ @alias_key = alias_key
22
+ end
23
+
24
+ # resource_id is used in reporting engines to uniquely identify the individual resource.
25
+ def resource_id
26
+ "#{@alias_key}"
27
+ end
28
+
29
+ # resource appearance in test reports.
30
+ def to_s
31
+ "mail_alias #{resource_id}"
32
+ end
33
+
34
+ # aliased_to matcher checks if the given alias_value is set to the initialized alias_key
35
+ def aliased_to?(alias_value)
36
+ # /etc/aliases if the file where the alias and its value(s) are stored
37
+ cmd = inspec.command("cat /etc/aliases | grep '^#{@alias_key}:'")
38
+ raise Inspec::Exceptions::ResourceFailed, "#{@alias_key} is not a valid key in the aliases" if cmd.exit_status.to_i != 0
39
+
40
+ # in general aliases file contains : separated values like alias_key : alias_value1, alias_value2
41
+ alias_values_combined = cmd.stdout.split(":").map(&:strip)[1]
42
+ alias_values_splitted = alias_values_combined.split(",").map(&:strip)
43
+ alias_values_splitted.include?(alias_value)
44
+ end
45
+ end
46
+ end