inspec-core 5.10.5 → 5.17.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/etc/keys/progress-2022-05-04.pem.pub +9 -0
- data/lib/inspec/cli.rb +2 -0
- data/lib/inspec/dependency_loader.rb +5 -1
- data/lib/inspec/profile.rb +14 -7
- data/lib/inspec/resources/default_gateway.rb +61 -0
- data/lib/inspec/resources/docker_container.rb +21 -0
- data/lib/inspec/resources/docker_image.rb +53 -0
- data/lib/inspec/resources/file.rb +91 -0
- data/lib/inspec/resources/groups.rb +5 -0
- data/lib/inspec/resources/host.rb +42 -3
- data/lib/inspec/resources/linux_audit_system.rb +81 -0
- data/lib/inspec/resources/mail_alias.rb +46 -0
- data/lib/inspec/resources/php_config.rb +72 -0
- data/lib/inspec/resources/processes.rb +11 -0
- data/lib/inspec/resources/routing_table.rb +137 -0
- data/lib/inspec/resources/service.rb +90 -2
- data/lib/inspec/resources/user.rb +12 -0
- data/lib/inspec/resources/users.rb +79 -14
- data/lib/inspec/resources/x509_private_key.rb +93 -0
- data/lib/inspec/resources/zfs.rb +48 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +32 -21
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b31dbb074483f274162eeea0fde1b234cd19e1c65257e19f7d0bc2f46c375b70
|
4
|
+
data.tar.gz: 31756fedad66edc248e5ae7b1ca5ab08408f6d7d6e6ce6dfb9053d1323c1acac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
data/lib/inspec/profile.rb
CHANGED
@@ -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
|
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
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
313
|
+
resolve_ipv4 = JSON.parse(cmd.stdout)
|
302
314
|
rescue JSON::ParserError => _e
|
303
315
|
return nil
|
304
316
|
end
|
305
317
|
|
306
|
-
|
307
|
-
|
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")
|