inspec-core 4.20.10 → 4.22.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/inspec-core.gemspec +1 -1
- data/lib/inspec/base_cli.rb +1 -1
- data/lib/inspec/exceptions.rb +1 -0
- data/lib/inspec/input_registry.rb +2 -1
- data/lib/inspec/metadata.rb +6 -1
- data/lib/inspec/plugin/v2/plugin_types/reporter.rb +11 -5
- data/lib/inspec/profile.rb +30 -9
- data/lib/inspec/reporters/base.rb +11 -5
- data/lib/inspec/reporters/cli.rb +1 -0
- data/lib/inspec/reporters/json.rb +9 -4
- data/lib/inspec/resources/apt.rb +2 -0
- data/lib/inspec/resources/interface.rb +55 -0
- data/lib/inspec/resources/interfaces.rb +119 -0
- data/lib/inspec/resources/mount.rb +1 -1
- data/lib/inspec/resources/mysql_session.rb +26 -7
- data/lib/inspec/resources/postgres_session.rb +1 -1
- data/lib/inspec/resources/service.rb +2 -2
- data/lib/inspec/resources/users.rb +1 -1
- data/lib/inspec/run_data.rb +1 -1
- data/lib/inspec/run_data/profile.rb +4 -4
- data/lib/inspec/runner.rb +8 -2
- data/lib/inspec/runner_rspec.rb +4 -1
- data/lib/inspec/schema.rb +2 -0
- data/lib/inspec/schema/exec_json.rb +4 -3
- data/lib/inspec/schema/primitives.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-reporter-html2/README.md +53 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2.rb +18 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/reporter.rb +24 -0
- data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/version.rb +8 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +46 -0
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +77 -0
- data/lib/plugins/inspec-reporter-html2/templates/default.css +107 -0
- data/lib/plugins/inspec-reporter-html2/templates/default.js +79 -0
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +23 -0
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +15 -0
- data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +8 -0
- metadata +22 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b909b05a8f7510d2833aee0e464f0ed9cec64409fe5825fc05078c67b01e4a43
|
4
|
+
data.tar.gz: ee56167e3ab21f7eed5fd938e5728c3f48a7621fe71d1f3294cb4f2991298a79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 116d85fb0ef947cda4beb31c825cb02e134e2c9130338e062cc4e82d2d58a59e6f7fbdc8df4908759c0d508692efc00dac40550d0ce6ae9da72e904336f1fed6
|
7
|
+
data.tar.gz: e6208c479f04da18199aec2b1c8dc4c8e101bb08609b32fb9d026f485f69d7e848d62a68a6087171fa38a3486f44777d0ad56c64fe2743195b4cc1abb58f8ad9
|
data/Gemfile
CHANGED
data/inspec-core.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency "chef-telemetry", "~> 1.0"
|
27
27
|
spec.add_dependency "license-acceptance", ">= 0.2.13", "< 2.0"
|
28
28
|
spec.add_dependency "thor", ">= 0.20", "< 2.0"
|
29
|
-
spec.add_dependency "json_schemer", "
|
29
|
+
spec.add_dependency "json_schemer", ">= 0.2.1", "< 0.2.12"
|
30
30
|
spec.add_dependency "method_source", ">= 0.8", "< 2.0"
|
31
31
|
spec.add_dependency "rubyzip", "~> 1.2", ">= 1.2.2"
|
32
32
|
spec.add_dependency "rspec", "~> 3.9"
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -231,7 +231,7 @@ module Inspec
|
|
231
231
|
|
232
232
|
private
|
233
233
|
|
234
|
-
ALL_OF_OUR_REPORTERS = %w{json json-min json-rspec json-automate junit html yaml documentation progress}.freeze # BUT WHY?!?!
|
234
|
+
ALL_OF_OUR_REPORTERS = %w{json json-min json-rspec json-automate junit html html2 yaml documentation progress}.freeze # BUT WHY?!?!
|
235
235
|
|
236
236
|
def suppress_log_output?(opts)
|
237
237
|
return false if opts["reporter"].nil?
|
data/lib/inspec/exceptions.rb
CHANGED
@@ -4,6 +4,7 @@ module Inspec
|
|
4
4
|
module Exceptions
|
5
5
|
class InputsFileDoesNotExist < ArgumentError; end
|
6
6
|
class InputsFileNotReadable < ArgumentError; end
|
7
|
+
class ProfileLoadFailed < StandardError; end
|
7
8
|
class ResourceFailed < StandardError; end
|
8
9
|
class ResourceSkipped < StandardError; end
|
9
10
|
class SecretsBackendNotFound < ArgumentError; end
|
@@ -165,7 +165,8 @@ module Inspec
|
|
165
165
|
raise ArgumentError, "ERROR: An '=' is required when using --input. Usage: --input input_name1=input_value1 input2=value2"
|
166
166
|
end
|
167
167
|
end
|
168
|
-
|
168
|
+
pair = pair.match(/(.*?)=(.*)/)
|
169
|
+
input_name, input_value = pair[1], pair[2]
|
169
170
|
input_value = parse_cli_input_value(input_name, input_value)
|
170
171
|
evt = Inspec::Input::Event.new(
|
171
172
|
value: input_value,
|
data/lib/inspec/metadata.rb
CHANGED
@@ -9,7 +9,12 @@ require "inspec/version"
|
|
9
9
|
require "inspec/utils/spdx"
|
10
10
|
|
11
11
|
module Inspec
|
12
|
-
#
|
12
|
+
# The Metadata class represents a profile's metadata.
|
13
|
+
# This includes the metadata stored in the profile's metadata.rb file, as well as inferred
|
14
|
+
# metadata like if this profile supports the current runtime and the intended target.
|
15
|
+
# This class does NOT represent the runtime state of a profile during execution.
|
16
|
+
# See lib/inspec/profile.rb for the runtime representation of a profile.
|
17
|
+
#
|
13
18
|
# A Metadata object may be created and finalized with invalid data.
|
14
19
|
# This allows the check CLI command to analyse the issues.
|
15
20
|
# Use valid? to determine if the metadata is coherent.
|
@@ -31,17 +31,14 @@ module Inspec::Plugin::V2::PluginType
|
|
31
31
|
runtime_config = Inspec::Config.cached.respond_to?(:final_options) ? Inspec::Config.cached.final_options : {}
|
32
32
|
|
33
33
|
message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
|
34
|
-
trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
34
|
+
@trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
35
35
|
include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
|
36
36
|
|
37
37
|
@run_data[:profiles]&.each do |p|
|
38
38
|
p[:controls].each do |c|
|
39
39
|
c[:results]&.map! do |r|
|
40
40
|
r.delete(:backtrace) unless include_backtrace
|
41
|
-
|
42
|
-
r[:message] = r[:message][0...trunc] + "[Truncated to #{trunc} characters]"
|
43
|
-
end
|
44
|
-
r
|
41
|
+
process_message_truncation(r)
|
45
42
|
end
|
46
43
|
end
|
47
44
|
end
|
@@ -64,5 +61,14 @@ module Inspec::Plugin::V2::PluginType
|
|
64
61
|
def self.run_data_schema_constraints
|
65
62
|
raise NotImplementedError, "#{self.class} must implement a `run_data_schema_constraints` class method to declare its compatibiltity with the RunData API."
|
66
63
|
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def process_message_truncation(result)
|
68
|
+
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
69
|
+
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
70
|
+
end
|
71
|
+
result
|
72
|
+
end
|
67
73
|
end
|
68
74
|
end
|
data/lib/inspec/profile.rb
CHANGED
@@ -94,6 +94,7 @@ module Inspec
|
|
94
94
|
@input_values = options[:inputs]
|
95
95
|
@tests_collected = false
|
96
96
|
@libraries_loaded = false
|
97
|
+
@state = :loaded
|
97
98
|
@check_mode = options[:check_mode] || false
|
98
99
|
@parent_profile = options[:parent_profile]
|
99
100
|
@legacy_profile_path = options[:profiles_path] || false
|
@@ -146,7 +147,12 @@ module Inspec
|
|
146
147
|
options[:profile_context] ||
|
147
148
|
Inspec::ProfileContext.for_profile(self, @backend)
|
148
149
|
|
149
|
-
|
150
|
+
if metadata.supports_platform?(@backend)
|
151
|
+
@supports_platform = true
|
152
|
+
else
|
153
|
+
@supports_platform = false
|
154
|
+
@state = :skipped
|
155
|
+
end
|
150
156
|
@supports_runtime = metadata.supports_runtime?
|
151
157
|
end
|
152
158
|
|
@@ -162,6 +168,10 @@ module Inspec
|
|
162
168
|
@writable
|
163
169
|
end
|
164
170
|
|
171
|
+
def failed?
|
172
|
+
@state == :failed
|
173
|
+
end
|
174
|
+
|
165
175
|
#
|
166
176
|
# Is this profile is supported on the current platform of the
|
167
177
|
# backend machine and the current inspec version.
|
@@ -197,7 +207,7 @@ module Inspec
|
|
197
207
|
end
|
198
208
|
|
199
209
|
def collect_tests(include_list = @controls)
|
200
|
-
unless @tests_collected
|
210
|
+
unless @tests_collected || failed?
|
201
211
|
return unless supports_platform?
|
202
212
|
|
203
213
|
locked_dependencies.each(&:collect_tests)
|
@@ -206,7 +216,12 @@ module Inspec
|
|
206
216
|
next if content.nil? || content.empty?
|
207
217
|
|
208
218
|
abs_path = source_reader.target.abs_path(path)
|
209
|
-
|
219
|
+
begin
|
220
|
+
@runner_context.load_control_file(content, abs_path, nil)
|
221
|
+
rescue => e
|
222
|
+
@state = :failed
|
223
|
+
raise Inspec::Exceptions::ProfileLoadFailed, "Failed to load source for #{path}: #{e}"
|
224
|
+
end
|
210
225
|
end
|
211
226
|
@tests_collected = true
|
212
227
|
end
|
@@ -249,12 +264,13 @@ module Inspec
|
|
249
264
|
d = dep.profile
|
250
265
|
# this will force a dependent profile load so we are only going to add
|
251
266
|
# this metadata if the parent profile is supported.
|
252
|
-
if supports_platform
|
267
|
+
if @supports_platform && !d.supports_platform?
|
253
268
|
# since ruby 1.9 hashes are ordered so we can just use index values here
|
254
269
|
# TODO: NO! this is a violation of encapsulation to an extreme
|
255
270
|
metadata.dependencies[i][:status] = "skipped"
|
256
271
|
msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'."
|
257
|
-
metadata.dependencies[i][:
|
272
|
+
metadata.dependencies[i][:status_message] = msg
|
273
|
+
metadata.dependencies[i][:skip_message] = msg # Repeat as skip_message for backward compatibility
|
258
274
|
next
|
259
275
|
elsif metadata.dependencies[i]
|
260
276
|
# Currently wrapper profiles will load all dependencies, and then we
|
@@ -324,12 +340,13 @@ module Inspec
|
|
324
340
|
res[:sha256] = sha256
|
325
341
|
res[:parent_profile] = parent_profile unless parent_profile.nil?
|
326
342
|
|
327
|
-
if
|
343
|
+
if @supports_platform
|
344
|
+
res[:status_message] = @status_message || ""
|
345
|
+
res[:status] = failed? ? "failed" : "loaded"
|
346
|
+
else
|
328
347
|
res[:status] = "skipped"
|
329
348
|
msg = "Skipping profile: '#{name}' on unsupported platform: '#{backend.platform.name}/#{backend.platform.release}'."
|
330
|
-
res[:
|
331
|
-
else
|
332
|
-
res[:status] = "loaded"
|
349
|
+
res[:status_message] = msg
|
333
350
|
end
|
334
351
|
|
335
352
|
# convert legacy os-* supports to their platform counterpart
|
@@ -455,6 +472,10 @@ module Inspec
|
|
455
472
|
params[:controls].values.length
|
456
473
|
end
|
457
474
|
|
475
|
+
def set_status_message(msg)
|
476
|
+
@status_message = msg.to_s
|
477
|
+
end
|
478
|
+
|
458
479
|
# generates a archive of a folder profile
|
459
480
|
# assumes that the profile was checked before
|
460
481
|
def archive(opts)
|
@@ -14,17 +14,14 @@ module Inspec::Reporters
|
|
14
14
|
runtime_config = Inspec::Config.cached.respond_to?(:final_options) ? Inspec::Config.cached.final_options : {}
|
15
15
|
|
16
16
|
message_truncation = runtime_config[:reporter_message_truncation] || "ALL"
|
17
|
-
trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
17
|
+
@trunc = message_truncation == "ALL" ? -1 : message_truncation.to_i
|
18
18
|
include_backtrace = runtime_config[:reporter_backtrace_inclusion].nil? ? true : runtime_config[:reporter_backtrace_inclusion]
|
19
19
|
|
20
20
|
@run_data[:profiles]&.each do |p|
|
21
21
|
p[:controls].each do |c|
|
22
22
|
c[:results]&.map! do |r|
|
23
23
|
r.delete(:backtrace) unless include_backtrace
|
24
|
-
|
25
|
-
r[:message] = r[:message][0...trunc] + "[Truncated to #{trunc} characters]"
|
26
|
-
end
|
27
|
-
r
|
24
|
+
process_message_truncation(r)
|
28
25
|
end
|
29
26
|
end
|
30
27
|
end
|
@@ -43,5 +40,14 @@ module Inspec::Reporters
|
|
43
40
|
def render
|
44
41
|
raise NotImplementedError, "#{self.class} must implement a `#render` method to format its output."
|
45
42
|
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def process_message_truncation(result)
|
47
|
+
if result.key?(:message) && result[:message] != "" && @trunc > -1 && result[:message].length > @trunc
|
48
|
+
result[:message] = result[:message][0...@trunc] + "[Truncated to #{@trunc} characters]"
|
49
|
+
end
|
50
|
+
result
|
51
|
+
end
|
46
52
|
end
|
47
53
|
end
|
data/lib/inspec/reporters/cli.rb
CHANGED
@@ -72,6 +72,7 @@ module Inspec::Reporters
|
|
72
72
|
"Profile" => format_profile_name(profile),
|
73
73
|
"Version" => profile[:version] || "(not specified)",
|
74
74
|
}
|
75
|
+
header["Failure Message"] = profile[:status_message] if profile[:status] == "failed"
|
75
76
|
header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil?
|
76
77
|
header["Target ID"] = @config["target_id"] unless @config["target_id"].nil?
|
77
78
|
|
@@ -45,8 +45,8 @@ module Inspec::Reporters
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def profiles
|
48
|
-
run_data[:profiles].map
|
49
|
-
{
|
48
|
+
run_data[:profiles].map do |p|
|
49
|
+
res = {
|
50
50
|
name: p[:name],
|
51
51
|
version: p[:version],
|
52
52
|
sha256: p[:sha256],
|
@@ -64,10 +64,15 @@ module Inspec::Reporters
|
|
64
64
|
groups: profile_groups(p),
|
65
65
|
controls: profile_controls(p),
|
66
66
|
status: p[:status],
|
67
|
-
|
67
|
+
status_message: p[:status_message],
|
68
68
|
waiver_data: p[:waiver_data],
|
69
69
|
}.reject { |_k, v| v.nil? }
|
70
|
-
|
70
|
+
|
71
|
+
# For backwards compatibility
|
72
|
+
res[:skip_message] = res[:status_message] if res[:status] == "skipped"
|
73
|
+
|
74
|
+
res
|
75
|
+
end
|
71
76
|
end
|
72
77
|
|
73
78
|
def profile_groups(profile)
|
data/lib/inspec/resources/apt.rb
CHANGED
@@ -90,12 +90,14 @@ module Inspec::Resources
|
|
90
90
|
# deb "http://archive.ubuntu.com/ubuntu/" wily main restricted ...
|
91
91
|
# deb http://archive.ubuntu.com/ubuntu/ wily main restricted ...
|
92
92
|
# deb [trusted=yes] http://archive.ubuntu.com/ubuntu/ wily main restricted ...
|
93
|
+
# deb cdrom:[Ubuntu 15.10 _Wily Werewolf_ - Release amd64 (20151021)]/ wily main restricted ...
|
93
94
|
|
94
95
|
words = line.split
|
95
96
|
words.delete_at 1 if words[1] && words[1].start_with?("[")
|
96
97
|
type, url, distro, *components = words
|
97
98
|
url = url.delete('"') if url
|
98
99
|
|
100
|
+
next if words[1] && words[1].start_with?("cdrom:") # skip unsupported apt-cdrom repos
|
99
101
|
next if components.empty?
|
100
102
|
next unless URI::HTTP === URI.parse(url)
|
101
103
|
next unless %w{deb deb-src}.include? type
|
@@ -47,10 +47,18 @@ module Inspec::Resources
|
|
47
47
|
ipv6_addresses && !ipv6_addresses.empty?
|
48
48
|
end
|
49
49
|
|
50
|
+
def ipv4_address
|
51
|
+
ipv4_addresses.first
|
52
|
+
end
|
53
|
+
|
50
54
|
def ipv4_addresses
|
51
55
|
ipv4_cidrs.map { |i| i.split("/")[0] }
|
52
56
|
end
|
53
57
|
|
58
|
+
def ipv6_address
|
59
|
+
ipv6_addresses.first
|
60
|
+
end
|
61
|
+
|
54
62
|
def ipv6_addresses
|
55
63
|
ipv6_cidrs.map { |i| i.split("/")[0] }
|
56
64
|
end
|
@@ -85,6 +93,7 @@ module Inspec::Resources
|
|
85
93
|
@cache ||= begin
|
86
94
|
provider = LinuxInterface.new(inspec) if inspec.os.linux?
|
87
95
|
provider = WindowsInterface.new(inspec) if inspec.os.windows?
|
96
|
+
provider = BsdInterface.new(inspec) if inspec.os.bsd? # includes macOS
|
88
97
|
Hash(provider && provider.interface_info(@iface))
|
89
98
|
end
|
90
99
|
end
|
@@ -98,6 +107,52 @@ module Inspec::Resources
|
|
98
107
|
end
|
99
108
|
end
|
100
109
|
|
110
|
+
class BsdInterface < InterfaceInfo
|
111
|
+
def interface_info(iface)
|
112
|
+
cmd = inspec.command("ifconfig #{iface}")
|
113
|
+
return nil if cmd.exit_status.to_i != 0
|
114
|
+
|
115
|
+
lines = cmd.stdout.split("\n")
|
116
|
+
iface_info = {
|
117
|
+
name: iface,
|
118
|
+
ipv4_addresses: [], # Actually CIDRs
|
119
|
+
ipv6_addresses: [], # are expected to go here
|
120
|
+
}
|
121
|
+
|
122
|
+
iface_info[:up] = lines[0].include?("UP")
|
123
|
+
lines.each do |line|
|
124
|
+
# IPv4 case
|
125
|
+
m = line.match(/^\s+inet\s+((?:\d{1,3}\.){3}\d{1,3})\s+netmask\s+(0x[a-f0-9]{8})/)
|
126
|
+
if m
|
127
|
+
ip = m[1]
|
128
|
+
hex_mask = m[2]
|
129
|
+
cidr = hex_mask.to_i(16).to_s(2).count("1")
|
130
|
+
iface_info[:ipv4_addresses] << "#{ip}/#{cidr}"
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
# IPv6 case
|
135
|
+
m = line.match(/^\s+inet6\s+([a-f0-9:]+)%#{iface}\s+prefixlen\s+(\d+)/)
|
136
|
+
if m
|
137
|
+
ip = m[1]
|
138
|
+
cidr = m[2]
|
139
|
+
iface_info[:ipv6_addresses] << "#{ip}/#{cidr}"
|
140
|
+
next
|
141
|
+
end
|
142
|
+
|
143
|
+
# Speed detect, crummy - can't detect wifi, finds any number in the string
|
144
|
+
# Ethernet autoselect (1000baseT <full-duplex>)
|
145
|
+
m = line.match(/^\s+media:\D+(\d+)/)
|
146
|
+
if m
|
147
|
+
iface_info[:speed] = m[1].to_i
|
148
|
+
next
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
iface_info
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
101
156
|
class LinuxInterface < InterfaceInfo
|
102
157
|
def interface_info(iface)
|
103
158
|
# will return "[mtu]\n1500\n[type]\n1"
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require "inspec/utils/filter"
|
2
|
+
require "inspec/resources/command"
|
3
|
+
|
4
|
+
module Inspec::Resources
|
5
|
+
class Interfaces < Inspec.resource(1)
|
6
|
+
name "interfaces"
|
7
|
+
supports platform: "unix"
|
8
|
+
supports platform: "windows"
|
9
|
+
desc "Use the interfaces InSpec audit resource to test properties for multiple network interfaces installed on the system"
|
10
|
+
example <<~EXAMPLE
|
11
|
+
describe interfaces do
|
12
|
+
its('names') { should include 'eth0' }
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
attr_reader :iface_data
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"Interfaces"
|
20
|
+
end
|
21
|
+
|
22
|
+
filter = FilterTable.create
|
23
|
+
filter.register_column(:names, field: "name")
|
24
|
+
.install_filter_methods_on_resource(self, :scan_interfaces)
|
25
|
+
|
26
|
+
def ipv4_address
|
27
|
+
require "ipaddr"
|
28
|
+
|
29
|
+
# Loop over interface names
|
30
|
+
# Select those that are up and have an ipv4 address
|
31
|
+
interfaces = names.map { |n| inspec.interface(n) }.select do |i|
|
32
|
+
i.ipv4_address? && i.up?
|
33
|
+
end
|
34
|
+
|
35
|
+
addrs = interfaces.map(&:ipv4_addresses).flatten.map { |a| IPAddr.new(a) }
|
36
|
+
|
37
|
+
# Look for progressively "better" IP addresses
|
38
|
+
[
|
39
|
+
# Loopback and private IP ranges
|
40
|
+
IPAddr.new("127.0.0.0/8"),
|
41
|
+
IPAddr.new("192.168.0.0/16"),
|
42
|
+
IPAddr.new("172.16.0.0/12"),
|
43
|
+
IPAddr.new("10.0.0.0/8"),
|
44
|
+
].each do |private_range|
|
45
|
+
filtered_addrs = addrs.reject { |a| private_range.include?(a) }
|
46
|
+
if filtered_addrs.empty?
|
47
|
+
# Everything we had was a private or loopback IP. Return the "best" thing we were left with.
|
48
|
+
return addrs.first.to_s
|
49
|
+
end
|
50
|
+
|
51
|
+
addrs = filtered_addrs
|
52
|
+
end
|
53
|
+
addrs.first.to_s
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def scan_interfaces
|
59
|
+
@iface_data ||= begin
|
60
|
+
provider = LinuxInterfaceLister.new(inspec) if inspec.os.linux?
|
61
|
+
provider = WindowsInterfaceLister.new(inspec) if inspec.os.windows?
|
62
|
+
provider = BsdInterfaceLister.new(inspec) if inspec.os.bsd? # includes macOS
|
63
|
+
Array(provider && provider.scan_interfaces)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class InterfaceLister
|
68
|
+
attr_reader :inspec
|
69
|
+
def initialize(inspec)
|
70
|
+
@inspec = inspec
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class BsdInterfaceLister < InterfaceLister
|
75
|
+
def scan_interfaces
|
76
|
+
iface_data = []
|
77
|
+
cmd = inspec.command("ifconfig -a")
|
78
|
+
cmd.stdout.split("\n").each do |line|
|
79
|
+
# lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
|
80
|
+
m = line.match(/^(\S+):/)
|
81
|
+
if m
|
82
|
+
iface_data << { "name" => m[1] }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
iface_data
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
class LinuxInterfaceLister < InterfaceLister
|
90
|
+
def scan_interfaces
|
91
|
+
iface_data = []
|
92
|
+
cmd = inspec.command("ls /sys/class/net")
|
93
|
+
cmd.stdout.split("\n").each do |iface|
|
94
|
+
iface_data << { "name" => iface }
|
95
|
+
end
|
96
|
+
iface_data
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class WindowsInterfaceLister < InterfaceLister
|
101
|
+
def scan_interfaces
|
102
|
+
iface_data = []
|
103
|
+
cmd = inspec.command("Get-NetAdapter | Select-Object -Property Name | ConvertTo-Json")
|
104
|
+
begin
|
105
|
+
adapter_info = JSON.parse(cmd.stdout)
|
106
|
+
# May be a Hash if only one, or Array if multiple - normalize to Array
|
107
|
+
adapter_info = [ adapter_info ] if adapter_info.is_a? Hash
|
108
|
+
rescue JSON::ParserError => _e
|
109
|
+
return nil
|
110
|
+
end
|
111
|
+
adapter_info.each do |info|
|
112
|
+
iface_data << { "name" => info["Name"] }
|
113
|
+
end
|
114
|
+
iface_data
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|