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 +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")
|