inspec-core 5.12.2 → 5.14.0
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/lib/inspec/cli.rb +2 -0
- data/lib/inspec/profile.rb +7 -5
- data/lib/inspec/resources/default_gateway.rb +61 -0
- data/lib/inspec/resources/file.rb +91 -0
- data/lib/inspec/resources/groups.rb +5 -0
- data/lib/inspec/resources/linux_audit_system.rb +81 -0
- data/lib/inspec/resources/service.rb +73 -0
- data/lib/inspec/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cb92e0f21964ecac43d5d9f208e7a2b07a57c2daa0fef4aad06515f933b9072
|
4
|
+
data.tar.gz: 6c4d5f59ed6dde73193198f4ad32c801515cec12d9c04924765d1ac7ee4bd5bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8397f679eea47dead2cb1952026b74c800adf7ea650120597b714c3ccdef1c7e0eeb6af387458a31c028173a6bc332b07de103d4d545dbd37257573b77c6e0b1
|
7
|
+
data.tar.gz: 96d8ac046aa00ec95fc173e81c28999d744ed823af7a4a6401e9d1776817e303e451e7c8d46450d2c39c02e6ef4ff4c82d1c425683e82299effa2f3384f9f93e
|
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
|
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
|
@@ -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
|
|
@@ -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
|
@@ -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
|
@@ -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
|
@@ -271,6 +271,30 @@ module Inspec::Resources
|
|
271
271
|
info[:startname]
|
272
272
|
end
|
273
273
|
|
274
|
+
# matcher equivalent to startmode property; compares start-up mode
|
275
|
+
# supported only on windows.
|
276
|
+
def has_start_mode?(mode)
|
277
|
+
raise Inspec::Exceptions::ResourceSkipped, "The `has_start_mode` matcher is not supported on your OS yet." unless inspec.os.windows?
|
278
|
+
|
279
|
+
mode == startmode
|
280
|
+
end
|
281
|
+
|
282
|
+
# matcher to check if the service is monitored by the given monitoring tool/software
|
283
|
+
def monitored_by?(monitoring_tool)
|
284
|
+
# Currently supported monitoring tools are: monit & god
|
285
|
+
# To add support for new monitoring tools, extend the case statement with additional monitoring tool and
|
286
|
+
# add the definition and logic in a new class (inheriting the base class MonitoringTool: optional)
|
287
|
+
case monitoring_tool
|
288
|
+
when "monit"
|
289
|
+
current_monitoring_tool = Monit.new(inspec, @service_name)
|
290
|
+
when "god"
|
291
|
+
current_monitoring_tool = God.new(inspec, @service_name)
|
292
|
+
else
|
293
|
+
puts "The monitoring tool #{monitoring_tool} is not yet supported by InSpec."
|
294
|
+
end
|
295
|
+
current_monitoring_tool.is_service_monitored?
|
296
|
+
end
|
297
|
+
|
274
298
|
def to_s
|
275
299
|
"Service #{@service_name}"
|
276
300
|
end
|
@@ -893,4 +917,53 @@ module Inspec::Resources
|
|
893
917
|
Runit.new(inspec, service_ctl)
|
894
918
|
end
|
895
919
|
end
|
920
|
+
|
921
|
+
# Helper class for monitored_by matcher
|
922
|
+
class MonitoringTool
|
923
|
+
attr_reader :inspec, :service_name
|
924
|
+
def initialize(inspec, service_name)
|
925
|
+
@inspec = inspec
|
926
|
+
@service_name ||= service_name
|
927
|
+
end
|
928
|
+
|
929
|
+
def find_utility_or_error(utility_name)
|
930
|
+
[ "/usr/sbin/#{utility_name}" , "/sbin/#{utility_name}" , "/usr/bin/#{utility_name}" , "/bin/#{utility_name}" , "#{utility_name}" ].each do |cmd|
|
931
|
+
return cmd if inspec.command(cmd).exist?
|
932
|
+
end
|
933
|
+
|
934
|
+
raise Inspec::Exceptions::ResourceFailed, "Could not find `#{utility_name}`"
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
class Monit < MonitoringTool
|
939
|
+
def is_service_monitored?
|
940
|
+
utility = find_utility_or_error("monit")
|
941
|
+
utility_cmd = inspec.command("#{utility} summary")
|
942
|
+
|
943
|
+
raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} summary failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
|
944
|
+
|
945
|
+
monitoring_info = utility_cmd.stdout.split("\n")
|
946
|
+
monitoring_info.map! { |info| info.strip.squeeze(" ") }
|
947
|
+
is_monitored = false
|
948
|
+
monitoring_info.each do |info|
|
949
|
+
if info =~ /^#{service_name} OK.*/
|
950
|
+
is_monitored = true
|
951
|
+
break
|
952
|
+
end
|
953
|
+
end
|
954
|
+
is_monitored
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
class God < MonitoringTool
|
959
|
+
def is_service_monitored?
|
960
|
+
utility = find_utility_or_error("god")
|
961
|
+
utility_cmd = inspec.command("#{utility} status #{service_name}")
|
962
|
+
|
963
|
+
raise Inspec::Exceptions::ResourceFailed, "Executing #{utility} status #{service_name} failed: #{utility_cmd.stderr}" if utility_cmd.exit_status.to_i != 0
|
964
|
+
|
965
|
+
monitoring_info = utility_cmd.stdout.strip
|
966
|
+
monitoring_info =~ /^#{service_name}: up/
|
967
|
+
end
|
968
|
+
end
|
896
969
|
end
|
data/lib/inspec/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inspec-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chef InSpec Team
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: chef-telemetry
|
@@ -520,6 +520,7 @@ files:
|
|
520
520
|
- lib/inspec/resources/cron.rb
|
521
521
|
- lib/inspec/resources/crontab.rb
|
522
522
|
- lib/inspec/resources/csv.rb
|
523
|
+
- lib/inspec/resources/default_gateway.rb
|
523
524
|
- lib/inspec/resources/dh_params.rb
|
524
525
|
- lib/inspec/resources/directory.rb
|
525
526
|
- lib/inspec/resources/docker.rb
|
@@ -566,6 +567,7 @@ files:
|
|
566
567
|
- lib/inspec/resources/ksh.rb
|
567
568
|
- lib/inspec/resources/launchd_service.rb
|
568
569
|
- lib/inspec/resources/limits_conf.rb
|
570
|
+
- lib/inspec/resources/linux_audit_system.rb
|
569
571
|
- lib/inspec/resources/linux_kernel_parameter.rb
|
570
572
|
- lib/inspec/resources/login_defs.rb
|
571
573
|
- lib/inspec/resources/lxc.rb
|