inspec-core 4.20.2 → 4.22.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +2 -1
  3. data/inspec-core.gemspec +1 -1
  4. data/lib/inspec/base_cli.rb +1 -1
  5. data/lib/inspec/cli.rb +6 -5
  6. data/lib/inspec/config.rb +0 -1
  7. data/lib/inspec/exceptions.rb +1 -0
  8. data/lib/inspec/input_registry.rb +2 -1
  9. data/lib/inspec/metadata.rb +6 -1
  10. data/lib/inspec/profile.rb +30 -9
  11. data/lib/inspec/reporters.rb +12 -7
  12. data/lib/inspec/reporters/cli.rb +1 -0
  13. data/lib/inspec/reporters/json.rb +9 -4
  14. data/lib/inspec/resources/apt.rb +2 -0
  15. data/lib/inspec/resources/interface.rb +55 -0
  16. data/lib/inspec/resources/interfaces.rb +119 -0
  17. data/lib/inspec/resources/service.rb +1 -1
  18. data/lib/inspec/run_data.rb +10 -3
  19. data/lib/inspec/run_data/profile.rb +4 -4
  20. data/lib/inspec/runner.rb +8 -2
  21. data/lib/inspec/runner_rspec.rb +4 -1
  22. data/lib/inspec/schema.rb +2 -0
  23. data/lib/inspec/schema/exec_json.rb +4 -3
  24. data/lib/inspec/schema/primitives.rb +1 -1
  25. data/lib/inspec/utils/telemetry/run_context_probe.rb +48 -0
  26. data/lib/inspec/version.rb +1 -1
  27. data/lib/plugins/inspec-reporter-html2/README.md +53 -0
  28. data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2.rb +18 -0
  29. data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/reporter.rb +24 -0
  30. data/lib/plugins/inspec-reporter-html2/lib/inspec-reporter-html2/version.rb +8 -0
  31. data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +46 -0
  32. data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +77 -0
  33. data/lib/plugins/inspec-reporter-html2/templates/default.css +107 -0
  34. data/lib/plugins/inspec-reporter-html2/templates/default.js +79 -0
  35. data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +23 -0
  36. data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +15 -0
  37. data/lib/plugins/inspec-reporter-html2/templates/selector.html.erb +8 -0
  38. data/lib/plugins/inspec-reporter-json-min/README.md +10 -0
  39. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min.rb +13 -0
  40. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min/reporter.rb +50 -0
  41. data/lib/plugins/inspec-reporter-json-min/lib/inspec-reporter-json-min/version.rb +5 -0
  42. metadata +21 -5
  43. data/lib/inspec/reporters/json_min.rb +0 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2395ce22d38edca9eaac2fad00c1fc61f3fb95a154618540d23bc2a1e6ce0fcd
4
- data.tar.gz: 4ebdbe5526025408e729b52e0614b43fe89f70b0faae5eeecc8c23db8f0f1ff9
3
+ metadata.gz: 5517c996bd812fed2bb1190f2db0ae0f38ae3eddeab32494fb82dd843c9d9ef3
4
+ data.tar.gz: e293cd323bec3ac3da7300e52d49637a322f6a7a3ac156be4c45bfdc9b4a7440
5
5
  SHA512:
6
- metadata.gz: f2b21bfbc96b4830edf20f1d48493257845bfbeb52137141269371066c47359092d3ed280cc64e9358b3587d0ddb0c54a643cafd730d57ad2d7ba2331d35b33a
7
- data.tar.gz: 84bc11b59621836d20c045efcdee955cc97c605e5902fafaa79c8756dd44f66334731256c6ba4273484d645484f374b1ec2daf471a6dc56805ea819ab2685f38
6
+ metadata.gz: dbb3737b7b031b251d78fadc50e50d32612b5a8a8a183ef204e3abe3b005c5f937850206347ba350f1584145b124bf4ddbdeaf2e6f5c5bbe93a20e979c9883dc
7
+ data.tar.gz: d128dbec0edc3ef77481bf8cb2dd5d70f48189d7c53e1abe19e52480361edcfbbc865f84b2a085cf2b8ab0a27de5ee26c96921f4fba1dca9b688ea9147ac7c65
data/Gemfile CHANGED
@@ -9,7 +9,7 @@ gem "inspec", path: "."
9
9
  # in it in order to package the executable. Hence the odd backwards dependency.
10
10
  gem "inspec-bin", path: "./inspec-bin"
11
11
 
12
- gem "ffi", [">= 1.9.14", "< 1.13"] # 1.13 does not work on Windows: https://github.com/ffi/ffi/issues/784
12
+ gem "ffi", ">= 1.9.14", "!= 1.13.0"
13
13
 
14
14
  group :omnibus do
15
15
  gem "rb-readline"
@@ -31,6 +31,7 @@ group :test do
31
31
  gem "m"
32
32
  gem "pry", "~> 0.10"
33
33
  gem "pry-byebug"
34
+ gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
34
35
  end
35
36
 
36
37
  group :integration do
@@ -39,7 +39,7 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "faraday", ">= 0.9.0"
40
40
  spec.add_dependency "tty-table", "~> 0.10"
41
41
  spec.add_dependency "tty-prompt", "~> 0.17"
42
- spec.add_dependency "tomlrb", "~> 1.2"
42
+ spec.add_dependency "tomlrb", "~> 1.2.0"
43
43
  spec.add_dependency "addressable", "~> 2.4"
44
44
  spec.add_dependency "parslet", "~> 1.5"
45
45
  spec.add_dependency "semverse", "~> 3.0"
@@ -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?
@@ -375,6 +375,12 @@ class Inspec::InspecCLI < Inspec::BaseCLI
375
375
  puts "Valid schemas are #{Inspec::Schema::OutputSchema.names.join(", ")}"
376
376
  end
377
377
 
378
+ desc "run_context", "used to test run-context detection", hide: true
379
+ def run_context
380
+ require "inspec/utils/telemetry/run_context_probe"
381
+ puts Inspec::Telemetry::RunContextProbe.guess_run_context
382
+ end
383
+
378
384
  desc "version", "prints the version of this tool"
379
385
  option :format, type: :string
380
386
  def version
@@ -387,11 +393,6 @@ class Inspec::InspecCLI < Inspec::BaseCLI
387
393
  end
388
394
  map %w{-v --version} => :version
389
395
 
390
- desc "nothing", "does nothing"
391
- def nothing
392
- puts "you did nothing"
393
- end
394
-
395
396
  private
396
397
 
397
398
  def run_command(opts)
@@ -344,7 +344,6 @@ module Inspec
344
344
  cli
345
345
  json
346
346
  json-automate
347
- json-min
348
347
  junit
349
348
  yaml
350
349
  }
@@ -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
- input_name, input_value = pair.split("=")
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,
@@ -9,7 +9,12 @@ require "inspec/version"
9
9
  require "inspec/utils/spdx"
10
10
 
11
11
  module Inspec
12
- # Extract metadata.rb information
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.
@@ -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
- @supports_platform = metadata.supports_platform?(@backend)
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
- @runner_context.load_control_file(content, abs_path, nil)
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? && !d.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][:skip_message] = msg
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 !supports_platform?
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[:skip_message] = msg
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)
@@ -2,7 +2,6 @@ require "inspec/reporters/base"
2
2
  require "inspec/reporters/cli"
3
3
  require "inspec/reporters/json"
4
4
  require "inspec/reporters/json_automate"
5
- require "inspec/reporters/json_min"
6
5
  require "inspec/reporters/junit"
7
6
  require "inspec/reporters/automate"
8
7
  require "inspec/reporters/yaml"
@@ -21,8 +20,6 @@ module Inspec::Reporters
21
20
  # right to introduce breaking changes to this reporter at any time.
22
21
  when "json-automate"
23
22
  reporter = Inspec::Reporters::JsonAutomate.new(config)
24
- when "json-min"
25
- reporter = Inspec::Reporters::JsonMin.new(config)
26
23
  when "junit"
27
24
  reporter = Inspec::Reporters::Junit.new(config)
28
25
  when "automate"
@@ -60,15 +57,23 @@ module Inspec::Reporters
60
57
  case name
61
58
  when "json"
62
59
  reporter = Inspec::Reporters::Json.new(config)
63
- when "json-min"
64
- reporter = Inspec::Reporters::JsonMin.new(config)
65
60
  when "json-automate"
66
61
  reporter = Inspec::Reporters::JsonAutomate.new(config)
67
62
  when "yaml"
68
63
  reporter = Inspec::Reporters::Yaml.new(config)
69
64
  else
70
- # use base run_data hash for any other report
71
- return run_data
65
+ # If we made it here, it might be a plugin
66
+ begin
67
+ activator = Inspec::Plugin::V2::Registry.instance.find_activator(plugin_type: :reporter, activator_name: name.to_sym)
68
+ activator.activate!
69
+ reporter = activator.implementation_class.new(config)
70
+ unless reporter.respond_to(:report?)
71
+ return run_data
72
+ end
73
+ rescue Inspec::Plugin::V2::LoadError
74
+ # Must not have been a plugin - just return the run_data
75
+ return run_data
76
+ end
72
77
  end
73
78
 
74
79
  reporter.report
@@ -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 { |p|
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
- skip_message: p[:skip_message],
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)
@@ -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