inspec-core 5.12.2 → 5.14.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|