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/file.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# license: All rights reserved
|
6
6
|
|
7
7
|
module Inspec::Resources
|
8
|
-
class
|
8
|
+
class FileResource < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
|
9
9
|
name 'file'
|
10
10
|
desc 'Use the file InSpec audit resource to test all system file types, including files, directories, symbolic links, named pipes, sockets, character devices, block devices, and doors.'
|
11
11
|
example "
|
data/lib/resources/gem.rb
CHANGED
@@ -2,47 +2,49 @@
|
|
2
2
|
# author: Christoph Hartmann
|
3
3
|
# author: Dominik Richter
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
module Inspec::Resources
|
6
|
+
class GemPackage < Inspec.resource(1)
|
7
|
+
name 'gem'
|
8
|
+
desc 'Use the gem InSpec audit resource to test if a global gem package is installed.'
|
9
|
+
example "
|
10
|
+
describe gem('rubocop') do
|
11
|
+
it { should be_installed }
|
12
|
+
end
|
13
|
+
"
|
14
|
+
|
15
|
+
def initialize(package_name)
|
16
|
+
@package_name = package_name
|
11
17
|
end
|
12
|
-
"
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
@info[:name] = params[1]
|
33
|
-
@info[:version] = versions[0]
|
34
|
-
@info
|
35
|
-
end
|
19
|
+
def info
|
20
|
+
return @info if defined?(@info)
|
21
|
+
|
22
|
+
cmd = inspec.command("gem list --local -a -q \^#{@package_name}\$")
|
23
|
+
@info = {
|
24
|
+
installed: cmd.exit_status == 0,
|
25
|
+
type: 'gem',
|
26
|
+
}
|
27
|
+
return @info unless @info[:installed]
|
28
|
+
|
29
|
+
# extract package name and version
|
30
|
+
# parses data like winrm (1.3.4, 1.3.3)
|
31
|
+
params = /^\s*([^\(]*?)\s*\((.*?)\)\s*$/.match(cmd.stdout.chomp)
|
32
|
+
versions = params[2].split(',')
|
33
|
+
@info[:name] = params[1]
|
34
|
+
@info[:version] = versions[0]
|
35
|
+
@info
|
36
|
+
end
|
36
37
|
|
37
|
-
|
38
|
-
|
39
|
-
|
38
|
+
def installed?
|
39
|
+
info[:installed] == true
|
40
|
+
end
|
40
41
|
|
41
|
-
|
42
|
-
|
43
|
-
|
42
|
+
def version
|
43
|
+
info[:version]
|
44
|
+
end
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
def to_s
|
47
|
+
"gem package #{@package_name}"
|
48
|
+
end
|
47
49
|
end
|
48
50
|
end
|
data/lib/resources/group.rb
CHANGED
@@ -13,123 +13,125 @@
|
|
13
13
|
# it { should have_gid 0 }
|
14
14
|
# end
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
module Inspec::Resources
|
17
|
+
class Group < Inspec.resource(1)
|
18
|
+
name 'group'
|
19
|
+
desc 'Use the group InSpec audit resource to test groups on the system.'
|
20
|
+
example "
|
21
|
+
describe group('root') do
|
22
|
+
it { should exist }
|
23
|
+
its('gid') { should eq 0 }
|
24
|
+
end
|
25
|
+
"
|
26
|
+
|
27
|
+
def initialize(groupname, domain = nil)
|
28
|
+
@group = groupname.downcase
|
29
|
+
@domain = domain
|
30
|
+
@domain = @domain.downcase unless @domain.nil?
|
31
|
+
|
32
|
+
@cache = nil
|
33
|
+
|
34
|
+
# select group manager
|
35
|
+
@group_provider = nil
|
36
|
+
if inspec.os.unix?
|
37
|
+
@group_provider = UnixGroup.new(inspec)
|
38
|
+
elsif inspec.os.windows?
|
39
|
+
@group_provider = WindowsGroup.new(inspec)
|
40
|
+
else
|
41
|
+
return skip_resource 'The `group` resource is not supported on your OS yet.'
|
42
|
+
end
|
23
43
|
end
|
24
|
-
"
|
25
|
-
|
26
|
-
def initialize(groupname, domain = nil)
|
27
|
-
@group = groupname.downcase
|
28
|
-
@domain = domain
|
29
|
-
@domain = @domain.downcase unless @domain.nil?
|
30
|
-
|
31
|
-
@cache = nil
|
32
|
-
|
33
|
-
# select group manager
|
34
|
-
@group_provider = nil
|
35
|
-
if inspec.os.unix?
|
36
|
-
@group_provider = UnixGroup.new(inspec)
|
37
|
-
elsif inspec.os.windows?
|
38
|
-
@group_provider = WindowsGroup.new(inspec)
|
39
|
-
else
|
40
|
-
return skip_resource 'The `group` resource is not supported on your OS yet.'
|
41
|
-
end
|
42
|
-
end
|
43
44
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
# verifies if a group exists
|
46
|
+
def exists?
|
47
|
+
# ensure that we found one group
|
48
|
+
!group_info.nil? && group_info.size > 0
|
49
|
+
end
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
def gid
|
52
|
+
return nil if group_info.nil? || group_info.size == 0
|
52
53
|
|
53
|
-
|
54
|
-
|
54
|
+
# the default case should be one group
|
55
|
+
return group_info[0][:gid] if group_info.size == 1
|
55
56
|
|
56
|
-
|
57
|
-
|
58
|
-
|
57
|
+
# return array if we got multiple gids
|
58
|
+
group_info.map { |grp| grp[:gid] }
|
59
|
+
end
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
# implements rspec has matcher, to be compatible with serverspec
|
62
|
+
def has_gid?(compare_gid)
|
63
|
+
gid == compare_gid
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
-
|
66
|
+
def local
|
67
|
+
return nil if group_info.nil? || group_info.size == 0
|
67
68
|
|
68
|
-
|
69
|
-
|
69
|
+
# the default case should be one group
|
70
|
+
return group_info[0][:local] if group_info.size == 1
|
70
71
|
|
71
|
-
|
72
|
-
|
73
|
-
|
72
|
+
# return array if we got multiple gids
|
73
|
+
group_info.map { |grp| grp[:local] }
|
74
|
+
end
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
76
|
+
def to_s
|
77
|
+
"Group #{@group}"
|
78
|
+
end
|
78
79
|
|
79
|
-
|
80
|
+
private
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
82
|
+
def group_info
|
83
|
+
return @cache if !@cache.nil?
|
84
|
+
@cache = @group_provider.group_info(@group, @domain) if !@group_provider.nil?
|
85
|
+
end
|
84
86
|
end
|
85
|
-
end
|
86
87
|
|
87
|
-
class GroupInfo
|
88
|
-
|
89
|
-
|
90
|
-
|
88
|
+
class GroupInfo
|
89
|
+
attr_reader :inspec
|
90
|
+
def initialize(inspec)
|
91
|
+
@inspec = inspec
|
92
|
+
end
|
91
93
|
end
|
92
|
-
end
|
93
94
|
|
94
|
-
# implements generic unix groups via /etc/group
|
95
|
-
class UnixGroup < GroupInfo
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
95
|
+
# implements generic unix groups via /etc/group
|
96
|
+
class UnixGroup < GroupInfo
|
97
|
+
def group_info(group, _domain = nil)
|
98
|
+
inspec.etc_group.where(name: group).entries.map { |grp|
|
99
|
+
{
|
100
|
+
name: grp['name'],
|
101
|
+
gid: grp['gid'],
|
102
|
+
}
|
101
103
|
}
|
102
|
-
}
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
class WindowsGroup < GroupInfo
|
107
|
-
def group_info(compare_group, compare_domain = nil)
|
108
|
-
cmd = inspec.command('Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json')
|
109
|
-
|
110
|
-
# cannot rely on exit code for now, successful command returns exit code 1
|
111
|
-
# return nil if cmd.exit_status != 0, try to parse json
|
112
|
-
begin
|
113
|
-
groups = JSON.parse(cmd.stdout)
|
114
|
-
rescue JSON::ParserError => _e
|
115
|
-
return nil
|
116
104
|
end
|
105
|
+
end
|
117
106
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
#
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
107
|
+
class WindowsGroup < GroupInfo
|
108
|
+
def group_info(compare_group, compare_domain = nil)
|
109
|
+
cmd = inspec.command('Get-WmiObject Win32_Group | Select-Object -Property Caption, Domain, Name, SID, LocalAccount | ConvertTo-Json')
|
110
|
+
|
111
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
112
|
+
# return nil if cmd.exit_status != 0, try to parse json
|
113
|
+
begin
|
114
|
+
groups = JSON.parse(cmd.stdout)
|
115
|
+
rescue JSON::ParserError => _e
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
# ensure we have an array of groups
|
120
|
+
groups = [groups] if !groups.is_a?(Array)
|
121
|
+
|
122
|
+
# reduce list
|
123
|
+
groups.each_with_object([]) do |grp, grp_collection|
|
124
|
+
# map object
|
125
|
+
grp_info = {
|
126
|
+
name: grp['Name'],
|
127
|
+
domain: grp['Domain'],
|
128
|
+
caption: grp['Caption'],
|
129
|
+
gid: nil,
|
130
|
+
sid: grp['SID'],
|
131
|
+
local: grp['LocalAccount'],
|
132
|
+
}
|
133
|
+
return grp_collection.push(grp_info) if grp_info[:name].casecmp(compare_group) == 0 && (compare_domain.nil? || grp_info[:domain].casecmp(compare_domain) == 0)
|
134
|
+
end
|
133
135
|
end
|
134
136
|
end
|
135
137
|
end
|
data/lib/resources/host.rb
CHANGED
@@ -24,126 +24,128 @@
|
|
24
24
|
# it { should be_resolvable.by('dns') }
|
25
25
|
# end
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
27
|
+
module Inspec::Resources
|
28
|
+
class Host < Inspec.resource(1)
|
29
|
+
name 'host'
|
30
|
+
desc 'Use the host InSpec audit resource to test the name used to refer to a specific host and its availability, including the Internet protocols and ports over which that host name should be available.'
|
31
|
+
example "
|
32
|
+
describe host('example.com') do
|
33
|
+
it { should be_reachable }
|
34
|
+
end
|
35
|
+
"
|
36
|
+
|
37
|
+
def initialize(hostname, params = {})
|
38
|
+
@hostname = hostname
|
39
|
+
@port = params[:port] || nil
|
40
|
+
@proto = params[:proto] || nil
|
41
|
+
|
42
|
+
@host_provider = nil
|
43
|
+
if inspec.os.linux?
|
44
|
+
@host_provider = LinuxHostProvider.new(inspec)
|
45
|
+
elsif inspec.os.windows?
|
46
|
+
@host_provider = WindowsHostProvider.new(inspec)
|
47
|
+
else
|
48
|
+
return skip_resource 'The `host` resource is not supported on your OS yet.'
|
49
|
+
end
|
33
50
|
end
|
34
|
-
"
|
35
|
-
|
36
|
-
def initialize(hostname, params = {})
|
37
|
-
@hostname = hostname
|
38
|
-
@port = params[:port] || nil
|
39
|
-
@proto = params[:proto] || nil
|
40
|
-
|
41
|
-
@host_provider = nil
|
42
|
-
if inspec.os.linux?
|
43
|
-
@host_provider = LinuxHostProvider.new(inspec)
|
44
|
-
elsif inspec.os.windows?
|
45
|
-
@host_provider = WindowsHostProvider.new(inspec)
|
46
|
-
else
|
47
|
-
return skip_resource 'The `host` resource is not supported on your OS yet.'
|
48
|
-
end
|
49
|
-
end
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
# if we get the IP adress, the host is resolvable
|
53
|
+
def resolvable?(type = nil)
|
54
|
+
warn "The `host` resource ignores #{type} parameters. Continue to resolve host." if !type.nil?
|
55
|
+
resolve.nil? || resolve.empty? ? false : true
|
56
|
+
end
|
56
57
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
def reachable?(port = nil, proto = nil, timeout = nil)
|
59
|
+
fail "Use `host` resource with host('#{@hostname}', port: #{port}, proto: '#{proto}') parameters." if !port.nil? || !proto.nil? || !timeout.nil?
|
60
|
+
ping.nil? ? false : ping
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
# returns all A records of the IP adress, will return an array
|
64
|
+
def ipaddress
|
65
|
+
resolve.nil? || resolve.empty? ? nil : resolve
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
def to_s
|
69
|
+
"Host #{@hostname}"
|
70
|
+
end
|
70
71
|
|
71
|
-
|
72
|
+
private
|
72
73
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
74
|
+
def ping
|
75
|
+
return @ping_cache if defined?(@ping_cache)
|
76
|
+
@ping_cache = @host_provider.ping(@hostname, @port, @proto) if !@host_provider.nil?
|
77
|
+
end
|
77
78
|
|
78
|
-
|
79
|
-
|
80
|
-
|
79
|
+
def resolve
|
80
|
+
return @ip_cache if defined?(@ip_cache)
|
81
|
+
@ip_cache = @host_provider.resolve(@hostname) if !@host_provider.nil?
|
82
|
+
end
|
81
83
|
end
|
82
|
-
end
|
83
84
|
|
84
|
-
class HostProvider
|
85
|
-
|
86
|
-
|
87
|
-
|
85
|
+
class HostProvider
|
86
|
+
attr_reader :inspec
|
87
|
+
def initialize(inspec)
|
88
|
+
@inspec = inspec
|
89
|
+
end
|
88
90
|
end
|
89
|
-
end
|
90
91
|
|
91
|
-
class LinuxHostProvider < HostProvider
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
92
|
+
class LinuxHostProvider < HostProvider
|
93
|
+
# ping is difficult to achieve, since we are not sure
|
94
|
+
def ping(hostname, _port = nil, _proto = nil)
|
95
|
+
# fall back to ping, but we can only test ICMP packages with ping
|
96
|
+
# therefore we have to skip the test, if we do not have everything on the node to run the test
|
97
|
+
ping = inspec.command("ping -w 1 -c 1 #{hostname}")
|
98
|
+
ping.exit_status.to_i != 0 ? false : true
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
101
|
+
def resolve(hostname)
|
102
|
+
# TODO: we rely on getent hosts for now, but it prefers to return IPv6, only then IPv4
|
103
|
+
cmd = inspec.command("getent hosts #{hostname}")
|
104
|
+
return nil if cmd.exit_status.to_i != 0
|
104
105
|
|
105
|
-
|
106
|
-
|
107
|
-
|
106
|
+
# extract ip adress
|
107
|
+
resolve = /^\s*(?<ip>\S+)\s+(.*)\s*$/.match(cmd.stdout.chomp)
|
108
|
+
[resolve[1]] if resolve
|
109
|
+
end
|
108
110
|
end
|
109
|
-
end
|
110
111
|
|
111
|
-
# Windows
|
112
|
-
# TODO: UDP is not supported yey, we need a custom ps1 script to add udp support
|
113
|
-
# @see http://blogs.technet.com/b/josebda/archive/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup.aspx
|
114
|
-
# @see http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/19/creating-a-port-scanner-with-windows-powershell.aspx
|
115
|
-
class WindowsHostProvider < HostProvider
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
112
|
+
# Windows
|
113
|
+
# TODO: UDP is not supported yey, we need a custom ps1 script to add udp support
|
114
|
+
# @see http://blogs.technet.com/b/josebda/archive/2015/04/18/windows-powershell-equivalents-for-common-networking-commands-ipconfig-ping-nslookup.aspx
|
115
|
+
# @see http://blogs.technet.com/b/heyscriptingguy/archive/2014/03/19/creating-a-port-scanner-with-windows-powershell.aspx
|
116
|
+
class WindowsHostProvider < HostProvider
|
117
|
+
def ping(hostname, port = nil, proto = nil)
|
118
|
+
# TODO: abort if we cannot run it via udp
|
119
|
+
return nil if proto == 'udp'
|
120
|
+
|
121
|
+
# ICMP: Test-NetConnection www.microsoft.com
|
122
|
+
# TCP and port: Test-NetConnection -ComputerName www.microsoft.com -RemotePort 80
|
123
|
+
request = "Test-NetConnection -ComputerName #{hostname}"
|
124
|
+
request += " -RemotePort #{port}" unless port.nil?
|
125
|
+
request += '| Select-Object -Property ComputerName, RemoteAddress, RemotePort, SourceAddress, PingSucceeded | ConvertTo-Json'
|
126
|
+
p request
|
127
|
+
request += '| Select-Object -Property ComputerName, PingSucceeded | ConvertTo-Json'
|
128
|
+
cmd = inspec.command(request)
|
129
|
+
|
130
|
+
begin
|
131
|
+
ping = JSON.parse(cmd.stdout)
|
132
|
+
rescue JSON::ParserError => _e
|
133
|
+
return nil
|
134
|
+
end
|
135
|
+
|
136
|
+
ping['PingSucceeded']
|
133
137
|
end
|
134
138
|
|
135
|
-
|
136
|
-
|
139
|
+
def resolve(hostname)
|
140
|
+
cmd = inspec.command("Resolve-DnsName –Type A #{hostname} | ConvertTo-Json")
|
141
|
+
begin
|
142
|
+
resolv = JSON.parse(cmd.stdout)
|
143
|
+
rescue JSON::ParserError => _e
|
144
|
+
return nil
|
145
|
+
end
|
137
146
|
|
138
|
-
|
139
|
-
|
140
|
-
begin
|
141
|
-
resolv = JSON.parse(cmd.stdout)
|
142
|
-
rescue JSON::ParserError => _e
|
143
|
-
return nil
|
147
|
+
resolv = [resolv] unless resolv.is_a?(Array)
|
148
|
+
resolv.map { |entry| entry['IPAddress'] }
|
144
149
|
end
|
145
|
-
|
146
|
-
resolv = [resolv] unless resolv.is_a?(Array)
|
147
|
-
resolv.map { |entry| entry['IPAddress'] }
|
148
150
|
end
|
149
151
|
end
|