inspec 0.14.8 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -2
- data/bin/inspec +3 -4
- data/examples/inheritance/README.md +19 -0
- data/examples/inheritance/controls/example.rb +11 -0
- data/examples/inheritance/inspec.yml +10 -0
- data/lib/bundles/inspec-compliance/cli.rb +1 -4
- data/lib/bundles/inspec-supermarket/cli.rb +1 -4
- data/lib/inspec/dsl.rb +48 -55
- data/lib/inspec/profile.rb +6 -2
- data/lib/inspec/profile_context.rb +21 -8
- data/lib/inspec/runner.rb +17 -12
- data/lib/inspec/runner_rspec.rb +1 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache.rb +20 -18
- data/lib/resources/apache_conf.rb +92 -90
- data/lib/resources/apt.rb +92 -90
- data/lib/resources/audit_policy.rb +35 -33
- data/lib/resources/auditd_conf.rb +41 -39
- data/lib/resources/auditd_rules.rb +155 -153
- data/lib/resources/bond.rb +1 -1
- data/lib/resources/bridge.rb +97 -95
- data/lib/resources/command.rb +47 -45
- data/lib/resources/csv.rb +23 -21
- data/lib/resources/directory.rb +1 -1
- data/lib/resources/etc_group.rb +116 -114
- data/lib/resources/file.rb +1 -1
- data/lib/resources/gem.rb +39 -37
- data/lib/resources/group.rb +100 -98
- data/lib/resources/host.rb +103 -101
- data/lib/resources/inetd_conf.rb +42 -40
- data/lib/resources/ini.rb +15 -13
- data/lib/resources/interface.rb +106 -104
- data/lib/resources/iptables.rb +36 -34
- data/lib/resources/json.rb +64 -62
- data/lib/resources/kernel_module.rb +30 -28
- data/lib/resources/kernel_parameter.rb +44 -42
- data/lib/resources/limits_conf.rb +41 -39
- data/lib/resources/login_def.rb +38 -36
- data/lib/resources/mount.rb +43 -41
- data/lib/resources/mysql.rb +67 -65
- data/lib/resources/mysql_conf.rb +89 -87
- data/lib/resources/mysql_session.rb +46 -44
- data/lib/resources/npm.rb +35 -33
- data/lib/resources/ntp_conf.rb +44 -42
- data/lib/resources/oneget.rb +46 -44
- data/lib/resources/os.rb +22 -20
- data/lib/resources/os_env.rb +47 -45
- data/lib/resources/package.rb +213 -211
- data/lib/resources/parse_config.rb +59 -57
- data/lib/resources/passwd.rb +89 -87
- data/lib/resources/pip.rb +60 -58
- data/lib/resources/port.rb +352 -350
- data/lib/resources/postgres.rb +26 -24
- data/lib/resources/postgres_conf.rb +66 -64
- data/lib/resources/postgres_session.rb +47 -45
- data/lib/resources/processes.rb +56 -54
- data/lib/resources/registry_key.rb +150 -148
- data/lib/resources/script.rb +30 -28
- data/lib/resources/security_policy.rb +56 -54
- data/lib/resources/service.rb +638 -636
- data/lib/resources/shadow.rb +98 -96
- data/lib/resources/ssh_conf.rb +58 -56
- data/lib/resources/user.rb +363 -361
- data/lib/resources/windows_feature.rb +46 -44
- data/lib/resources/xinetd.rb +111 -109
- data/lib/resources/yaml.rb +16 -14
- data/lib/resources/yum.rb +107 -105
- data/lib/utils/base_cli.rb +18 -0
- data/test/helper.rb +2 -2
- data/test/unit/profile_context_test.rb +1 -1
- data/test/unit/resources/file_test.rb +1 -1
- data/test/unit/resources/mount_test.rb +1 -1
- metadata +5 -2
@@ -13,77 +13,79 @@
|
|
13
13
|
# }
|
14
14
|
# describe parse_config(audit, options ) do
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
module Inspec::Resources
|
17
|
+
class PConfig < Inspec.resource(1)
|
18
|
+
name 'parse_config'
|
19
|
+
desc 'Use the parse_config InSpec audit resource to test arbitrary configuration files.'
|
20
|
+
example "
|
21
|
+
output = command('some-command').stdout
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
describe parse_config(output, { data_config_option: value } ) do
|
24
|
+
its('setting') { should eq 1 }
|
25
|
+
end
|
26
|
+
"
|
26
27
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
28
|
+
def initialize(content = nil, useropts = nil)
|
29
|
+
@opts = {}
|
30
|
+
@opts = useropts.dup unless useropts.nil?
|
31
|
+
@files_contents = {}
|
32
|
+
@params = nil
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
@content = content
|
35
|
+
read_content if @content.nil?
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
def method_missing(name)
|
39
|
+
@params || read_content
|
40
|
+
@params[name.to_s]
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
43
|
+
def parse_file(conf_path)
|
44
|
+
@conf_path = conf_path
|
44
45
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
# read the file
|
47
|
+
if !inspec.file(conf_path).file?
|
48
|
+
return skip_resource "Can't find file \"#{conf_path}\""
|
49
|
+
end
|
50
|
+
@content = read_file(conf_path)
|
51
|
+
if @content.empty? && inspec.file(conf_path).size > 0
|
52
|
+
return skip_resource "Can't read file \"#{conf_path}\""
|
53
|
+
end
|
54
|
+
|
55
|
+
read_content
|
52
56
|
end
|
53
57
|
|
54
|
-
|
55
|
-
|
58
|
+
def read_file(path)
|
59
|
+
@files_contents[path] ||= inspec.file(path).content
|
60
|
+
end
|
56
61
|
|
57
|
-
|
58
|
-
|
59
|
-
|
62
|
+
def read_content
|
63
|
+
# parse the file
|
64
|
+
@params = SimpleConfig.new(@content, @opts).params
|
65
|
+
@content
|
66
|
+
end
|
60
67
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@content
|
68
|
+
def to_s
|
69
|
+
"Parse Config #{@conf_path}"
|
70
|
+
end
|
65
71
|
end
|
66
72
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
73
|
+
class PConfigFile < PConfig
|
74
|
+
name 'parse_config_file'
|
75
|
+
desc 'Use the parse_config_file InSpec audit resource to test arbitrary configuration files. It works identiacal to parse_config. Instead of using a command output, this resource works with files.'
|
76
|
+
example "
|
77
|
+
describe parse_config_file('/path/to/file') do
|
78
|
+
its('setting') { should eq 1 }
|
79
|
+
end
|
80
|
+
"
|
71
81
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
example "
|
76
|
-
describe parse_config_file('/path/to/file') do
|
77
|
-
its('setting') { should eq 1 }
|
82
|
+
def initialize(path, opts = nil)
|
83
|
+
super(nil, opts)
|
84
|
+
parse_file(path)
|
78
85
|
end
|
79
|
-
"
|
80
|
-
|
81
|
-
def initialize(path, opts = nil)
|
82
|
-
super(nil, opts)
|
83
|
-
parse_file(path)
|
84
|
-
end
|
85
86
|
|
86
|
-
|
87
|
-
|
87
|
+
def to_s
|
88
|
+
"Parse Config File #{@conf_path}"
|
89
|
+
end
|
88
90
|
end
|
89
91
|
end
|
data/lib/resources/passwd.rb
CHANGED
@@ -15,112 +15,114 @@
|
|
15
15
|
|
16
16
|
require 'utils/parser'
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
18
|
+
module Inspec::Resources
|
19
|
+
class Passwd < Inspec.resource(1)
|
20
|
+
name 'passwd'
|
21
|
+
desc 'Use the passwd InSpec audit resource to test the contents of /etc/passwd, which contains the following information for users that may log into the system and/or as users that own running processes.'
|
22
|
+
example "
|
23
|
+
describe passwd do
|
24
|
+
its('users') { should_not include 'forbidden_user' }
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
describe passwd.uids(0) do
|
28
|
+
its('users') { should cmp 'root' }
|
29
|
+
its('count') { should eq 1 }
|
30
|
+
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
describe passwd.shells(/nologin/) do
|
33
|
+
# find all users with a nologin shell
|
34
|
+
its('users') { should_not include 'my_login_user' }
|
35
|
+
end
|
36
|
+
"
|
37
|
+
|
38
|
+
include PasswdParser
|
39
|
+
|
40
|
+
attr_reader :uid
|
41
|
+
attr_reader :params
|
42
|
+
attr_reader :content
|
43
|
+
attr_reader :lines
|
44
|
+
|
45
|
+
def initialize(path = nil, opts = nil)
|
46
|
+
opts ||= {}
|
47
|
+
@path = path || '/etc/passwd'
|
48
|
+
@content = opts[:content] || inspec.file(@path).content
|
49
|
+
@lines = @content.to_s.split("\n")
|
50
|
+
@filters = opts[:filters] || ''
|
51
|
+
@params = parse_passwd(@content)
|
34
52
|
end
|
35
|
-
"
|
36
|
-
|
37
|
-
include PasswdParser
|
38
|
-
|
39
|
-
attr_reader :uid
|
40
|
-
attr_reader :params
|
41
|
-
attr_reader :content
|
42
|
-
attr_reader :lines
|
43
|
-
|
44
|
-
def initialize(path = nil, opts = nil)
|
45
|
-
opts ||= {}
|
46
|
-
@path = path || '/etc/passwd'
|
47
|
-
@content = opts[:content] || inspec.file(@path).content
|
48
|
-
@lines = @content.to_s.split("\n")
|
49
|
-
@filters = opts[:filters] || ''
|
50
|
-
@params = parse_passwd(@content)
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
54
|
+
def filter(hm = {})
|
55
|
+
return self if hm.nil? || hm.empty?
|
56
|
+
res = @params
|
57
|
+
filters = ''
|
58
|
+
hm.each do |attr, condition|
|
59
|
+
condition = condition.to_s if condition.is_a? Integer
|
60
|
+
filters += " #{attr} = #{condition.inspect}"
|
61
|
+
res = res.find_all do |line|
|
62
|
+
case line[attr.to_s]
|
63
|
+
when condition
|
64
|
+
true
|
65
|
+
else
|
66
|
+
false
|
67
|
+
end
|
66
68
|
end
|
67
69
|
end
|
70
|
+
content = res.map { |x| x.values.join(':') }.join("\n")
|
71
|
+
Passwd.new(@path, content: content, filters: @filters + filters)
|
68
72
|
end
|
69
|
-
content = res.map { |x| x.values.join(':') }.join("\n")
|
70
|
-
Passwd.new(@path, content: content, filters: @filters + filters)
|
71
|
-
end
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
def usernames
|
75
|
+
warn '[DEPRECATION] `passwd.usernames` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
|
76
|
+
users
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
def username
|
80
|
+
warn '[DEPRECATION] `passwd.user` is deprecated. Please use `passwd.users` instead. It will be removed in version 1.0.0.'
|
81
|
+
users[0]
|
82
|
+
end
|
82
83
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
def uid(x)
|
85
|
+
warn '[DEPRECATION] `passwd.uid(arg)` is deprecated. Please use `passwd.uids(arg)` instead. It will be removed in version 1.0.0.'
|
86
|
+
uids(x)
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
def users(name = nil)
|
90
|
+
name.nil? ? map_data('user') : filter(user: name)
|
91
|
+
end
|
91
92
|
|
92
|
-
|
93
|
-
|
94
|
-
|
93
|
+
def passwords(password = nil)
|
94
|
+
password.nil? ? map_data('password') : filter(password: password)
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
def uids(uid = nil)
|
98
|
+
uid.nil? ? map_data('uid') : filter(uid: uid)
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
def gids(gid = nil)
|
102
|
+
gid.nil? ? map_data('gid') : filter(gid: gid)
|
103
|
+
end
|
103
104
|
|
104
|
-
|
105
|
-
|
106
|
-
|
105
|
+
def homes(home = nil)
|
106
|
+
home.nil? ? map_data('home') : filter(home: home)
|
107
|
+
end
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
def shells(shell = nil)
|
110
|
+
shell.nil? ? map_data('shell') : filter(shell: shell)
|
111
|
+
end
|
111
112
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
113
|
+
def to_s
|
114
|
+
f = @filters.empty? ? '' : ' with'+@filters
|
115
|
+
"/etc/passwd#{f}"
|
116
|
+
end
|
116
117
|
|
117
|
-
|
118
|
-
|
119
|
-
|
118
|
+
def count
|
119
|
+
@params.length
|
120
|
+
end
|
120
121
|
|
121
|
-
|
122
|
+
private
|
122
123
|
|
123
|
-
|
124
|
-
|
124
|
+
def map_data(id)
|
125
|
+
@params.map { |x| x[id] }
|
126
|
+
end
|
125
127
|
end
|
126
128
|
end
|
data/lib/resources/pip.rb
CHANGED
@@ -7,75 +7,77 @@
|
|
7
7
|
# it { should be_installed }
|
8
8
|
# end
|
9
9
|
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
module Inspec::Resources
|
11
|
+
class PipPackage < Inspec.resource(1)
|
12
|
+
name 'pip'
|
13
|
+
desc 'Use the pip InSpec audit resource to test packages that are installed using the pip installer.'
|
14
|
+
example "
|
15
|
+
describe pip('Jinja2') do
|
16
|
+
it { should be_installed }
|
17
|
+
end
|
18
|
+
"
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
def initialize(package_name)
|
21
|
+
@package_name = package_name
|
22
|
+
end
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
def info
|
25
|
+
return @info if defined?(@info)
|
25
26
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
@info = {}
|
28
|
+
@info[:type] = 'pip'
|
29
|
+
cmd = inspec.command("#{pip_cmd} show #{@package_name}")
|
30
|
+
return @info if cmd.exit_status != 0
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
32
|
+
params = SimpleConfig.new(
|
33
|
+
cmd.stdout,
|
34
|
+
assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/,
|
35
|
+
multiple_values: false,
|
36
|
+
).params
|
37
|
+
@info[:name] = params['Name']
|
38
|
+
@info[:version] = params['Version']
|
39
|
+
@info[:installed] = true
|
40
|
+
@info
|
41
|
+
end
|
41
42
|
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
def installed?
|
44
|
+
info[:installed] == true
|
45
|
+
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
def version
|
48
|
+
info[:version]
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
52
|
-
|
51
|
+
def to_s
|
52
|
+
"Pip Package #{@package_name}"
|
53
|
+
end
|
53
54
|
|
54
|
-
|
55
|
+
private
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
57
|
+
def pip_cmd
|
58
|
+
# Pip is not on the default path for Windows, therefore we do some logic
|
59
|
+
# to find the binary on Windows
|
60
|
+
family = inspec.os[:family]
|
61
|
+
case family
|
62
|
+
when 'windows'
|
63
|
+
# we need to detect the pip command on Windows
|
64
|
+
cmd = inspec.command('New-Object -Type PSObject | Add-Member -MemberType NoteProperty -Name Pip -Value (Invoke-Command -ScriptBlock {where.exe pip}) -PassThru | Add-Member -MemberType NoteProperty -Name Python -Value (Invoke-Command -ScriptBlock {where.exe python}) -PassThru | ConvertTo-Json')
|
65
|
+
begin
|
66
|
+
paths = JSON.parse(cmd.stdout)
|
67
|
+
# use pip if it on system path
|
68
|
+
pipcmd = paths['Pip']
|
69
|
+
# calculate path on windows
|
70
|
+
if defined?(paths['Python']) && pipcmd.nil?
|
71
|
+
pipdir = paths['Python'].split('\\')
|
72
|
+
# remove python.exe
|
73
|
+
pipdir.pop
|
74
|
+
pipcmd = pipdir.push('Scripts').push('pip.exe').join('/')
|
75
|
+
end
|
76
|
+
rescue JSON::ParserError => _e
|
77
|
+
return nil
|
74
78
|
end
|
75
|
-
rescue JSON::ParserError => _e
|
76
|
-
return nil
|
77
79
|
end
|
80
|
+
pipcmd || 'pip'
|
78
81
|
end
|
79
|
-
pipcmd || 'pip'
|
80
82
|
end
|
81
83
|
end
|