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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -2
  3. data/bin/inspec +3 -4
  4. data/examples/inheritance/README.md +19 -0
  5. data/examples/inheritance/controls/example.rb +11 -0
  6. data/examples/inheritance/inspec.yml +10 -0
  7. data/lib/bundles/inspec-compliance/cli.rb +1 -4
  8. data/lib/bundles/inspec-supermarket/cli.rb +1 -4
  9. data/lib/inspec/dsl.rb +48 -55
  10. data/lib/inspec/profile.rb +6 -2
  11. data/lib/inspec/profile_context.rb +21 -8
  12. data/lib/inspec/runner.rb +17 -12
  13. data/lib/inspec/runner_rspec.rb +1 -0
  14. data/lib/inspec/version.rb +1 -1
  15. data/lib/resources/apache.rb +20 -18
  16. data/lib/resources/apache_conf.rb +92 -90
  17. data/lib/resources/apt.rb +92 -90
  18. data/lib/resources/audit_policy.rb +35 -33
  19. data/lib/resources/auditd_conf.rb +41 -39
  20. data/lib/resources/auditd_rules.rb +155 -153
  21. data/lib/resources/bond.rb +1 -1
  22. data/lib/resources/bridge.rb +97 -95
  23. data/lib/resources/command.rb +47 -45
  24. data/lib/resources/csv.rb +23 -21
  25. data/lib/resources/directory.rb +1 -1
  26. data/lib/resources/etc_group.rb +116 -114
  27. data/lib/resources/file.rb +1 -1
  28. data/lib/resources/gem.rb +39 -37
  29. data/lib/resources/group.rb +100 -98
  30. data/lib/resources/host.rb +103 -101
  31. data/lib/resources/inetd_conf.rb +42 -40
  32. data/lib/resources/ini.rb +15 -13
  33. data/lib/resources/interface.rb +106 -104
  34. data/lib/resources/iptables.rb +36 -34
  35. data/lib/resources/json.rb +64 -62
  36. data/lib/resources/kernel_module.rb +30 -28
  37. data/lib/resources/kernel_parameter.rb +44 -42
  38. data/lib/resources/limits_conf.rb +41 -39
  39. data/lib/resources/login_def.rb +38 -36
  40. data/lib/resources/mount.rb +43 -41
  41. data/lib/resources/mysql.rb +67 -65
  42. data/lib/resources/mysql_conf.rb +89 -87
  43. data/lib/resources/mysql_session.rb +46 -44
  44. data/lib/resources/npm.rb +35 -33
  45. data/lib/resources/ntp_conf.rb +44 -42
  46. data/lib/resources/oneget.rb +46 -44
  47. data/lib/resources/os.rb +22 -20
  48. data/lib/resources/os_env.rb +47 -45
  49. data/lib/resources/package.rb +213 -211
  50. data/lib/resources/parse_config.rb +59 -57
  51. data/lib/resources/passwd.rb +89 -87
  52. data/lib/resources/pip.rb +60 -58
  53. data/lib/resources/port.rb +352 -350
  54. data/lib/resources/postgres.rb +26 -24
  55. data/lib/resources/postgres_conf.rb +66 -64
  56. data/lib/resources/postgres_session.rb +47 -45
  57. data/lib/resources/processes.rb +56 -54
  58. data/lib/resources/registry_key.rb +150 -148
  59. data/lib/resources/script.rb +30 -28
  60. data/lib/resources/security_policy.rb +56 -54
  61. data/lib/resources/service.rb +638 -636
  62. data/lib/resources/shadow.rb +98 -96
  63. data/lib/resources/ssh_conf.rb +58 -56
  64. data/lib/resources/user.rb +363 -361
  65. data/lib/resources/windows_feature.rb +46 -44
  66. data/lib/resources/xinetd.rb +111 -109
  67. data/lib/resources/yaml.rb +16 -14
  68. data/lib/resources/yum.rb +107 -105
  69. data/lib/utils/base_cli.rb +18 -0
  70. data/test/helper.rb +2 -2
  71. data/test/unit/profile_context_test.rb +1 -1
  72. data/test/unit/resources/file_test.rb +1 -1
  73. data/test/unit/resources/mount_test.rb +1 -1
  74. metadata +5 -2
@@ -17,413 +17,415 @@ require 'utils/parser'
17
17
  #
18
18
  # TODO: currently we return local ip only
19
19
  # TODO: improve handling of same port on multiple interfaces
20
- class Port < Inspec.resource(1)
21
- name 'port'
22
- desc "Use the port InSpec audit resource to test basic port properties, such as port, process, if it's listening."
23
- example "
24
- describe port(80) do
25
- it { should be_listening }
26
- its('protocols') {should eq ['tcp']}
27
- end
28
- "
29
-
30
- def initialize(ip = nil, port) # rubocop:disable OptionalArguments
31
- @ip = ip
32
- @port = port
33
- @port_manager = nil
34
- @cache = nil
35
- os = inspec.os
36
- if os.linux?
37
- @port_manager = LinuxPorts.new(inspec)
38
- elsif %w{darwin aix}.include?(os[:family])
39
- # AIX: see http://www.ibm.com/developerworks/aix/library/au-lsof.html#resources
40
- # and https://www-01.ibm.com/marketing/iwm/iwm/web/reg/pick.do?source=aixbp
41
- # Darwin: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/lsof.8.html
42
- @port_manager = LsofPorts.new(inspec)
43
- elsif os.windows?
44
- @port_manager = WindowsPorts.new(inspec)
45
- elsif ['freebsd'].include?(os[:family])
46
- @port_manager = FreeBsdPorts.new(inspec)
47
- elsif os.solaris?
48
- @port_manager = SolarisPorts.new(inspec)
49
- else
50
- return skip_resource 'The `port` resource is not supported on your OS yet.'
20
+ module Inspec::Resources
21
+ class Port < Inspec.resource(1)
22
+ name 'port'
23
+ desc "Use the port InSpec audit resource to test basic port properties, such as port, process, if it's listening."
24
+ example "
25
+ describe port(80) do
26
+ it { should be_listening }
27
+ its('protocols') {should eq ['tcp']}
28
+ end
29
+ "
30
+
31
+ def initialize(ip = nil, port) # rubocop:disable OptionalArguments
32
+ @ip = ip
33
+ @port = port
34
+ @port_manager = nil
35
+ @cache = nil
36
+ os = inspec.os
37
+ if os.linux?
38
+ @port_manager = LinuxPorts.new(inspec)
39
+ elsif %w{darwin aix}.include?(os[:family])
40
+ # AIX: see http://www.ibm.com/developerworks/aix/library/au-lsof.html#resources
41
+ # and https://www-01.ibm.com/marketing/iwm/iwm/web/reg/pick.do?source=aixbp
42
+ # Darwin: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/lsof.8.html
43
+ @port_manager = LsofPorts.new(inspec)
44
+ elsif os.windows?
45
+ @port_manager = WindowsPorts.new(inspec)
46
+ elsif ['freebsd'].include?(os[:family])
47
+ @port_manager = FreeBsdPorts.new(inspec)
48
+ elsif os.solaris?
49
+ @port_manager = SolarisPorts.new(inspec)
50
+ else
51
+ return skip_resource 'The `port` resource is not supported on your OS yet.'
52
+ end
51
53
  end
52
- end
53
54
 
54
- def listening?(_protocol = nil, _local_address = nil)
55
- info.size > 0
56
- end
55
+ def listening?(_protocol = nil, _local_address = nil)
56
+ info.size > 0
57
+ end
57
58
 
58
- def protocols
59
- res = info.map { |x| x[:protocol] }.uniq.compact
60
- res.size > 0 ? res : nil
61
- end
59
+ def protocols
60
+ res = info.map { |x| x[:protocol] }.uniq.compact
61
+ res.size > 0 ? res : nil
62
+ end
62
63
 
63
- def processes
64
- res = info.map { |x| x[:process] }.uniq.compact
65
- res.size > 0 ? res : nil
66
- end
64
+ def processes
65
+ res = info.map { |x| x[:process] }.uniq.compact
66
+ res.size > 0 ? res : nil
67
+ end
67
68
 
68
- def pids
69
- res = info.map { |x| x[:pid] }.uniq.compact
70
- res.size > 0 ? res : nil
71
- end
69
+ def pids
70
+ res = info.map { |x| x[:pid] }.uniq.compact
71
+ res.size > 0 ? res : nil
72
+ end
72
73
 
73
- def to_s
74
- "Port #{@port}"
75
- end
74
+ def to_s
75
+ "Port #{@port}"
76
+ end
76
77
 
77
- private
78
+ private
78
79
 
79
- def info
80
- return @cache if !@cache.nil?
81
- # abort if os detection has not worked
82
- return @cache = [] if @port_manager.nil?
83
- # query ports
84
- ports = @port_manager.info || []
85
- @cache = ports.select { |p| p[:port] == @port && (!@ip || p[:address] == @ip) }
80
+ def info
81
+ return @cache if !@cache.nil?
82
+ # abort if os detection has not worked
83
+ return @cache = [] if @port_manager.nil?
84
+ # query ports
85
+ ports = @port_manager.info || []
86
+ @cache = ports.select { |p| p[:port] == @port && (!@ip || p[:address] == @ip) }
87
+ end
86
88
  end
87
- end
88
89
 
89
- # implements an info method and returns all ip adresses and protocols for
90
- # each port
91
- # [{
92
- # port: 22,
93
- # address: '0.0.0.0'
94
- # protocol: 'tcp'
95
- # },
96
- # {
97
- # port: 22,
98
- # address: '::'
99
- # protocol: 'tcp6'
100
- # }]
101
- class PortsInfo
102
- attr_reader :inspec
103
- def initialize(inspec)
104
- @inspec = inspec
90
+ # implements an info method and returns all ip adresses and protocols for
91
+ # each port
92
+ # [{
93
+ # port: 22,
94
+ # address: '0.0.0.0'
95
+ # protocol: 'tcp'
96
+ # },
97
+ # {
98
+ # port: 22,
99
+ # address: '::'
100
+ # protocol: 'tcp6'
101
+ # }]
102
+ class PortsInfo
103
+ attr_reader :inspec
104
+ def initialize(inspec)
105
+ @inspec = inspec
106
+ end
105
107
  end
106
- end
107
108
 
108
- # TODO: Add UDP infromation Get-NetUDPEndpoint
109
- # TODO: currently Windows only supports tcp ports
110
- # TODO: Get-NetTCPConnection does not return PIDs
111
- # TODO: double-check output with 'netstat -ano'
112
- # @see https://connect.microsoft.com/PowerShell/feedback/details/1349420/get-nettcpconnection-does-not-show-processid
113
- class WindowsPorts < PortsInfo
114
- def info
115
- # get all port information
116
- cmd = inspec.command('Get-NetTCPConnection | Select-Object -Property State, Caption, Description, LocalAddress, LocalPort, RemoteAddress, RemotePort, DisplayName, Status | ConvertTo-Json')
117
-
118
- begin
119
- ports = JSON.parse(cmd.stdout)
120
- rescue JSON::ParserError => _e
121
- return nil
122
- end
109
+ # TODO: Add UDP infromation Get-NetUDPEndpoint
110
+ # TODO: currently Windows only supports tcp ports
111
+ # TODO: Get-NetTCPConnection does not return PIDs
112
+ # TODO: double-check output with 'netstat -ano'
113
+ # @see https://connect.microsoft.com/PowerShell/feedback/details/1349420/get-nettcpconnection-does-not-show-processid
114
+ class WindowsPorts < PortsInfo
115
+ def info
116
+ # get all port information
117
+ cmd = inspec.command('Get-NetTCPConnection | Select-Object -Property State, Caption, Description, LocalAddress, LocalPort, RemoteAddress, RemotePort, DisplayName, Status | ConvertTo-Json')
118
+
119
+ begin
120
+ ports = JSON.parse(cmd.stdout)
121
+ rescue JSON::ParserError => _e
122
+ return nil
123
+ end
123
124
 
124
- return nil if ports.nil?
125
+ return nil if ports.nil?
125
126
 
126
- ports.map { |x|
127
- {
128
- port: x['LocalPort'],
129
- address: x['LocalAddress'],
130
- protocol: 'tcp',
131
- process: nil,
132
- pid: nil,
127
+ ports.map { |x|
128
+ {
129
+ port: x['LocalPort'],
130
+ address: x['LocalAddress'],
131
+ protocol: 'tcp',
132
+ process: nil,
133
+ pid: nil,
134
+ }
133
135
  }
134
- }
136
+ end
135
137
  end
136
- end
137
-
138
- # extracts udp and tcp ports from the lsof command
139
- class LsofPorts < PortsInfo
140
- attr_reader :lsof
141
138
 
142
- def initialize(inspec, lsofpath = nil)
143
- @lsof = lsofpath || 'lsof'
144
- super(inspec)
145
- end
139
+ # extracts udp and tcp ports from the lsof command
140
+ class LsofPorts < PortsInfo
141
+ attr_reader :lsof
146
142
 
147
- def info
148
- ports = []
149
-
150
- # check that lsof is available, otherwise fail
151
- fail 'Please ensure `lsof` is available on the machine.' if !inspec.command(@lsof.to_s).exist?
152
-
153
- # -F p=pid, c=command, P=protocol name, t=type, n=internet addresses
154
- # see 'OUTPUT FOR OTHER PROGRAMS' in LSOF(8)
155
- lsof_cmd = inspec.command("#{@lsof} -nP -i -FpctPn")
156
- return nil if lsof_cmd.exit_status.to_i != 0
157
-
158
- # map to desired return struct
159
- lsof_parser(lsof_cmd).each do |process, port_ids|
160
- pid, cmd = process.split(':')
161
- port_ids.each do |port_str|
162
- # should not break on ipv6 addresses
163
- ipv, proto, port, host = port_str.split(':', 4)
164
- ports.push({ port: port.to_i,
165
- address: host,
166
- protocol: ipv == 'ipv6' ? proto + '6' : proto,
167
- process: cmd,
168
- pid: pid.to_i })
169
- end
143
+ def initialize(inspec, lsofpath = nil)
144
+ @lsof = lsofpath || 'lsof'
145
+ super(inspec)
170
146
  end
171
147
 
172
- ports
173
- end
174
-
175
- # rubocop:disable Metrics/CyclomaticComplexity
176
- # rubocop:disable Metrics/AbcSize
177
- def lsof_parser(lsof_cmd)
178
- procs = {}
179
- # build this with formatted output (-F) from lsof
180
- # procs = {
181
- # '123:sshd' => [
182
- # 'ipv4:tcp:22:127.0.0.1',
183
- # 'ipv6:tcp:22:::1',
184
- # 'ipv4:tcp:*',
185
- # 'ipv6:tcp:*',
186
- # ],
187
- # '456:ntpd' => [
188
- # 'ipv4:udp:123:*',
189
- # 'ipv6:udp:123:*',
190
- # ]
191
- # }
192
- proc_id = port_id = nil
193
- lsof_cmd.stdout.each_line do |line|
194
- line.chomp!
195
- key = line.slice!(0)
196
- case key
197
- when 'p'
198
- proc_id = line
199
- port_id = nil
200
- when 'c'
201
- proc_id += ':' + line
202
- when 't'
203
- port_id = line.downcase
204
- when 'P'
205
- port_id += ':' + line.downcase
206
- when 'n'
207
- src, dst = line.split('->')
208
-
209
- # skip active comm streams
210
- next if dst
211
-
212
- host, port = /^(\S+):(\d+|\*)$/.match(src)[1, 2]
213
-
214
- # skip channels from port 0 - what does this mean?
215
- next if port == '*'
216
-
217
- # create new array stub if !exist?
218
- procs[proc_id] = [] unless procs.key?(proc_id)
219
-
220
- # change address '*' to zero
221
- host = (port_id =~ /^ipv6:/) ? '[::]' : '0.0.0.0' if host == '*'
222
- # entrust URI to scrub the host and port
223
- begin
224
- uri = URI("addr://#{host}:#{port}")
225
- uri.host && uri.port
226
- rescue => e
227
- warn "could not parse URI 'addr://#{host}:#{port}' - #{e}"
228
- next
148
+ def info
149
+ ports = []
150
+
151
+ # check that lsof is available, otherwise fail
152
+ fail 'Please ensure `lsof` is available on the machine.' if !inspec.command(@lsof.to_s).exist?
153
+
154
+ # -F p=pid, c=command, P=protocol name, t=type, n=internet addresses
155
+ # see 'OUTPUT FOR OTHER PROGRAMS' in LSOF(8)
156
+ lsof_cmd = inspec.command("#{@lsof} -nP -i -FpctPn")
157
+ return nil if lsof_cmd.exit_status.to_i != 0
158
+
159
+ # map to desired return struct
160
+ lsof_parser(lsof_cmd).each do |process, port_ids|
161
+ pid, cmd = process.split(':')
162
+ port_ids.each do |port_str|
163
+ # should not break on ipv6 addresses
164
+ ipv, proto, port, host = port_str.split(':', 4)
165
+ ports.push({ port: port.to_i,
166
+ address: host,
167
+ protocol: ipv == 'ipv6' ? proto + '6' : proto,
168
+ process: cmd,
169
+ pid: pid.to_i })
229
170
  end
171
+ end
230
172
 
231
- # e.g. 'ipv4:tcp:22:127.0.0.1'
232
- # strip ipv6 squares for inspec
233
- port_id += ':' + port + ':' + host.gsub(/^\[|\]$/, '')
173
+ ports
174
+ end
234
175
 
235
- # lsof will give us another port unless it's done
236
- procs[proc_id] << port_id
176
+ # rubocop:disable Metrics/CyclomaticComplexity
177
+ # rubocop:disable Metrics/AbcSize
178
+ def lsof_parser(lsof_cmd)
179
+ procs = {}
180
+ # build this with formatted output (-F) from lsof
181
+ # procs = {
182
+ # '123:sshd' => [
183
+ # 'ipv4:tcp:22:127.0.0.1',
184
+ # 'ipv6:tcp:22:::1',
185
+ # 'ipv4:tcp:*',
186
+ # 'ipv6:tcp:*',
187
+ # ],
188
+ # '456:ntpd' => [
189
+ # 'ipv4:udp:123:*',
190
+ # 'ipv6:udp:123:*',
191
+ # ]
192
+ # }
193
+ proc_id = port_id = nil
194
+ lsof_cmd.stdout.each_line do |line|
195
+ line.chomp!
196
+ key = line.slice!(0)
197
+ case key
198
+ when 'p'
199
+ proc_id = line
200
+ port_id = nil
201
+ when 'c'
202
+ proc_id += ':' + line
203
+ when 't'
204
+ port_id = line.downcase
205
+ when 'P'
206
+ port_id += ':' + line.downcase
207
+ when 'n'
208
+ src, dst = line.split('->')
209
+
210
+ # skip active comm streams
211
+ next if dst
212
+
213
+ host, port = /^(\S+):(\d+|\*)$/.match(src)[1, 2]
214
+
215
+ # skip channels from port 0 - what does this mean?
216
+ next if port == '*'
217
+
218
+ # create new array stub if !exist?
219
+ procs[proc_id] = [] unless procs.key?(proc_id)
220
+
221
+ # change address '*' to zero
222
+ host = (port_id =~ /^ipv6:/) ? '[::]' : '0.0.0.0' if host == '*'
223
+ # entrust URI to scrub the host and port
224
+ begin
225
+ uri = URI("addr://#{host}:#{port}")
226
+ uri.host && uri.port
227
+ rescue => e
228
+ warn "could not parse URI 'addr://#{host}:#{port}' - #{e}"
229
+ next
230
+ end
231
+
232
+ # e.g. 'ipv4:tcp:22:127.0.0.1'
233
+ # strip ipv6 squares for inspec
234
+ port_id += ':' + port + ':' + host.gsub(/^\[|\]$/, '')
235
+
236
+ # lsof will give us another port unless it's done
237
+ procs[proc_id] << port_id
238
+ end
237
239
  end
238
- end
239
240
 
240
- procs
241
+ procs
242
+ end
241
243
  end
242
- end
243
244
 
244
- # extract port information from netstat
245
- class LinuxPorts < PortsInfo
246
- def info
247
- cmd = inspec.command('netstat -tulpen')
248
- return nil if cmd.exit_status.to_i != 0
245
+ # extract port information from netstat
246
+ class LinuxPorts < PortsInfo
247
+ def info
248
+ cmd = inspec.command('netstat -tulpen')
249
+ return nil if cmd.exit_status.to_i != 0
249
250
 
250
- ports = []
251
- # parse all lines
252
- cmd.stdout.each_line do |line|
253
- port_info = parse_netstat_line(line)
251
+ ports = []
252
+ # parse all lines
253
+ cmd.stdout.each_line do |line|
254
+ port_info = parse_netstat_line(line)
254
255
 
255
- # only push protocols we are interested in
256
- next unless %w{tcp tcp6 udp udp6}.include?(port_info[:protocol])
257
- ports.push(port_info)
256
+ # only push protocols we are interested in
257
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info[:protocol])
258
+ ports.push(port_info)
259
+ end
260
+ ports
258
261
  end
259
- ports
260
- end
261
262
 
262
- def parse_net_address(net_addr, protocol)
263
- if protocol.eql?('tcp6') || protocol.eql?('udp6')
264
- # prep for URI parsing, parse ip6 port
265
- ip6 = /^(\S+):(\d+)$/.match(net_addr)
266
- ip6addr = ip6[1]
267
- ip6addr = '::' if ip6addr =~ /^:::$/
268
- # build uri
269
- ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
270
- # replace []
271
- host = ip_addr.host[1..ip_addr.host.size-2]
272
- else
273
- ip_addr = URI('addr://'+net_addr)
274
- host = ip_addr.host
275
- end
263
+ def parse_net_address(net_addr, protocol)
264
+ if protocol.eql?('tcp6') || protocol.eql?('udp6')
265
+ # prep for URI parsing, parse ip6 port
266
+ ip6 = /^(\S+):(\d+)$/.match(net_addr)
267
+ ip6addr = ip6[1]
268
+ ip6addr = '::' if ip6addr =~ /^:::$/
269
+ # build uri
270
+ ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
271
+ # replace []
272
+ host = ip_addr.host[1..ip_addr.host.size-2]
273
+ else
274
+ ip_addr = URI('addr://'+net_addr)
275
+ host = ip_addr.host
276
+ end
276
277
 
277
- port = ip_addr.port
278
+ port = ip_addr.port
278
279
 
279
- [host, port]
280
- rescue URI::InvalidURIError => e
281
- warn "Could not parse #{net_addr}, #{e}"
282
- nil
283
- end
280
+ [host, port]
281
+ rescue URI::InvalidURIError => e
282
+ warn "Could not parse #{net_addr}, #{e}"
283
+ nil
284
+ end
284
285
 
285
- def parse_netstat_line(line)
286
- # parse each line
287
- # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name
288
- parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)\s+(\S+)\s+(\S+)/.match(line)
286
+ def parse_netstat_line(line)
287
+ # parse each line
288
+ # 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name
289
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)\s+(\S+)\s+(\S+)/.match(line)
289
290
 
290
- return {} if parsed.nil? || line.match(/^proto/i)
291
+ return {} if parsed.nil? || line.match(/^proto/i)
291
292
 
292
- # parse ip4 and ip6 addresses
293
- protocol = parsed[1].downcase
293
+ # parse ip4 and ip6 addresses
294
+ protocol = parsed[1].downcase
294
295
 
295
- # detect protocol if not provided
296
- protocol += '6' if parsed[4].count(':') > 1 && %w{tcp udp}.include?(protocol)
296
+ # detect protocol if not provided
297
+ protocol += '6' if parsed[4].count(':') > 1 && %w{tcp udp}.include?(protocol)
297
298
 
298
- # extract host and port information
299
- host, port = parse_net_address(parsed[4], protocol)
299
+ # extract host and port information
300
+ host, port = parse_net_address(parsed[4], protocol)
300
301
 
301
- # extract PID
302
- process = parsed[9].split('/')
303
- pid = process[0]
304
- pid = pid.to_i if pid =~ /^\d+$/
305
- process = process[1]
302
+ # extract PID
303
+ process = parsed[9].split('/')
304
+ pid = process[0]
305
+ pid = pid.to_i if pid =~ /^\d+$/
306
+ process = process[1]
306
307
 
307
- # map data
308
- {
309
- port: port,
310
- address: host,
311
- protocol: protocol,
312
- process: process,
313
- pid: pid,
314
- }
308
+ # map data
309
+ {
310
+ port: port,
311
+ address: host,
312
+ protocol: protocol,
313
+ process: process,
314
+ pid: pid,
315
+ }
316
+ end
315
317
  end
316
- end
317
318
 
318
- # extracts information from sockstat
319
- class FreeBsdPorts < PortsInfo
320
- def info
321
- cmd = inspec.command('sockstat -46l')
322
- return nil if cmd.exit_status.to_i != 0
319
+ # extracts information from sockstat
320
+ class FreeBsdPorts < PortsInfo
321
+ def info
322
+ cmd = inspec.command('sockstat -46l')
323
+ return nil if cmd.exit_status.to_i != 0
323
324
 
324
- ports = []
325
- # split on each newline
326
- cmd.stdout.each_line do |line|
327
- port_info = parse_sockstat_line(line)
325
+ ports = []
326
+ # split on each newline
327
+ cmd.stdout.each_line do |line|
328
+ port_info = parse_sockstat_line(line)
328
329
 
329
- # push data, if not headerfile
330
- next unless %w{tcp tcp6 udp udp6}.include?(port_info[:protocol])
331
- ports.push(port_info)
330
+ # push data, if not headerfile
331
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info[:protocol])
332
+ ports.push(port_info)
333
+ end
334
+ ports
332
335
  end
333
- ports
334
- end
335
336
 
336
- def parse_net_address(net_addr, protocol)
337
- case protocol
338
- when 'tcp4', 'udp4', 'tcp', 'udp'
339
- # replace * with 0.0.0.0
340
- net_addr = net_addr.gsub(/^\*:/, '0.0.0.0:') if net_addr =~ /^*:(\d+)$/
341
- ip_addr = URI('addr://'+net_addr)
342
- host = ip_addr.host
343
- port = ip_addr.port
344
- when 'tcp6', 'udp6'
345
- return [] if net_addr == '*:*' # abort for now
346
- # replace * with 0:0:0:0:0:0:0:0
347
- net_addr = net_addr.gsub(/^\*:/, '0:0:0:0:0:0:0:0:') if net_addr =~ /^*:(\d+)$/
348
- # extract port
349
- ip6 = /^(\S+):(\d+)$/.match(net_addr)
350
- ip6addr = ip6[1]
351
- ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
352
- # replace []
353
- host = ip_addr.host[1..ip_addr.host.size-2]
354
- port = ip_addr.port
337
+ def parse_net_address(net_addr, protocol)
338
+ case protocol
339
+ when 'tcp4', 'udp4', 'tcp', 'udp'
340
+ # replace * with 0.0.0.0
341
+ net_addr = net_addr.gsub(/^\*:/, '0.0.0.0:') if net_addr =~ /^*:(\d+)$/
342
+ ip_addr = URI('addr://'+net_addr)
343
+ host = ip_addr.host
344
+ port = ip_addr.port
345
+ when 'tcp6', 'udp6'
346
+ return [] if net_addr == '*:*' # abort for now
347
+ # replace * with 0:0:0:0:0:0:0:0
348
+ net_addr = net_addr.gsub(/^\*:/, '0:0:0:0:0:0:0:0:') if net_addr =~ /^*:(\d+)$/
349
+ # extract port
350
+ ip6 = /^(\S+):(\d+)$/.match(net_addr)
351
+ ip6addr = ip6[1]
352
+ ip_addr = URI("addr://[#{ip6addr}]:#{ip6[2]}")
353
+ # replace []
354
+ host = ip_addr.host[1..ip_addr.host.size-2]
355
+ port = ip_addr.port
356
+ end
357
+ [host, port]
358
+ rescue URI::InvalidURIError => e
359
+ warn "Could not parse #{net_addr}, #{e}"
360
+ nil
355
361
  end
356
- [host, port]
357
- rescue URI::InvalidURIError => e
358
- warn "Could not parse #{net_addr}, #{e}"
359
- nil
360
- end
361
-
362
- def parse_sockstat_line(line)
363
- # 1 - USER, 2 - COMMAND, 3 - PID, 4 - FD 5 - PROTO, 6 - LOCAL ADDRESS, 7 - FOREIGN ADDRESS
364
- parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/.match(line)
365
- return {} if parsed.nil?
366
-
367
- # extract ip information
368
- protocol = parsed[5].downcase
369
- host, port = parse_net_address(parsed[6], protocol)
370
- return {} if host.nil? or port.nil?
371
-
372
- # extract process
373
- process = parsed[2]
374
-
375
- # extract PID
376
- pid = parsed[3]
377
- pid = pid.to_i if pid =~ /^\d+$/
378
-
379
- # map tcp4 and udp4
380
- protocol = 'tcp' if protocol.eql?('tcp4')
381
- protocol = 'udp' if protocol.eql?('udp4')
382
-
383
- # map data
384
- {
385
- port: port,
386
- address: host,
387
- protocol: protocol,
388
- process: process,
389
- pid: pid,
390
- }
391
- end
392
- end
393
362
 
394
- class SolarisPorts < FreeBsdPorts
395
- include SolarisNetstatParser
363
+ def parse_sockstat_line(line)
364
+ # 1 - USER, 2 - COMMAND, 3 - PID, 4 - FD 5 - PROTO, 6 - LOCAL ADDRESS, 7 - FOREIGN ADDRESS
365
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)$/.match(line)
366
+ return {} if parsed.nil?
396
367
 
397
- def info
398
- # extract all port info
399
- cmd = inspec.command('netstat -an -f inet -f inet6')
400
- return nil if cmd.exit_status.to_i != 0
368
+ # extract ip information
369
+ protocol = parsed[5].downcase
370
+ host, port = parse_net_address(parsed[6], protocol)
371
+ return {} if host.nil? or port.nil?
401
372
 
402
- # parse the content
403
- netstat_ports = parse_netstat(cmd.stdout)
373
+ # extract process
374
+ process = parsed[2]
404
375
 
405
- # filter all ports, where we listen
406
- listen = netstat_ports.select { |val|
407
- !val['state'].nil? && 'listen'.casecmp(val['state']) == 0
408
- }
376
+ # extract PID
377
+ pid = parsed[3]
378
+ pid = pid.to_i if pid =~ /^\d+$/
409
379
 
410
- # map the data
411
- ports = listen.map { |val|
412
- protocol = val['protocol']
413
- local_addr = val['local-address']
380
+ # map tcp4 and udp4
381
+ protocol = 'tcp' if protocol.eql?('tcp4')
382
+ protocol = 'udp' if protocol.eql?('udp4')
414
383
 
415
- # solaris uses 127.0.0.1.57455 instead 127.0.0.1:57455, lets convert the
416
- # the last . to :
417
- local_addr[local_addr.rindex('.')] = ':'
418
- host, port = parse_net_address(local_addr, protocol)
384
+ # map data
419
385
  {
420
386
  port: port,
421
387
  address: host,
422
388
  protocol: protocol,
423
- process: nil, # we do not have pid on solaris
424
- pid: nil, # we do not have pid on solaris
389
+ process: process,
390
+ pid: pid,
391
+ }
392
+ end
393
+ end
394
+
395
+ class SolarisPorts < FreeBsdPorts
396
+ include SolarisNetstatParser
397
+
398
+ def info
399
+ # extract all port info
400
+ cmd = inspec.command('netstat -an -f inet -f inet6')
401
+ return nil if cmd.exit_status.to_i != 0
402
+
403
+ # parse the content
404
+ netstat_ports = parse_netstat(cmd.stdout)
405
+
406
+ # filter all ports, where we listen
407
+ listen = netstat_ports.select { |val|
408
+ !val['state'].nil? && 'listen'.casecmp(val['state']) == 0
409
+ }
410
+
411
+ # map the data
412
+ ports = listen.map { |val|
413
+ protocol = val['protocol']
414
+ local_addr = val['local-address']
415
+
416
+ # solaris uses 127.0.0.1.57455 instead 127.0.0.1:57455, lets convert the
417
+ # the last . to :
418
+ local_addr[local_addr.rindex('.')] = ':'
419
+ host, port = parse_net_address(local_addr, protocol)
420
+ {
421
+ port: port,
422
+ address: host,
423
+ protocol: protocol,
424
+ process: nil, # we do not have pid on solaris
425
+ pid: nil, # we do not have pid on solaris
426
+ }
425
427
  }
426
- }
427
- ports
428
+ ports
429
+ end
428
430
  end
429
431
  end