inspec 0.14.8 → 0.15.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/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
data/lib/resources/bond.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
require 'resources/file'
|
6
6
|
|
7
7
|
module Inspec::Resources
|
8
|
-
class Bond <
|
8
|
+
class Bond < FileResource
|
9
9
|
name 'bond'
|
10
10
|
desc 'Use the bond InSpec audit resource to test a logical, bonded network interface (i.e. "two or more network interfaces aggregated into a single, logical network interface"). On Linux platforms, any value in the /proc/net/bonding directory may be tested.'
|
11
11
|
example "
|
data/lib/resources/bridge.rb
CHANGED
@@ -8,114 +8,116 @@
|
|
8
8
|
# it { should have_interface 'eth0' }
|
9
9
|
# end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
module Inspec::Resources
|
12
|
+
class Bridge < Inspec.resource(1)
|
13
|
+
name 'bridge'
|
14
|
+
desc 'Use the bridge InSpec audit resource to test basic network bridge properties, such as name, if an interface is defined, and the associations for any defined interface.'
|
15
|
+
example "
|
16
|
+
describe bridge 'br0' do
|
17
|
+
it { should exist }
|
18
|
+
it { should have_interface 'eth0' }
|
19
|
+
end
|
20
|
+
"
|
21
|
+
|
22
|
+
def initialize(bridge_name)
|
23
|
+
@bridge_name = bridge_name
|
24
|
+
|
25
|
+
@bridge_provider = nil
|
26
|
+
if inspec.os.linux?
|
27
|
+
@bridge_provider = LinuxBridge.new(inspec)
|
28
|
+
elsif inspec.os.windows?
|
29
|
+
@bridge_provider = WindowsBridge.new(inspec)
|
30
|
+
else
|
31
|
+
return skip_resource 'The `bridge` resource is not supported on your OS yet.'
|
32
|
+
end
|
18
33
|
end
|
19
|
-
"
|
20
|
-
|
21
|
-
def initialize(bridge_name)
|
22
|
-
@bridge_name = bridge_name
|
23
|
-
|
24
|
-
@bridge_provider = nil
|
25
|
-
if inspec.os.linux?
|
26
|
-
@bridge_provider = LinuxBridge.new(inspec)
|
27
|
-
elsif inspec.os.windows?
|
28
|
-
@bridge_provider = WindowsBridge.new(inspec)
|
29
|
-
else
|
30
|
-
return skip_resource 'The `bridge` resource is not supported on your OS yet.'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def exists?
|
35
|
-
!bridge_info.nil? && !bridge_info[:name].nil?
|
36
|
-
end
|
37
|
-
|
38
|
-
def has_interface?(interface)
|
39
|
-
return skip_resource 'The `bridge` resource does not provide interface detection for Windows yet' if inspec.os.windows?
|
40
|
-
bridge_info.nil? ? false : bridge_info[:interfaces].include?(interface)
|
41
|
-
end
|
42
34
|
|
43
|
-
|
44
|
-
|
45
|
-
|
35
|
+
def exists?
|
36
|
+
!bridge_info.nil? && !bridge_info[:name].nil?
|
37
|
+
end
|
46
38
|
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
def has_interface?(interface)
|
40
|
+
return skip_resource 'The `bridge` resource does not provide interface detection for Windows yet' if inspec.os.windows?
|
41
|
+
bridge_info.nil? ? false : bridge_info[:interfaces].include?(interface)
|
42
|
+
end
|
50
43
|
|
51
|
-
|
44
|
+
def interfaces
|
45
|
+
bridge_info.nil? ? nil : bridge_info[:interfaces]
|
46
|
+
end
|
52
47
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
end
|
48
|
+
def to_s
|
49
|
+
"Bridge #{@bridge_name}"
|
50
|
+
end
|
58
51
|
|
59
|
-
|
60
|
-
attr_reader :inspec
|
61
|
-
def initialize(inspec)
|
62
|
-
@inspec = inspec
|
63
|
-
end
|
64
|
-
end
|
52
|
+
private
|
65
53
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
# @see http://unix.stackexchange.com/questions/40560/how-to-know-if-a-network-interface-is-tap-tun-bridge-or-physical
|
71
|
-
class LinuxBridge < BridgeDetection
|
72
|
-
def bridge_info(bridge_name)
|
73
|
-
# read bridge information
|
74
|
-
bridge = inspec.file("/sys/class/net/#{bridge_name}/bridge").directory?
|
75
|
-
return nil unless bridge
|
76
|
-
|
77
|
-
# load interface names
|
78
|
-
interfaces = inspec.command("ls -1 /sys/class/net/#{bridge_name}/brif/")
|
79
|
-
interfaces = interfaces.stdout.chomp.split("\n")
|
80
|
-
{
|
81
|
-
name: bridge_name,
|
82
|
-
interfaces: interfaces,
|
83
|
-
}
|
54
|
+
def bridge_info
|
55
|
+
return @cache if defined?(@cache)
|
56
|
+
@cache = @bridge_provider.bridge_info(@bridge_name) if !@bridge_provider.nil?
|
57
|
+
end
|
84
58
|
end
|
85
|
-
end
|
86
59
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
# RegKeys: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}
|
92
|
-
class WindowsBridge < BridgeDetection
|
93
|
-
def bridge_info(bridge_name)
|
94
|
-
# find all bridge adapters
|
95
|
-
cmd = inspec.command('Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json')
|
96
|
-
|
97
|
-
# filter network interface
|
98
|
-
begin
|
99
|
-
bridges = JSON.parse(cmd.stdout)
|
100
|
-
rescue JSON::ParserError => _e
|
101
|
-
return nil
|
60
|
+
class BridgeDetection
|
61
|
+
attr_reader :inspec
|
62
|
+
def initialize(inspec)
|
63
|
+
@inspec = inspec
|
102
64
|
end
|
65
|
+
end
|
103
66
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
67
|
+
# Linux Bridge
|
68
|
+
# If /sys/class/net/{interface}/bridge exists then it must be a bridge
|
69
|
+
# /sys/class/net/{interface}/brif contains the network interfaces
|
70
|
+
# @see http://www.tldp.org/HOWTO/BRIDGE-STP-HOWTO/set-up-the-bridge.html
|
71
|
+
# @see http://unix.stackexchange.com/questions/40560/how-to-know-if-a-network-interface-is-tap-tun-bridge-or-physical
|
72
|
+
class LinuxBridge < BridgeDetection
|
73
|
+
def bridge_info(bridge_name)
|
74
|
+
# read bridge information
|
75
|
+
bridge = inspec.file("/sys/class/net/#{bridge_name}/bridge").directory?
|
76
|
+
return nil unless bridge
|
77
|
+
|
78
|
+
# load interface names
|
79
|
+
interfaces = inspec.command("ls -1 /sys/class/net/#{bridge_name}/brif/")
|
80
|
+
interfaces = interfaces.stdout.chomp.split("\n")
|
81
|
+
{
|
82
|
+
name: bridge_name,
|
83
|
+
interfaces: interfaces,
|
113
84
|
}
|
114
|
-
adapter_collection.push(info) if info[:name].casecmp(bridge_name) == 0
|
115
85
|
end
|
86
|
+
end
|
116
87
|
|
117
|
-
|
118
|
-
|
119
|
-
|
88
|
+
# Windows Bridge
|
89
|
+
# select netadapter by adapter binding for windows
|
90
|
+
# Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter
|
91
|
+
# @see https://technet.microsoft.com/en-us/library/jj130921(v=wps.630).aspx
|
92
|
+
# RegKeys: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}
|
93
|
+
class WindowsBridge < BridgeDetection
|
94
|
+
def bridge_info(bridge_name)
|
95
|
+
# find all bridge adapters
|
96
|
+
cmd = inspec.command('Get-NetAdapterBinding -ComponentID ms_bridge | Get-NetAdapter | Select-Object -Property Name, InterfaceDescription | ConvertTo-Json')
|
97
|
+
|
98
|
+
# filter network interface
|
99
|
+
begin
|
100
|
+
bridges = JSON.parse(cmd.stdout)
|
101
|
+
rescue JSON::ParserError => _e
|
102
|
+
return nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# ensure we have an array of groups
|
106
|
+
bridges = [bridges] if !bridges.is_a?(Array)
|
107
|
+
|
108
|
+
# select the requested interface
|
109
|
+
bridges = bridges.each_with_object([]) do |adapter, adapter_collection|
|
110
|
+
# map object
|
111
|
+
info = {
|
112
|
+
name: adapter['Name'],
|
113
|
+
interfaces: nil,
|
114
|
+
}
|
115
|
+
adapter_collection.push(info) if info[:name].casecmp(bridge_name) == 0
|
116
|
+
end
|
117
|
+
|
118
|
+
return nil if bridges.size == 0
|
119
|
+
warn "[Possible Error] detected multiple bridges interfaces with the name #{bridge_name}" if bridges.size > 1
|
120
|
+
bridges[0]
|
121
|
+
end
|
120
122
|
end
|
121
123
|
end
|
data/lib/resources/command.rb
CHANGED
@@ -4,58 +4,60 @@
|
|
4
4
|
# author: Christoph Hartmann
|
5
5
|
# license: All rights reserved
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
7
|
+
module Inspec::Resources
|
8
|
+
class Cmd < Inspec.resource(1)
|
9
|
+
name 'command'
|
10
|
+
desc 'Use the command InSpec audit resource to test an arbitrary command that is run on the system.'
|
11
|
+
example "
|
12
|
+
describe command('ls -al /') do
|
13
|
+
it { should exist }
|
14
|
+
its(:stdout) { should match /bin/ }
|
15
|
+
its('stderr') { should eq '' }
|
16
|
+
its(:exit_status) { should eq 0 }
|
17
|
+
end
|
18
|
+
"
|
19
|
+
|
20
|
+
attr_reader :command
|
21
|
+
|
22
|
+
def initialize(cmd)
|
23
|
+
@command = cmd
|
16
24
|
end
|
17
|
-
"
|
18
25
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
@command = cmd
|
23
|
-
end
|
24
|
-
|
25
|
-
def result
|
26
|
-
@result ||= inspec.backend.run_command(@command)
|
27
|
-
end
|
26
|
+
def result
|
27
|
+
@result ||= inspec.backend.run_command(@command)
|
28
|
+
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
30
|
+
def stdout
|
31
|
+
result.stdout
|
32
|
+
end
|
32
33
|
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
def stderr
|
35
|
+
result.stderr
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def exit_status
|
39
|
+
result.exit_status.to_i
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
42
|
+
def exist?
|
43
|
+
# silent for mock resources
|
44
|
+
return false if inspec.os[:family].to_s == 'unknown'
|
45
|
+
|
46
|
+
if inspec.os.linux?
|
47
|
+
res = inspec.backend.run_command("bash -c 'type \"#{@command}\"'")
|
48
|
+
elsif inspec.os.windows?
|
49
|
+
res = inspec.backend.run_command("where.exe \"#{@command}\"")
|
50
|
+
elsif inspec.os.unix?
|
51
|
+
res = inspec.backend.run_command("type \"#{@command}\"")
|
52
|
+
else
|
53
|
+
warn "`command(#{@command}).exist?` is not suported on your OS: #{inspec.os[:family]}"
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
res.exit_status.to_i == 0
|
54
57
|
end
|
55
|
-
res.exit_status.to_i == 0
|
56
|
-
end
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
def to_s
|
60
|
+
"Command #{@command}"
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
data/lib/resources/csv.rb
CHANGED
@@ -5,29 +5,31 @@
|
|
5
5
|
# Parses a csv document
|
6
6
|
# This implementation was inspired by a blog post
|
7
7
|
# @see http://technicalpickles.com/posts/parsing-csv-with-ruby
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
8
|
+
module Inspec::Resources
|
9
|
+
class CsvConfig < JsonConfig
|
10
|
+
name 'csv'
|
11
|
+
desc 'Use the csv InSpec audit resource to test configuration data in a CSV file.'
|
12
|
+
example "
|
13
|
+
describe csv('example.csv') do
|
14
|
+
its('name') { should eq(['John', 'Alice']) }
|
15
|
+
end
|
16
|
+
"
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
# override file load and parse hash from csv
|
19
|
+
def parse(content)
|
20
|
+
require 'csv'
|
21
|
+
# convert empty field to nil
|
22
|
+
CSV::Converters[:blank_to_nil] = lambda do |field|
|
23
|
+
field && field.empty? ? nil : field
|
24
|
+
end
|
25
|
+
# implicit conversion of values
|
26
|
+
csv = CSV.new(content, headers: true, converters: [:all, :blank_to_nil])
|
27
|
+
# convert to hash
|
28
|
+
csv.to_a.map(&:to_hash)
|
23
29
|
end
|
24
|
-
# implicit conversion of values
|
25
|
-
csv = CSV.new(content, headers: true, converters: [:all, :blank_to_nil])
|
26
|
-
# convert to hash
|
27
|
-
csv.to_a.map(&:to_hash)
|
28
|
-
end
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
def to_s
|
32
|
+
"Csv #{@path}"
|
33
|
+
end
|
32
34
|
end
|
33
35
|
end
|
data/lib/resources/directory.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
require 'resources/file'
|
6
6
|
|
7
7
|
module Inspec::Resources
|
8
|
-
class Directory <
|
8
|
+
class Directory < FileResource
|
9
9
|
name 'directory'
|
10
10
|
desc 'Use the directory InSpec audit resource to test if the file type is a directory. This is equivalent to using the file InSpec audit resource and the be_directory matcher, but provides a simpler and more direct way to test directories. All of the matchers available to file may be used with directory.'
|
11
11
|
example "
|
data/lib/resources/etc_group.rb
CHANGED
@@ -24,135 +24,137 @@
|
|
24
24
|
require 'utils/convert'
|
25
25
|
require 'utils/parser'
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
module Inspec::Resources
|
28
|
+
class EtcGroup < Inspec.resource(1)
|
29
|
+
include Converter
|
30
|
+
include CommentParser
|
31
|
+
|
32
|
+
name 'etc_group'
|
33
|
+
desc 'Use the etc_group InSpec audit resource to test groups that are defined on Linux and UNIX platforms. The /etc/group file stores details about each group---group name, password, group identifier, along with a comma-separate list of users that belong to the group.'
|
34
|
+
example "
|
35
|
+
describe etc_group do
|
36
|
+
its('gids') { should_not contain_duplicates }
|
37
|
+
its('groups') { should include 'my_user' }
|
38
|
+
its('users') { should include 'my_user' }
|
39
|
+
end
|
40
|
+
"
|
41
|
+
|
42
|
+
attr_accessor :gid, :entries
|
43
|
+
def initialize(path = nil)
|
44
|
+
@path = path || '/etc/group'
|
45
|
+
@entries = parse_group(@path)
|
46
|
+
|
47
|
+
# skip resource if it is not supported on current OS
|
48
|
+
return skip_resource 'The `etc_group` resource is not supported on your OS.' \
|
49
|
+
unless inspec.os.unix?
|
38
50
|
end
|
39
|
-
"
|
40
51
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@entries = parse_group(@path)
|
45
|
-
|
46
|
-
# skip resource if it is not supported on current OS
|
47
|
-
return skip_resource 'The `etc_group` resource is not supported on your OS.' \
|
48
|
-
unless inspec.os.unix?
|
49
|
-
end
|
50
|
-
|
51
|
-
def groups(filter = nil)
|
52
|
-
entries = filter || @entries
|
53
|
-
entries.map { |x| x['name'] } if !entries.nil?
|
54
|
-
end
|
55
|
-
|
56
|
-
def gids(filter = nil)
|
57
|
-
entries = filter || @entries
|
58
|
-
entries.map { |x| x['gid'] } if !entries.nil?
|
59
|
-
end
|
60
|
-
|
61
|
-
def users(filter = nil)
|
62
|
-
entries = filter || @entries
|
63
|
-
return nil if entries.nil?
|
64
|
-
# filter the user entry
|
65
|
-
res = entries.map { |x|
|
66
|
-
x['members'].split(',') if !x.nil? && !x['members'].nil?
|
67
|
-
}.flatten
|
68
|
-
# filter nil elements
|
69
|
-
res.reject { |x| x.nil? || x.empty? }
|
70
|
-
end
|
71
|
-
|
72
|
-
def where(conditions = {})
|
73
|
-
return if conditions.empty?
|
74
|
-
fields = {
|
75
|
-
name: 'name',
|
76
|
-
group_name: 'name',
|
77
|
-
password: 'password',
|
78
|
-
gid: 'gid',
|
79
|
-
group_id: 'gid',
|
80
|
-
users: 'members',
|
81
|
-
members: 'members',
|
82
|
-
}
|
83
|
-
res = entries
|
84
|
-
|
85
|
-
conditions.each do |k, v|
|
86
|
-
idx = fields[k.to_sym]
|
87
|
-
next if idx.nil?
|
88
|
-
res = res.select { |x| x[idx] == v.to_s }
|
52
|
+
def groups(filter = nil)
|
53
|
+
entries = filter || @entries
|
54
|
+
entries.map { |x| x['name'] } if !entries.nil?
|
89
55
|
end
|
90
56
|
|
91
|
-
|
92
|
-
|
57
|
+
def gids(filter = nil)
|
58
|
+
entries = filter || @entries
|
59
|
+
entries.map { |x| x['gid'] } if !entries.nil?
|
60
|
+
end
|
93
61
|
|
94
|
-
|
95
|
-
|
96
|
-
|
62
|
+
def users(filter = nil)
|
63
|
+
entries = filter || @entries
|
64
|
+
return nil if entries.nil?
|
65
|
+
# filter the user entry
|
66
|
+
res = entries.map { |x|
|
67
|
+
x['members'].split(',') if !x.nil? && !x['members'].nil?
|
68
|
+
}.flatten
|
69
|
+
# filter nil elements
|
70
|
+
res.reject { |x| x.nil? || x.empty? }
|
71
|
+
end
|
97
72
|
|
98
|
-
|
73
|
+
def where(conditions = {})
|
74
|
+
return if conditions.empty?
|
75
|
+
fields = {
|
76
|
+
name: 'name',
|
77
|
+
group_name: 'name',
|
78
|
+
password: 'password',
|
79
|
+
gid: 'gid',
|
80
|
+
group_id: 'gid',
|
81
|
+
users: 'members',
|
82
|
+
members: 'members',
|
83
|
+
}
|
84
|
+
res = entries
|
85
|
+
|
86
|
+
conditions.each do |k, v|
|
87
|
+
idx = fields[k.to_sym]
|
88
|
+
next if idx.nil?
|
89
|
+
res = res.select { |x| x[idx] == v.to_s }
|
90
|
+
end
|
91
|
+
|
92
|
+
EtcGroupView.new(self, res)
|
93
|
+
end
|
99
94
|
|
100
|
-
|
101
|
-
|
102
|
-
if @content.nil?
|
103
|
-
skip_resource "Can't access group file in #{path}"
|
104
|
-
return []
|
95
|
+
def to_s
|
96
|
+
'/etc/group'
|
105
97
|
end
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def parse_group(path)
|
102
|
+
@content = inspec.file(path).content
|
103
|
+
if @content.nil?
|
104
|
+
skip_resource "Can't access group file in #{path}"
|
105
|
+
return []
|
106
|
+
end
|
107
|
+
# iterate over each line and filter comments
|
108
|
+
@content.split("\n").each_with_object([]) do |line, lines|
|
109
|
+
grp_info = parse_group_line(line)
|
110
|
+
lines.push(grp_info) if !grp_info.nil? && grp_info.size > 0
|
111
|
+
end
|
110
112
|
end
|
111
|
-
end
|
112
113
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
114
|
+
def parse_group_line(line)
|
115
|
+
opts = {
|
116
|
+
comment_char: '#',
|
117
|
+
standalone_comments: false,
|
118
|
+
}
|
119
|
+
line, _idx_nl = parse_comment_line(line, opts)
|
120
|
+
x = line.split(':')
|
121
|
+
# abort if we have an empty or comment line
|
122
|
+
return nil if x.size == 0
|
123
|
+
# map data
|
124
|
+
{
|
125
|
+
'name' => x.at(0), # Name of the group.
|
126
|
+
'password' => x.at(1), # Group's encrypted password.
|
127
|
+
'gid' => convert_to_i(x.at(2)), # The group's decimal ID.
|
128
|
+
'members' => x.at(3), # Group members.
|
129
|
+
}
|
130
|
+
end
|
129
131
|
end
|
130
|
-
end
|
131
132
|
|
132
|
-
# object that hold a specifc view on etc group
|
133
|
-
class EtcGroupView
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
133
|
+
# object that hold a specifc view on etc group
|
134
|
+
class EtcGroupView
|
135
|
+
def initialize(parent, filter)
|
136
|
+
@parent = parent
|
137
|
+
@filter = filter
|
138
|
+
end
|
138
139
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
140
|
+
# returns the group object
|
141
|
+
def entries
|
142
|
+
@filter
|
143
|
+
end
|
143
144
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
145
|
+
# only returns group name
|
146
|
+
def groups
|
147
|
+
@parent.groups(@filter)
|
148
|
+
end
|
148
149
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
150
|
+
# only return gids
|
151
|
+
def gids
|
152
|
+
@parent.gids(@filter)
|
153
|
+
end
|
153
154
|
|
154
|
-
|
155
|
-
|
156
|
-
|
155
|
+
# only returns users
|
156
|
+
def users
|
157
|
+
@parent.users(@filter)
|
158
|
+
end
|
157
159
|
end
|
158
160
|
end
|