inspec-core 5.10.5 → 5.17.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d96c590b3ff91ce5db5eaf8ee6ef68721fd17d103f250c8c3106941784f8f336
4
- data.tar.gz: 50b56c3506f186b51fcf07df23a59050461132e0c03245bed088961ef0e854fb
3
+ metadata.gz: b31dbb074483f274162eeea0fde1b234cd19e1c65257e19f7d0bc2f46c375b70
4
+ data.tar.gz: 31756fedad66edc248e5ae7b1ca5ab08408f6d7d6e6ce6dfb9053d1323c1acac
5
5
  SHA512:
6
- metadata.gz: 0eeb703b52391323d79b9ff99ce9bdc4b4aac5903fd6e10087b7ae92372b733b7a5779672fb67a3dc7aa6cfb2e3c859ca35ab81a6e49c0331ae623487e6ea241
7
- data.tar.gz: 7f076f8c2bef5080a73b6fb8886657137a558bf9c624dd11906c91ef9f555e8c6713af8f62282f9a2fa20e650624fbcd3172fe0e9557f59dd8eb539684192ff1
6
+ metadata.gz: e765163668a3799ae45fbe2a5ddd219832341c32b01b62422506324a62c44b99ad771f47c662be5abb1198fdd65969e2a038f54c5059841561edda42170f7631
7
+ data.tar.gz: bbbadea0724f15d1a67c895eab750eea1a660bfc2e65143af703f8b5a88eb9e0c9fde1f9c08356fd9e4b32d202d620902fbad8784618cc01eec866c20f5247c7
@@ -0,0 +1,9 @@
1
+ -----BEGIN PUBLIC KEY-----
2
+ MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8FWKhVwT5ilFdk5/schY
3
+ J3X6DkDbZPul7MAKYuicmyvrk4VZFqFJYzUo9G+ZvtWCjCMZF3JLDbg0VJ+V3j2b
4
+ A5MRUDMH1MtbQZS8u0AIUAitHsSiMgu4w/EHUSHODoDmbdaHT1xkFe9IxMSM1/AV
5
+ /s5u2uAMuiayo+dGW5i9xf/LMZN1JCeX8Yqw85CpS01gMC2XxoUu5wGLwBwXkdql
6
+ +H3SaWRdDTtccgtu0Rxt9dzRtHAPNWKSLl9TScW6Qt/+7bgb3M6+7od/FuPziAS9
7
+ 1NGUiKL9vajpafxUJF8863q0l64dAXGWXVFed7/7GFiNVuLxBksPzK8VrkyCS214
8
+ kQIDAQAB
9
+ -----END PUBLIC KEY-----
data/lib/inspec/cli.rb CHANGED
@@ -95,6 +95,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
95
95
  desc "check PATH", "verify all tests at the specified PATH"
96
96
  option :format, type: :string,
97
97
  desc: "The output format to use doc (default), json. If valid format is not provided then it will use the default."
98
+ option :with_cookstyle, type: :boolean,
99
+ desc: "Enable or disable cookstyle checks.", default: false
98
100
  profile_options
99
101
  def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
100
102
  o = config
@@ -50,7 +50,11 @@ module Inspec
50
50
  end
51
51
 
52
52
  def gem_version_installed?(name, version)
53
- list_installed_gems.any? { |s| s.name == name && Gem::Requirement.new(version.split(",")) =~ s.version }
53
+ if version.nil?
54
+ list_installed_gems.any? { |s| s.name == name }
55
+ else
56
+ list_installed_gems.any? { |s| s.name == name && Gem::Requirement.new(version.split(",")) =~ s.version }
57
+ end
54
58
  end
55
59
 
56
60
  private
@@ -105,6 +105,7 @@ module Inspec
105
105
  @check_mode = options[:check_mode] || false
106
106
  @parent_profile = options[:parent_profile]
107
107
  @legacy_profile_path = options[:profiles_path] || false
108
+ @check_cookstyle = options[:with_cookstyle]
108
109
  Metadata.finalize(@source_reader.metadata, @profile_id, options)
109
110
 
110
111
  # if a backend has already been created, clone it so each profile has its own unique backend object
@@ -409,7 +410,7 @@ module Inspec
409
410
  else
410
411
  ui = Inspec::UI.new
411
412
  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
+ choice = ui.prompt.select("The above listed gem dependencies are required to run the profile. Would you like to install them ?", %w{Yes No})
413
414
  if choice == "Yes"
414
415
  Inspec::Config.cached[:auto_install_gems] = true
415
416
  load_gem_dependencies
@@ -655,12 +656,13 @@ module Inspec
655
656
  end
656
657
 
657
658
  # Running cookstyle to check for code offenses
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)
659
+ if @check_cookstyle
660
+ cookstyle_linting_check.each do |lint_output|
661
+ data = lint_output.split(":")
662
+ msg = "#{data[-2]}:#{data[-1]}"
663
+ offense.call(data[0], data[1], data[2], nil, msg)
664
+ end
662
665
  end
663
-
664
666
  # profile is valid if we could not find any error & offenses
665
667
  result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
666
668
 
@@ -848,7 +850,12 @@ module Inspec
848
850
  f = load_rule_filepath(prefix, rule)
849
851
  load_rule(rule, f, controls, groups)
850
852
  end
851
- params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
853
+ if @profile_id.nil?
854
+ # identifying inputs using profile name
855
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(params[:name])
856
+ else
857
+ params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
858
+ end
852
859
  params
853
860
  end
854
861
 
@@ -0,0 +1,61 @@
1
+ require "inspec/resources/command"
2
+ require_relative "routing_table"
3
+
4
+ module Inspec::Resources
5
+ class Defaultgateway < Routingtable
6
+ # resource internal name.
7
+ name "default_gateway"
8
+
9
+ # Restrict to only run on the below platforms (if none were given,
10
+ # all OS's and cloud API's supported)
11
+ supports platform: "unix"
12
+ supports platform: "windows"
13
+
14
+ desc "Use the `default_gateway` Chef InSpec audit resource to test the assigned ip address and interface for the default route."
15
+
16
+ example <<~EXAMPLE
17
+ describe default_gateway do
18
+ its(:ipaddress) { should eq '172.31.80.1' }
19
+ end
20
+ describe default_gateway do
21
+ its("interface") { should eq 'eth0' }
22
+ end
23
+ EXAMPLE
24
+
25
+ def initialize
26
+ skip_resource "The `default_gateway` resource is not yet available on your OS." unless inspec.os.unix? || inspec.os.windows?
27
+ # invoke the routing_table initialize; which populates the @routing_info
28
+ super()
29
+ end
30
+
31
+ # resource appearance in test reports.
32
+ def to_s
33
+ "default_gateway"
34
+ end
35
+
36
+ # fetches the ipaddress assigned to the default gateway
37
+ # default gateway's destination is either `default` or `0.0.0.0`
38
+ def ipaddress
39
+ # @routing_info is the hash populated in routing_table resource
40
+ # @routing_info contain values as:
41
+ # {
42
+ # destination1: [ [gateway1x, interface1x], [gateway1y, interface1y] ],
43
+ # destination2: [gateway2, interface2]
44
+ # }
45
+ %w{default 0.0.0.0}.each do |destination|
46
+ return @routing_info[destination][0][0] if @routing_info.key?(destination)
47
+ end
48
+ # raise exception because no destination with value default or 0.0.0.0 is found in the routing table
49
+ raise Inspec::Exceptions::ResourceFailed, "No routing found as part of default gateway"
50
+ end
51
+
52
+ # fetches the interface assigned to the default gateway
53
+ def interface
54
+ %w{default 0.0.0.0}.each do |destination|
55
+ return @routing_info[destination][0][1] if @routing_info.key?(destination)
56
+ end
57
+ # raise exception because no destination with value default or 0.0.0.0 is found in the routing table
58
+ raise Inspec::Exceptions::ResourceFailed, "No routing found as part of default gateway"
59
+ end
60
+ end
61
+ 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
@@ -181,6 +181,34 @@ module Inspec::Resources
181
181
  inv_mode & file.mode != 0
182
182
  end
183
183
 
184
+ def immutable?
185
+ raise Inspec::Exceptions::ResourceSkipped, "The `be_immutable` matcher is not supported on your OS yet." unless inspec.os.unix?
186
+
187
+ if inspec.os.linux?
188
+ file_info = LinuxImmutableFlagCheck.new(inspec, file)
189
+ else
190
+ file_info = UnixImmutableFlagCheck.new(inspec, file)
191
+ end
192
+
193
+ file_info.is_immutable?
194
+ end
195
+
196
+ # parse the json file content and returns the content
197
+ def content_as_json
198
+ require "json" unless defined?(JSON)
199
+ JSON.parse(file.content)
200
+ rescue => e
201
+ raise Inspec::Exceptions::ResourceFailed, "Unable to parse the given JSON file: #{e.message}"
202
+ end
203
+
204
+ # parse the yaml file content and returns the content
205
+ def content_as_yaml
206
+ require "yaml" unless defined?(YAML)
207
+ YAML.load(file.content)
208
+ rescue => e
209
+ raise Inspec::Exceptions::ResourceFailed, "Unable to parse the given YAML file: #{e.message}"
210
+ end
211
+
184
212
  def to_s
185
213
  if file
186
214
  "File #{source_path}"
@@ -373,4 +401,67 @@ module Inspec::Resources
373
401
  end
374
402
  end
375
403
  end
404
+
405
+ # Helper class for immutable matcher.
406
+ class ImmutableFlagCheck
407
+ attr_reader :inspec, :file_path
408
+ def initialize(inspec, file)
409
+ @inspec = inspec
410
+ @file_path = file.path
411
+ end
412
+
413
+ def find_utility_or_error(utility_name)
414
+ [
415
+ "/usr/sbin/#{utility_name}",
416
+ "/sbin/#{utility_name}",
417
+ "/usr/bin/#{utility_name}",
418
+ "/bin/#{utility_name}",
419
+ "#{utility_name}",
420
+ ].each do |cmd|
421
+ return cmd if inspec.command(cmd).exist?
422
+ end
423
+
424
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `#{utility_name}`"
425
+ end
426
+ end
427
+
428
+ class LinuxImmutableFlagCheck < ImmutableFlagCheck
429
+ def is_immutable?
430
+ # Check if lsattr is available. In general, all linux system has lsattr & chattr
431
+ # This logic check is valid for immutable flag set with chattr
432
+ utility = find_utility_or_error("lsattr")
433
+ utility_cmd = inspec.command("#{utility} #{file_path}")
434
+
435
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} #{file_path} failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
436
+
437
+ # General output for lsattr file_name is:
438
+ # ----i---------e----- file_name
439
+ # The fifth char resembles the immutable flag. Total 20 flags are allowed.
440
+ lsattr_info = utility_cmd.stdout.strip.squeeze(" ")
441
+ lsattr_info =~ /^.{4}i.{15} .*/
442
+ end
443
+ end
444
+
445
+ class UnixImmutableFlagCheck < ImmutableFlagCheck
446
+ def is_immutable?
447
+ # Check if chflags is available on the system. Most unix-like system comes with chflags.
448
+ # This logic check is valid for immutable flag set with chflags
449
+ find_utility_or_error("chflags")
450
+
451
+ # In general ls -lO is used to check immutable flag set by chflags
452
+ utility_cmd = inspec.command("ls -lO #{file_path}")
453
+
454
+ # But on some bsd system (eg: freebsd) ls -lo is used instead of ls -lO
455
+ utility_cmd = inspec.command("ls -lo #{file_path}") if utility_cmd.exit_status.to_i != 0
456
+
457
+ raise Inspec::Exceptions::ResourceFailed, "Executing ls -lo #{file_path} and ls -lO #{file_path} failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
458
+
459
+ # General output for ls -lO file_name is:
460
+ # -rw-r--r-- 1 current_user 1083951318 uchg 0 Apr 6 12:45 file_name
461
+ # The schg flag and the uchg flag represents the immutable flags
462
+ # uchg => user immutable flag, schg => system immutable flag.
463
+ file_info = utility_cmd.stdout.strip.split
464
+ file_info.include?("uchg") || file_info.include?("schg")
465
+ end
466
+ end
376
467
  end
@@ -145,6 +145,11 @@ module Inspec::Resources
145
145
  true
146
146
  end
147
147
 
148
+ # matcher equivalent to gid property.
149
+ def has_gid?(gid_value)
150
+ gid_value == gid
151
+ end
152
+
148
153
  def to_s
149
154
  "Group #{@group}"
150
155
  end
@@ -113,6 +113,16 @@ module Inspec::Resources
113
113
  resolve.nil? || resolve.empty? ? nil : resolve
114
114
  end
115
115
 
116
+ # returns an array of the ipv4 addresses
117
+ def ipv4_address
118
+ ipaddress.select { |ip| ip.match(Resolv::IPv4::Regex) }
119
+ end
120
+
121
+ # returns an array of the ipv6 addresses
122
+ def ipv6_address
123
+ ipaddress.select { |ip| ip.match(Resolv::IPv6::Regex) }
124
+ end
125
+
116
126
  def to_s
117
127
  resource_name = "Host #{hostname}"
118
128
  resource_name += " port #{port} proto #{protocol}" if port
@@ -296,15 +306,44 @@ module Inspec::Resources
296
306
  end
297
307
 
298
308
  def resolve(hostname)
309
+ addresses = []
310
+ # -Type A is the DNS query for IPv4 server Address.
299
311
  cmd = inspec.command("Resolve-DnsName –Type A #{hostname} | ConvertTo-Json")
300
312
  begin
301
- resolv = JSON.parse(cmd.stdout)
313
+ resolve_ipv4 = JSON.parse(cmd.stdout)
302
314
  rescue JSON::ParserError => _e
303
315
  return nil
304
316
  end
305
317
 
306
- resolv = [resolv] unless resolv.is_a?(Array)
307
- resolv.map { |entry| entry["IPAddress"] }
318
+ resolve_ipv4 = resolve_ipv4.inject(:merge) if resolve_ipv4.is_a?(Array)
319
+
320
+ # Append the ipv4 addresses
321
+ resolve_ipv4.each_value do |ip|
322
+ matched = ip.to_s.chomp.match(Resolv::IPv4::Regex)
323
+ next if matched.nil? || addresses.include?(matched.to_s)
324
+
325
+ addresses << matched.to_s
326
+ end
327
+
328
+ # -Type AAAA is the DNS query for IPv6 server Address.
329
+ cmd = inspec.command("Resolve-DnsName –Type AAAA #{hostname} | ConvertTo-Json")
330
+ begin
331
+ resolve_ipv6 = JSON.parse(cmd.stdout)
332
+ rescue JSON::ParserError => _e
333
+ return nil
334
+ end
335
+
336
+ resolve_ipv6 = resolve_ipv6.inject(:merge) if resolve_ipv6.is_a?(Array)
337
+
338
+ # Append the ipv6 addresses
339
+ resolve_ipv6.each_value do |ip|
340
+ matched = ip.to_s.chomp.match(Resolv::IPv6::Regex)
341
+ next if matched.nil? || addresses.include?(matched.to_s)
342
+
343
+ addresses << matched.to_s
344
+ end
345
+
346
+ addresses
308
347
  end
309
348
  end
310
349
  end
@@ -0,0 +1,81 @@
1
+ require "inspec/resources/command"
2
+ module Inspec::Resources
3
+ class LinuxAuditSystem < Inspec.resource(1)
4
+ # Resource's internal name.
5
+ name "linux_audit_system"
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: "linux"
10
+
11
+ desc "Use the `linux_audit_system` Chef InSpec audit resource to test the configuration of linux audit system."
12
+
13
+ example <<~EXAMPLE
14
+ describe linux_audit_system do
15
+ it { should be_enabled }
16
+ it { should be_running }
17
+ its("rules") { should include "-w /etc -p wa" }
18
+ its("rules") { should include %r{-w /etc -p wa} }
19
+ its("rules") { should include %r!-w /etc -p wa! }
20
+ end
21
+ EXAMPLE
22
+
23
+ attr_reader :auditctl_utility
24
+
25
+ # Resource initialization.
26
+ def initialize
27
+ skip_resource "The `linux_audit_system` resource is not yet available on your OS." unless inspec.os.linux?
28
+ @auditctl_utility = find_auditctl_or_error
29
+ end
30
+
31
+ # Resource appearance in test reports.
32
+ def to_s
33
+ "linux_audit_system"
34
+ end
35
+
36
+ # The be_enabled matcher checks if the auditing is enabled.
37
+ # The enabled flag 1 indicates that the auditing is enabled.
38
+ def enabled?
39
+ auditctl_cmd = inspec.command("#{auditctl_utility} -s | grep enabled")
40
+
41
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{auditctl_utility} -s | grep enabled failed: #{auditctl_cmd.stderr}" if auditctl_cmd.exit_status.to_i != 0
42
+
43
+ # Sample stdout: enabled 1
44
+ auditctl_enabled_status = auditctl_cmd.stdout.strip.split
45
+ auditctl_enabled_status[1].to_i == 1
46
+ end
47
+
48
+ # The be_running matcher checks if the audit daemon is running.
49
+ # A pid of 0 indicates that the audit daemon is not running.
50
+ def running?
51
+ auditctl_cmd = inspec.command("#{auditctl_utility} -s | grep pid")
52
+
53
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{auditctl_utility} -s | grep enabled failed: #{auditctl_cmd.stderr}" if auditctl_cmd.exit_status.to_i != 0
54
+
55
+ # Sample stdout: pid 682462
56
+ auditctl_running_status = auditctl_cmd.stdout.strip.split
57
+ !auditctl_running_status[1].nil? && auditctl_running_status[1].to_i != 0
58
+ end
59
+
60
+ # The rules property returns the array of audit rules obtained on auditctl -l.
61
+ # The auditctl -l list all rules, 1 per line.
62
+ def rules
63
+ auditctl_cmd = inspec.command("#{auditctl_utility} -l")
64
+
65
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{auditctl_utility} -l: #{auditctl_cmd.stderr}" if auditctl_cmd.exit_status.to_i != 0
66
+
67
+ auditctl_cmd.stdout.strip.split("\n")
68
+ end
69
+
70
+ private
71
+
72
+ # Check if auditctl is available on the system.
73
+ def find_auditctl_or_error
74
+ %w{/usr/sbin/auditctl /sbin/auditctl auditctl}.each do |cmd|
75
+ return cmd if inspec.command(cmd).exist?
76
+ end
77
+
78
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `auditctl`. This resource requires `auditctl` utility to be available on the system."
79
+ end
80
+ end
81
+ 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
@@ -0,0 +1,72 @@
1
+ require "inspec/resources/command"
2
+
3
+ module Inspec::Resources
4
+ class PhpConfig < Inspec.resource(1)
5
+ # Resource's internal name.
6
+ name "php_config"
7
+ supports platform: "unix"
8
+ supports platform: "windows"
9
+ desc "Use the php_config InSpec audit resource to test PHP config parameters"
10
+
11
+ example <<~EXAMPLE
12
+ describe php_config("config_param") do
13
+ its("value") { should eq "some_value" }
14
+ end
15
+
16
+ describe php_config("config_param", { "ini" => "path_to_ini_file" }) do
17
+ its("value") { should eq "some_value" }
18
+ end
19
+ EXAMPLE
20
+
21
+ # Resource initialization.
22
+ attr_reader :config_param, :config_file_or_path
23
+ def initialize(config_param, config_file_or_path = {})
24
+ @config_param = config_param
25
+ @config_file_or_path = config_file_or_path
26
+ end
27
+
28
+ # Unique resource id
29
+ def resource_id
30
+ config_param
31
+ end
32
+
33
+ # Resource appearance in test reports.
34
+ def to_s
35
+ "php_config #{resource_id}"
36
+ end
37
+
38
+ # Returns the value evaluated for the initialized config parameter
39
+ def value
40
+ php_utility = find_utility_or_error
41
+
42
+ # The keys in the hash provided by user can be string or symbols.
43
+ # Converting the key to symbols to handle scenario when "ini" key is provided as string.
44
+ config_file_or_path.transform_keys(&:to_sym)
45
+
46
+ # Assign the path with -c option for ini file provided by the user if any.
47
+ php_ini_file = !config_file_or_path.empty? && config_file_or_path.key?(:ini) ? "-c #{config_file_or_path[:ini]}" : ""
48
+
49
+ # The below command `get_cfg_var` is used to fetch the value for any config parameter.
50
+ php_cmd = "#{php_utility} #{php_ini_file} -r 'echo get_cfg_var(\"#{config_param}\");'"
51
+ config_value_cmd = inspec.command(php_cmd)
52
+
53
+ raise Inspec::Exceptions::ResourceFailed, "Executing #{php_cmd} failed: #{config_value_cmd.stderr}" if config_value_cmd.exit_status.to_i != 0
54
+
55
+ config_value = config_value_cmd.stdout.strip
56
+
57
+ # Convert value to integer if the config value are digits.
58
+ config_value.match(/^(\d)+$/) ? config_value.to_i : config_value
59
+ end
60
+
61
+ private
62
+
63
+ # Method to check if php is present or not on the system.
64
+ def find_utility_or_error
65
+ %w{/usr/sbin/php /sbin/php php}.each do |cmd|
66
+ return cmd if inspec.command(cmd).exist?
67
+ end
68
+
69
+ raise Inspec::Exceptions::ResourceFailed, "Could not find `php` on your system."
70
+ end
71
+ end
72
+ end
@@ -60,6 +60,17 @@ module Inspec::Resources
60
60
  @list
61
61
  end
62
62
 
63
+ # Matcher to check if the process is running
64
+ def running?
65
+ # A process is considered running if:
66
+ # unix: it is in running(R) state or either of sleep state(D: Uninterruptible or S: Interruptible)
67
+ # windows: it is responding i.e. state is True.
68
+
69
+ # Other codes like <(high priorty), N(low priority), +(foreground process group) etc. may appear after the state code in unix.
70
+ # Hence the regex used is /^statecode+/ where statecode is either R, S, or D.
71
+ states.any? and !!(states[0] =~ /True/ || states[0] =~ /^R+/ || states[0] =~ /^D+/ || states[0] =~ /^S+/)
72
+ end
73
+
63
74
  filter = FilterTable.create
64
75
  filter.register_column(:labels, field: "label")
65
76
  .register_column(:pids, field: "pid")