inspec 1.27.0 → 1.28.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a97d110626e91da96f74c2ade783985774f985c6
4
- data.tar.gz: 9c6f406166b6e6592b1a43b69353c5bcaf8dc0ef
3
+ metadata.gz: a0d407da61108e3110902410c9250bb1aa65727d
4
+ data.tar.gz: '08e3dfe5818627298ae9fada47ebdb20295be3d3'
5
5
  SHA512:
6
- metadata.gz: 9d6ec986d6914cd057869cce5ba76c3c13c31a8bd044aefcaed4cf6a01cc4cf91ac0b3c118519e7c58c98b8ddd63bef0e04780c8099e1c68bacbfca218d8ae88
7
- data.tar.gz: 6b20ef3baa24b1e3de67373101f6c356189ac990fd7789d889ac50fb6d9272f2889f45d61edbdcbe12d9ae4b75d3b302837b043536e7d1d57c5064b95ffa6312
6
+ metadata.gz: 00b4a96e07c8b54024b756c1abfe4fe0334d16817177ae538902ddb76cc9e21fbd604a473c41fc3538b2a26491872f8ae30c02af82cf913c430238fe526a07d9
7
+ data.tar.gz: a020a8a48d76d26da019ea92ad298b9e1c70d5a22dc8d3f691457201649d5108c85fb49ff90f37d1112f6b5710db2c5296a20d09ab9512fc5281a68535ba218a
@@ -1,5 +1,30 @@
1
1
  # Change Log
2
2
 
3
+ ## [v1.28.0](https://github.com/chef/inspec/tree/v1.28.0) (2017-06-15)
4
+ [Full Changelog](https://github.com/chef/inspec/compare/v1.27.0...v1.28.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Add support for CoreOS to the service resource [\#1928](https://github.com/chef/inspec/pull/1928) ([rarenerd](https://github.com/rarenerd))
9
+ - Host resource ping method should return stdout [\#1927](https://github.com/chef/inspec/pull/1927) ([justincmoy](https://github.com/justincmoy))
10
+ - Add TCP reachability support on Linux for host resource [\#1915](https://github.com/chef/inspec/pull/1915) ([adamleff](https://github.com/adamleff))
11
+ - Adds support for iis\_app InSpec testing [\#1905](https://github.com/chef/inspec/pull/1905) ([EasyAsABC123](https://github.com/EasyAsABC123))
12
+ - Add support for virtualization resource [\#1803](https://github.com/chef/inspec/pull/1803) ([tkak](https://github.com/tkak))
13
+
14
+ **Fixed bugs:**
15
+
16
+ - Error when listing compliance profiles against Automate pre 0.8 [\#1921](https://github.com/chef/inspec/issues/1921)
17
+ - Unexpected `nil` authentication with `inspec exec -t` and WinRM [\#1901](https://github.com/chef/inspec/issues/1901)
18
+ - inspec exec with --json-config option having multiple node information [\#1897](https://github.com/chef/inspec/issues/1897)
19
+ - describe package failing in newer version [\#1797](https://github.com/chef/inspec/issues/1797)
20
+ - Fix detection of Automate pre-0.8.x in Compliance::API [\#1922](https://github.com/chef/inspec/pull/1922) ([adamleff](https://github.com/adamleff))
21
+ - bugfix: reading tgz files with binread [\#1920](https://github.com/chef/inspec/pull/1920) ([arlimus](https://github.com/arlimus))
22
+ - fix intermitten functional vendor test failures [\#1919](https://github.com/chef/inspec/pull/1919) ([arlimus](https://github.com/arlimus))
23
+ - enforce option values where needed [\#1918](https://github.com/chef/inspec/pull/1918) ([arlimus](https://github.com/arlimus))
24
+ - inspec archive for tgz files on windows [\#1907](https://github.com/chef/inspec/pull/1907) ([arlimus](https://github.com/arlimus))
25
+ - reading binary profile data on windows [\#1906](https://github.com/chef/inspec/pull/1906) ([arlimus](https://github.com/arlimus))
26
+ - remove duplicate message in describe.one blocks [\#1896](https://github.com/chef/inspec/pull/1896) ([arlimus](https://github.com/arlimus))
27
+
3
28
  ## [v1.27.0](https://github.com/chef/inspec/tree/v1.27.0) (2017-06-06)
4
29
  [Full Changelog](https://github.com/chef/inspec/compare/v1.26.0...v1.27.0)
5
30
 
@@ -12,7 +12,7 @@ A `host` resource block declares a host name, and then (depending on what is to
12
12
 
13
13
  .. code-block:: ruby
14
14
 
15
- describe host('example.com', port: 80, proto: 'tcp') do
15
+ describe host('example.com', port: 80, protocol: 'tcp') do
16
16
  it { should be_reachable }
17
17
  end
18
18
 
@@ -21,7 +21,7 @@ where
21
21
  * `host()` must specify a host name and may specify a port number and/or a protocol
22
22
  * `'example.com'` is the host name
23
23
  * `port:` is the port number
24
- * `proto: 'name'` is the Internet protocol: TCP (`proto: 'tcp'`), UDP (`proto: 'udp'` or ICMP (`proto: 'icmp'`))
24
+ * `protocol: 'name'` is the Internet protocol: TCP (`protocol: 'tcp'`), UDP (`protocol: 'udp'` or ICMP (`protocol: 'icmp'`))
25
25
  * `be_reachable` is a valid matcher for this resource
26
26
 
27
27
 
@@ -73,13 +73,13 @@ The following examples show how to use this InSpec audit resource.
73
73
 
74
74
  ### Verify host name is reachable over a specific protocol and port number
75
75
 
76
- describe host('example.com', port: 53, proto: 'udp') do
76
+ describe host('example.com', port: 80, protocol: 'tcp') do
77
77
  it { should be_reachable }
78
78
  end
79
79
 
80
80
  ### Verify that a specific IP address can be resolved
81
81
 
82
- describe host('example.com', port: 80, proto: 'tcp') do
82
+ describe host('example.com') do
83
83
  it { should be_resolvable }
84
84
  its('ipaddress') { should include '192.168.1.1' }
85
85
  end
@@ -0,0 +1,126 @@
1
+ ---
2
+ title: About the iis_app Resource
3
+ ---
4
+
5
+ # iis_app
6
+
7
+ Use the `iis_app` InSpec audit resource to test the state of IIS on Windows Server 2012 (and later).
8
+
9
+ ## Syntax
10
+
11
+ An `iis_app` resource block declares details about the named site:
12
+
13
+ describe iis_app('application_path', 'site_name') do
14
+ it { should exist }
15
+ it { should have_application_pool('application_pool') }
16
+ it { should have_protocols('protocol') }
17
+ it { should have_site_name('site') }
18
+ it { should have_physical_path('physical_path') }
19
+ it { should have_path('application_path') }
20
+ end
21
+
22
+ where
23
+
24
+ * `'application_path'` is the path to the application, such as `'/myapp'`
25
+ * `'site_name'` is the name of the site, such as `'Default Web Site'`
26
+ * `('application_pool')` is the name of the application pool in which the site's root application is run, such as `'DefaultAppPool'`
27
+ * `('protocols')` is a binding for the site, such as `'http'`. A site may have multiple bindings; therefore, use a `have_protocol` matcher for each site protocol to be tested
28
+ * `('physical_path') is the physical path to the application, such as `'C:\\inetpub\\wwwroot\\myapp'`
29
+
30
+ For example:
31
+
32
+ describe iis_app('/myapp', 'Default Web Site') do
33
+ it { should exist }
34
+ it { should have_application_pool('MyAppPool') }
35
+ it { should have_protocols('http') }
36
+ it { should have_site_name('Default Web Site') }
37
+ it { should have_physical_path('C:\\inetpub\\wwwroot\\myapp') }
38
+ it { should have_path('\\My Application') }
39
+ end
40
+
41
+ ## Matchers
42
+
43
+ This InSpec audit resource has the following matchers:
44
+
45
+ ### cmp
46
+
47
+ <%= partial "/shared/matcher_cmp" %>
48
+
49
+ ### eq
50
+
51
+ <%= partial "/shared/matcher_eq" %>
52
+
53
+ ### exist
54
+
55
+ The `exist` matcher tests if the site exists:
56
+
57
+ it { should exist }
58
+
59
+ ### have\_application\_pool
60
+
61
+ The `have_application_pool` matcher tests if the named application pool exists for the web application:
62
+
63
+ it { should have_application_pool('DefaultAppPool') }
64
+
65
+ ### have_protocol
66
+
67
+ The `have_protocol` matcher tests if the specified protocol exists for the web application:
68
+
69
+ it { should have_protocol('http') }
70
+
71
+ or:
72
+
73
+ it { should have_protocol('https') }
74
+
75
+ A web application may have multiple bindings; use a `have_protocol` matcher for each unique web application binding to be tested.
76
+
77
+ ##### Protocol Attributes
78
+
79
+ The `have_protocol` matcher can also test attributes that are defined for a web application enabledProtocols.
80
+
81
+ it { should have_protocol('http') }
82
+
83
+ For example, testing a site that doesn't have https enabled:
84
+
85
+ it { should_not have_protocol('https') }
86
+ it { should have_protocol('http') }
87
+
88
+ Testing a web application with https enabled and http enabled:
89
+
90
+ it { should have_protocol('https') }
91
+ it { should have_protocol('http') }
92
+
93
+ ### have_physical_path
94
+
95
+ The `have_physical_path` matcher tests if the named path is defined for the web application:
96
+
97
+ it { should have_physical_path('C:\\inetpub\\wwwroot') }
98
+
99
+ ### include
100
+
101
+ <%= partial "/shared/matcher_include" %>
102
+
103
+ ### match
104
+
105
+ <%= partial "/shared/matcher_match" %>
106
+
107
+ ## Examples
108
+
109
+ The following examples show how to use this InSpec audit resource.
110
+
111
+ ### Test a default IIS web application
112
+
113
+ describe iis_app('Default Web Site') do
114
+ it { should exist }
115
+ it { should be_running }
116
+ it { should have_app_pool('DefaultAppPool') }
117
+ it { should have_binding('http *:80:') }
118
+ it { should have_path('%SystemDrive%\\inetpub\\wwwroot') }
119
+ end
120
+
121
+ ### Test if IIS service is running
122
+
123
+ describe service('W3SVC') do
124
+ it { should be_installed }
125
+ it { should be_running }
126
+ end
@@ -0,0 +1,71 @@
1
+ ---
2
+ title: About the virtualization Resource
3
+ ---
4
+
5
+ # virtualization
6
+
7
+ Use the `virtualization` InSpec audit resource to test the virtualization platform on which the system is running.
8
+
9
+ ## Syntax
10
+
11
+ An `virtualization` resource block declares the virtualization platform that should be tested:
12
+
13
+ describe virtualization do
14
+ its('system') { should MATCHER 'value' }
15
+ end
16
+
17
+ where
18
+
19
+ * `('system')` is the name of the system information of the virtualization platform (e.g. docker, lxc, vbox, kvm, etc)
20
+ * `MATCHER` is a valid matcher for this resource
21
+ * `'value'` is the value to be tested
22
+
23
+ ## Matchers
24
+
25
+ This InSpec audit resource has the following matchers:
26
+
27
+ ### be
28
+
29
+ <%= partial "/shared/matcher_be" %>
30
+
31
+ ### cmp
32
+
33
+ <%= partial "/shared/matcher_cmp" %>
34
+
35
+ ### eq
36
+
37
+ <%= partial "/shared/matcher_eq" %>
38
+
39
+ ### include
40
+
41
+ <%= partial "/shared/matcher_include" %>
42
+
43
+ ### match
44
+
45
+ <%= partial "/shared/matcher_match" %>
46
+
47
+ ## Examples
48
+
49
+ The following examples show how to use this InSpec audit resource.
50
+
51
+ ### Test for Docker
52
+
53
+ describe virtualization do
54
+ its('system') { should eq 'docker' }
55
+ end
56
+
57
+ ### Test for VirtualBox
58
+
59
+ describe virtualization do
60
+ its('system') { should eq 'vbox' }
61
+ its('role') { should eq 'guest' }
62
+ end
63
+
64
+ ### Detect the virtualization platform
65
+
66
+ if virtualization.system == 'vbox'
67
+ describe package('name') do
68
+ it { should be_installed }
69
+ end
70
+ end
71
+
@@ -0,0 +1,11 @@
1
+ ---
2
+ lockfile_version: 1
3
+ depends:
4
+ - name: profile
5
+ resolved_source:
6
+ path: "/Users/aleff/projects/inspec/examples/profile"
7
+ version_constraints: ">= 0"
8
+ - name: profile-attribute
9
+ resolved_source:
10
+ path: "/Users/aleff/projects/inspec/examples/profile-attribute"
11
+ version_constraints: ">= 0"
@@ -0,0 +1,18 @@
1
+ ---
2
+ lockfile_version: 1
3
+ depends:
4
+ - name: dev-sec/ssh-baseline
5
+ resolved_source:
6
+ url: https://github.com/dev-sec/ssh-baseline/archive/master.tar.gz
7
+ sha256: 403580959915ea24bc176b9ebdc555aeda5e2c957604b48d5f32b43554423582
8
+ version_constraints: ">= 0"
9
+ - name: ssl-baseline
10
+ resolved_source:
11
+ url: https://github.com/dev-sec/ssl-baseline/archive/master.tar.gz
12
+ sha256: 0e6d170415e120af5f1dda113f96f7e0d156e49f82706ac41d13da00599f9b25
13
+ version_constraints: ">= 0"
14
+ - name: windows-patch-benchmark
15
+ resolved_source:
16
+ url: https://github.com/chris-rock/windows-patch-benchmark/archive/master.tar.gz
17
+ sha256: d08d3cc35debff04e708147cdd07739876c5d1c8357afb5e58adfaad92dd650f
18
+ version_constraints: ">= 0"
@@ -204,11 +204,22 @@ module Compliance
204
204
  end
205
205
 
206
206
  def self.is_automate_server_pre_080?(config)
207
- config['server_type'] == 'automate' && config['version'].nil?
207
+ # Automate versions before 0.8.x may have a "version" key in the config.
208
+ # Unless it's a hash that also contains a "version" key, it came from
209
+ # an Automate server that is pre-0.8.x.
210
+ return false unless config['server_type'] == 'automate'
211
+ return true unless config.key?('version')
212
+ return true unless config['version'].is_a?(Hash)
213
+ config['version']['version'].nil?
208
214
  end
209
215
 
210
216
  def self.is_automate_server_080_and_later?(config)
211
- config['server_type'] == 'automate' && !config['version'].nil?
217
+ # Automate versions 0.8.x and later will have a "version" key in the config
218
+ # that looks like: "version":{"api":"compliance","version":"0.8.24"}
219
+ return false unless config['server_type'] == 'automate'
220
+ return false unless config.key?('version')
221
+ return false unless config['version'].is_a?(Hash)
222
+ !config['version']['version'].nil?
212
223
  end
213
224
 
214
225
  def self.is_automate_server?(config)
@@ -28,6 +28,10 @@ module Compliance
28
28
  @config[key] = value
29
29
  end
30
30
 
31
+ def key?(key)
32
+ @config.key?(key)
33
+ end
34
+
31
35
  def clean
32
36
  @config = {}
33
37
  end
@@ -96,6 +96,8 @@ module Fetchers
96
96
  @insecure = opts['insecure']
97
97
  @token = opts['token']
98
98
  @config = opts
99
+ @archive_path = nil
100
+ @temp_archive_path = nil
99
101
  end
100
102
 
101
103
  def fetch(path)
@@ -17,7 +17,7 @@ module Inspec::Archive
17
17
  tar.mkdir(input_filename, stat.mode)
18
18
  else
19
19
  tar.add_file_simple(input_filename, stat.mode, stat.size) do |io|
20
- io.write(File.read(path))
20
+ io.write(File.binread(path))
21
21
  end
22
22
  end
23
23
  end
@@ -19,7 +19,7 @@ module Inspec
19
19
  desc: 'Specify the login port for a remote scan.'
20
20
  option :user, type: :string,
21
21
  desc: 'The login user for a remote scan.'
22
- option :password, type: :string,
22
+ option :password, type: :string, lazy_default: -1,
23
23
  desc: 'Login password for a remote scan, if required.'
24
24
  option :key_files, aliases: :i, type: :array,
25
25
  desc: 'Login key or certificate file for a remote scan.'
@@ -27,7 +27,7 @@ module Inspec
27
27
  desc: 'Login path to use when connecting to the target (WinRM).'
28
28
  option :sudo, type: :boolean,
29
29
  desc: 'Run scans with sudo. Only activates on Unix and non-root user.'
30
- option :sudo_password, type: :string,
30
+ option :sudo_password, type: :string, lazy_default: -1,
31
31
  desc: 'Specify a sudo password, if it is required.'
32
32
  option :sudo_options, type: :string,
33
33
  desc: 'Additional sudo options for a remote scan.'
@@ -100,6 +100,23 @@ module Inspec
100
100
  end
101
101
 
102
102
  def opts
103
+ o = merged_opts
104
+
105
+ # Due to limitations in Thor it is not possible to set an argument to be
106
+ # both optional and its value to be mandatory. E.g. the user supplying
107
+ # the --password argument is optional and not always required, but
108
+ # whenever it is used, it requires a value. Handle options that were
109
+ # defined above and require a value here:
110
+ %w{password sudo-password}.each do |v|
111
+ id = v.tr('-', '_').to_sym
112
+ next unless o[id] == -1
113
+ raise ArgumentError, "Please provide a value for --#{v}. For example: --#{v}=hello."
114
+ end
115
+
116
+ o
117
+ end
118
+
119
+ def merged_opts
103
120
  # argv overrides json
104
121
  Thor::CoreExt::HashWithIndifferentAccess.new(options_json.merge(options))
105
122
  end
@@ -24,12 +24,29 @@ module Inspec
24
24
  def initialize(_path)
25
25
  end
26
26
 
27
+ # List all files that are offered.
28
+ #
29
+ # @return [Array[String]] list of file paths that are included
30
+ def files
31
+ raise "Fetcher #{self} does not implement `files()`. This is required."
32
+ end
33
+
34
+ # Read the contents of a file. This will typically refer to a text
35
+ # file reading a string.
36
+ #
37
+ # @param _file [String] path of the file to be read
38
+ # @return [String] contents of the file described
27
39
  def read(_file)
28
40
  raise "#{self} does not implement `read(...)`. This is required."
29
41
  end
30
42
 
31
- def files
32
- raise "Fetcher #{self} does not implement `files()`. This is required."
43
+ # Provide a method for reading binary contents from a file.
44
+ # It will default to #read if not defined. For most streams that implement
45
+ # it, it will be the same. For some special cases, it will add change the
46
+ # way in which encoding of the returned data structure is handled.
47
+ # Does not work with alias nor alias_method.
48
+ def binread(file)
49
+ read(file)
33
50
  end
34
51
 
35
52
  def relative_provider
@@ -65,6 +82,12 @@ module Inspec
65
82
  return nil unless File.file?(file)
66
83
  File.read(file)
67
84
  end
85
+
86
+ def binread(file)
87
+ return nil unless files.include?(file)
88
+ return nil unless File.file?(file)
89
+ File.binread(file)
90
+ end
68
91
  end
69
92
 
70
93
  class ZipProvider < FileProvider
@@ -163,6 +186,10 @@ module Inspec
163
186
  parent.read(abs_path(file))
164
187
  end
165
188
 
189
+ def binread(file)
190
+ parent.binread(abs_path(file))
191
+ end
192
+
166
193
  private
167
194
 
168
195
  def get_prefix(fs)
@@ -58,7 +58,7 @@ module Inspec
58
58
  end
59
59
 
60
60
  def resource_skipped
61
- @resource_skipped
61
+ @resource_skipped if defined?(@resource_skipped)
62
62
  end
63
63
 
64
64
  def skip_resource(message)
@@ -37,7 +37,7 @@ module Inspec
37
37
  cache = file_provider.files.find_all do |entry|
38
38
  entry.start_with?('vendor')
39
39
  end
40
- content = Hash[cache.map { |x| [x, file_provider.read(x)] }]
40
+ content = Hash[cache.map { |x| [x, file_provider.binread(x)] }]
41
41
  keys = content.keys
42
42
  keys.each do |key|
43
43
  next if content[key].nil?
@@ -47,7 +47,7 @@ module Inspec
47
47
 
48
48
  FileUtils.mkdir_p tar.dirname.to_s
49
49
  Inspec::Log.debug "Copy #{tar} to cache directory"
50
- File.write(tar.to_s, content[key].force_encoding('UTF-8'))
50
+ File.binwrite(tar.to_s, content[key])
51
51
  end
52
52
  end
53
53
 
@@ -96,6 +96,7 @@ require 'resources/groups'
96
96
  require 'resources/grub_conf'
97
97
  require 'resources/host'
98
98
  require 'resources/http'
99
+ require 'resources/iis_app'
99
100
  require 'resources/iis_site'
100
101
  require 'resources/inetd_conf'
101
102
  require 'resources/interface'
@@ -138,6 +139,7 @@ require 'resources/ssh_conf'
138
139
  require 'resources/sys_info'
139
140
  require 'resources/users'
140
141
  require 'resources/vbscript'
142
+ require 'resources/virtualization'
141
143
  require 'resources/windows_feature'
142
144
  require 'resources/windows_task'
143
145
  require 'resources/xinetd'
@@ -52,7 +52,7 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
52
52
  format_example(example).tap do |hash|
53
53
  e = example.exception
54
54
  next unless e
55
- hash[:message] = e.message
55
+ hash[:message] = exception_message(e)
56
56
 
57
57
  next if e.is_a? RSpec::Expectations::ExpectationNotMetError
58
58
  hash[:exception] = e.class.name
@@ -63,6 +63,14 @@ class InspecRspecMiniJson < RSpec::Core::Formatters::JsonFormatter
63
63
 
64
64
  private
65
65
 
66
+ def exception_message(exception)
67
+ if exception.is_a?(RSpec::Core::MultipleExceptionError)
68
+ exception.all_exceptions.map(&:message).uniq.join("\n\n")
69
+ else
70
+ exception.message
71
+ end
72
+ end
73
+
66
74
  def format_example(example)
67
75
  if !example.metadata[:description_args].empty? && example.metadata[:skip]
68
76
  # For skipped profiles, rspec returns in full_description the skip_message as well. We don't want
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.27.0'.freeze
7
+ VERSION = '1.28.0'.freeze
8
8
  end
@@ -86,6 +86,7 @@ module Inspec::Resources
86
86
  @legacy = AuditdRulesLegacy.new(@content)
87
87
  else
88
88
  parse_content
89
+ @legacy = nil
89
90
  end
90
91
  end
91
92
 
@@ -10,7 +10,7 @@
10
10
  # end
11
11
  #
12
12
  # To verify a hostname with protocol and port
13
- # describe host('example.com', port: 53, proto: 'udp') do
13
+ # describe host('example.com', port: 443, protocol: 'tcp') do
14
14
  # it { should be_reachable }
15
15
  # end
16
16
  #
@@ -33,17 +33,26 @@ module Inspec::Resources
33
33
  it { should be_reachable }
34
34
  end
35
35
 
36
- describe host('example.com', port: '80') do
36
+ describe host('example.com', port: '80', protocol: 'tcp') do
37
37
  it { should be_reachable }
38
38
  end
39
39
  "
40
40
 
41
+ attr_reader :hostname, :port, :protocol
42
+
41
43
  def initialize(hostname, params = {})
42
44
  @hostname = hostname
43
- @port = params[:port] || nil
44
- @proto = params[:proto] || nil
45
+ @port = params[:port]
45
46
 
46
- return skip_resource 'The UDP protocol for the `host` resource is not supported yet.' if @proto == 'udp'
47
+ if params[:proto]
48
+ warn '[DEPRECATION] The `proto` parameter is deprecated. Use `protocol` instead.'
49
+ @protocol = params[:proto]
50
+ else
51
+ @protocol = params.fetch(:protocol, 'icmp')
52
+ end
53
+
54
+ return skip_resource 'Invalid protocol: only `tcp` and `icmp` protocols are support for the `host` resource.' unless
55
+ %w{icmp tcp}.include?(@protocol)
47
56
 
48
57
  @host_provider = nil
49
58
  if inspec.os.linux?
@@ -55,6 +64,16 @@ module Inspec::Resources
55
64
  else
56
65
  return skip_resource 'The `host` resource is not supported on your OS yet.'
57
66
  end
67
+
68
+ missing_requirements = @host_provider.missing_requirements(protocol)
69
+ unless missing_requirements.empty?
70
+ return skip_resource "The following requirements are not met for this resource: #{missing_requirements.join(', ')}"
71
+ end
72
+ end
73
+
74
+ def proto
75
+ warn '[DEPRECATION] The `proto` method is deprecated. Use `protocol` instead.'
76
+ protocol
58
77
  end
59
78
 
60
79
  # if we get the IP address, the host is resolvable
@@ -63,9 +82,25 @@ module Inspec::Resources
63
82
  resolve.nil? || resolve.empty? ? false : true
64
83
  end
65
84
 
66
- def reachable?(port = nil, proto = nil, timeout = nil)
67
- raise "Use `host` resource with host('#{@hostname}', port: #{port}, proto: '#{proto}') parameters." if !port.nil? || !proto.nil? || !timeout.nil?
68
- ping.nil? ? false : ping
85
+ def reachable?
86
+ # ping checks do not require port or protocol
87
+ return ping.fetch(:success, false) if protocol == 'icmp'
88
+
89
+ # if either port or protocol are specified but not both, we cannot proceed.
90
+ if port.nil? || protocol.nil?
91
+ raise "Protocol required with port. Use `host` resource with host('#{hostname}', port: 1234, proto: 'tcp') parameters." if port.nil? || protocol.nil?
92
+ end
93
+
94
+ # perform the protocol-specific reachability test
95
+ ping.fetch(:success, false)
96
+ end
97
+
98
+ def connection
99
+ ping[:connection]
100
+ end
101
+
102
+ def socket
103
+ ping[:socket]
69
104
  end
70
105
 
71
106
  # returns all A records of the IP address, will return an array
@@ -74,19 +109,21 @@ module Inspec::Resources
74
109
  end
75
110
 
76
111
  def to_s
77
- "Host #{@hostname}"
112
+ "Host #{hostname}"
78
113
  end
79
114
 
80
115
  private
81
116
 
82
117
  def ping
83
118
  return @ping_cache if defined?(@ping_cache)
84
- @ping_cache = @host_provider.ping(@hostname, @port, @proto) if !@host_provider.nil?
119
+ return {} if @host_provider.nil?
120
+
121
+ @ping_cache = @host_provider.ping(hostname, port, protocol)
85
122
  end
86
123
 
87
124
  def resolve
88
125
  return @ip_cache if defined?(@ip_cache)
89
- @ip_cache = @host_provider.resolve(@hostname) if !@host_provider.nil?
126
+ @ip_cache = @host_provider.resolve(hostname) if !@host_provider.nil?
90
127
  end
91
128
  end
92
129
 
@@ -95,16 +132,37 @@ module Inspec::Resources
95
132
  def initialize(inspec)
96
133
  @inspec = inspec
97
134
  end
135
+
136
+ def missing_requirements(_protocol)
137
+ # each provider can return an array of missing requirements that can
138
+ # be enumerated in a skip_resource message
139
+ []
140
+ end
98
141
  end
99
142
 
100
143
  class DarwinHostProvider < HostProvider
101
- def ping(hostname, port = nil, proto = nil)
102
- if proto == 'tcp'
144
+ def missing_requirements(protocol)
145
+ missing = []
146
+
147
+ if protocol == 'tcp'
148
+ missing << 'netcat must be installed' unless inspec.command('nc').exist?
149
+ end
150
+
151
+ missing
152
+ end
153
+
154
+ def ping(hostname, port, protocol)
155
+ if protocol == 'tcp'
103
156
  resp = inspec.command("nc -vz -G 1 #{hostname} #{port}")
104
157
  else
105
158
  resp = inspec.command("ping -W 1 -c 1 #{hostname}")
106
159
  end
107
- resp.exit_status.to_i != 0 ? false : true
160
+
161
+ {
162
+ success: resp.exit_status.to_i.zero?,
163
+ connection: resp.stderr,
164
+ socket: resp.stdout,
165
+ }
108
166
  end
109
167
 
110
168
  def resolve(hostname)
@@ -121,12 +179,29 @@ module Inspec::Resources
121
179
  end
122
180
 
123
181
  class LinuxHostProvider < HostProvider
124
- # ping is difficult to achieve, since we are not sure
125
- def ping(hostname, _port = nil, _proto = nil)
126
- # fall back to ping, but we can only test ICMP packages with ping
127
- # therefore we have to skip the test, if we do not have everything on the node to run the test
128
- ping = inspec.command("ping -w 1 -c 1 #{hostname}")
129
- ping.exit_status.to_i != 0 ? false : true
182
+ def missing_requirements(protocol)
183
+ missing = []
184
+
185
+ if protocol == 'tcp'
186
+ missing << 'netcat must be installed' unless inspec.command('nc').exist?
187
+ end
188
+
189
+ missing
190
+ end
191
+
192
+ def ping(hostname, port, protocol)
193
+ if protocol == 'tcp'
194
+ resp = inspec.command("echo | nc -v -w 1 #{hostname} #{port}")
195
+ else
196
+ # fall back to ping, but we can only test ICMP packages with ping
197
+ resp = inspec.command("ping -w 1 -c 1 #{hostname}")
198
+ end
199
+
200
+ {
201
+ success: resp.exit_status.to_i.zero?,
202
+ connection: resp.stderr,
203
+ socket: resp.stdout,
204
+ }
130
205
  end
131
206
 
132
207
  def resolve(hostname)
@@ -156,15 +231,10 @@ module Inspec::Resources
156
231
  begin
157
232
  ping = JSON.parse(cmd.stdout)
158
233
  rescue JSON::ParserError => _e
159
- return nil
234
+ return {}
160
235
  end
161
236
 
162
- # Logic being if you provided a port you wanted to check it was open
163
- if port.nil?
164
- ping['PingSucceeded']
165
- else
166
- ping['TcpTestSucceeded']
167
- end
237
+ { success: port.nil? ? ping['PingSucceeded'] : ping['TcpTestSucceeded'] }
168
238
  end
169
239
 
170
240
  def resolve(hostname)
@@ -0,0 +1,103 @@
1
+ # encoding: utf-8
2
+ # frozen_string_literal: true
3
+ # check for web applications in IIS
4
+ # Note: this is only supported in windows 2012 and later
5
+ module Inspec::Resources
6
+ class IisApp < Inspec.resource(1)
7
+ name 'iis_app'
8
+ desc 'Tests IIS application configuration on windows. Supported in server 2012+ only'
9
+ example "
10
+ describe iis_app('/myapp', 'Default Web Site') do
11
+ it { should exist }
12
+ it { should have_application_pool('MyAppPool') }
13
+ it { should have_protocols('http') }
14
+ it { should have_site_name('Default Web Site') }
15
+ it { should have_physical_path('C:\\inetpub\\wwwroot\\myapp') }
16
+ it { should have_path('\\My Application') }
17
+ end
18
+ "
19
+
20
+ def initialize(path, site_name)
21
+ @path = path
22
+ @site_name = site_name
23
+ @cache = nil
24
+ @inspec = inspec
25
+
26
+ # verify that this resource is only supported on Windows
27
+ return skip_resource 'The `iis_app` resource is not supported on your OS.' unless inspec.os.windows?
28
+ end
29
+
30
+ def application_pool
31
+ iis_app[:application_pool]
32
+ end
33
+
34
+ def protocols
35
+ iis_app[:protocols]
36
+ end
37
+
38
+ def site_name
39
+ iis_app[:site_name]
40
+ end
41
+
42
+ def path
43
+ iis_app[:path]
44
+ end
45
+
46
+ def physical_path
47
+ iis_app[:physical_path]
48
+ end
49
+
50
+ def exists?
51
+ !iis_app[:path].empty?
52
+ end
53
+
54
+ def has_site_name?(site_name)
55
+ iis_app[:site_name] == site_name
56
+ end
57
+
58
+ def has_application_pool?(application_pool)
59
+ iis_app[:application_pool] == application_pool
60
+ end
61
+
62
+ def has_path?(path)
63
+ iis_app[:path] == path
64
+ end
65
+
66
+ def has_physical_path?(physical_path)
67
+ iis_app[:physical_path] == physical_path
68
+ end
69
+
70
+ def has_protocol?(protocol)
71
+ iis_app[:protocols].include?(protocol)
72
+ end
73
+
74
+ def to_s
75
+ "iis_app '#{@site_name}#{@path}'"
76
+ end
77
+
78
+ private
79
+
80
+ def iis_app
81
+ return @cache unless @cache.nil?
82
+ command = "Import-Module WebAdministration; Get-WebApplication -Name '#{@path}' -Site '#{@site_name}' | Select-Object * | ConvertTo-Json"
83
+ cmd = @inspec.command(command)
84
+
85
+ begin
86
+ app = JSON.parse(cmd.stdout)
87
+ rescue JSON::ParserError => _e
88
+ return {}
89
+ end
90
+
91
+ # map our values to a hash table
92
+ info = {
93
+ site_name: @site_name,
94
+ path: @path,
95
+ application_pool: app['applicationPool'],
96
+ physical_path: app['PhysicalPath'],
97
+ protocols: app['enabledProtocols'],
98
+ }
99
+
100
+ @cache = info unless info.nil?
101
+ end
102
+ end
103
+ end
@@ -151,6 +151,8 @@ module Inspec::Resources
151
151
  BSDInit.new(inspec, service_ctl)
152
152
  elsif %w{arch}.include?(platform)
153
153
  Systemd.new(inspec, service_ctl)
154
+ elsif %w{coreos}.include?(platform)
155
+ Systemd.new(inspec, service_ctl)
154
156
  elsif %w{suse opensuse}.include?(platform)
155
157
  if os[:release].to_i >= 12
156
158
  Systemd.new(inspec, service_ctl)
@@ -0,0 +1,252 @@
1
+ # encoding: utf-8
2
+ # author: Takaaki Furukawa
3
+
4
+ require 'hashie/mash'
5
+
6
+ module Inspec::Resources
7
+ class Virtualization < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
8
+ name 'virtualization'
9
+ desc 'Use the virtualization InSpec audit resource to test the virtualization platform on which the system is running'
10
+ example "
11
+ describe virtualization do
12
+ its('system') { should eq 'docker' }
13
+ end
14
+
15
+ describe virtualization do
16
+ its('role') { should eq 'guest' }
17
+ end
18
+
19
+ control 'test' do
20
+ describe file('/var/tmp/foo') do
21
+ it { should be_file }
22
+ end
23
+ only_if { virtualization.system == 'docker' }
24
+ end
25
+ "
26
+
27
+ def initialize
28
+ unless inspec.os.linux?
29
+ skip_resource 'The `virtualization` resource is not supported on your OS yet.'
30
+ else
31
+ collect_data_linux
32
+ end
33
+ end
34
+
35
+ # add helper methods for easy access of properties
36
+ # allows users to use virtualization.role, virtualization.system
37
+ %w{role system}.each do |property|
38
+ define_method(property.to_sym) do
39
+ @virtualization_data[property.to_sym]
40
+ end
41
+ end
42
+
43
+ def params
44
+ collect_data_linux
45
+ end
46
+
47
+ def to_s
48
+ 'Virtualization Detection'
49
+ end
50
+
51
+ private
52
+
53
+ def lxc_version_exists?
54
+ inspec.command('lxc-version').exist?
55
+ end
56
+
57
+ def docker_exists?
58
+ inspec.command('docker').exist?
59
+ end
60
+
61
+ def nova_exists?
62
+ inspec.command('nova').exist?
63
+ end
64
+
65
+ # Detect Xen
66
+ # /proc/xen is an empty dir for EL6 + Linode Guests + Paravirt EC2 instances
67
+ # Notes:
68
+ # - cpuid of guests, if we could get it, would also be a clue
69
+ # - may be able to determine if under paravirt from /dev/xen/evtchn (See OHAI-253)
70
+ # - Additional edge cases likely should not change the above assumptions
71
+ # but rather be additive - btm
72
+ def detect_xen
73
+ return false unless inspec.file('/proc/xen').exist?
74
+ @virtualization_data[:system] = 'xen'
75
+ @virtualization_data[:role] = 'guest'
76
+
77
+ # This file should exist on most Xen systems, normally empty for guests
78
+ if inspec.file('/proc/xen/capabilities').exist? &&
79
+ inspec.file('/proc/xen/capabilities').content =~ /control_d/i # rubocop:disable Style/MultilineOperationIndentation
80
+ @virtualization_data[:role] = 'host'
81
+ end
82
+ true
83
+ end
84
+
85
+ # Detect Virtualbox from kernel module
86
+ def detect_virtualbox
87
+ return false unless inspec.file('/proc/modules').exist?
88
+ modules = inspec.file('/proc/modules').content
89
+ if modules =~ /^vboxdrv/
90
+ Inspec::Log.debug('Plugin Virtualization: /proc/modules contains vboxdrv. Detecting as vbox host')
91
+ @virtualization_data[:system] = 'vbox'
92
+ @virtualization_data[:role] = 'host'
93
+ elsif modules =~ /^vboxguest/
94
+ Inspec::Log.debug('Plugin Virtualization: /proc/modules contains vboxguest. Detecting as vbox guest')
95
+ @virtualization_data[:system] = 'vbox'
96
+ @virtualization_data[:role] = 'guest'
97
+ else
98
+ return false
99
+ end
100
+ true
101
+ end
102
+
103
+ # if nova binary is present we're on an openstack host
104
+ def detect_openstack
105
+ return false unless nova_exists?
106
+ @virtualization_data[:system] = 'openstack'
107
+ @virtualization_data[:role] = 'host'
108
+ true
109
+ end
110
+
111
+ # Detect paravirt KVM/QEMU from cpuinfo, report as KVM
112
+ def detect_kvm_from_cpuinfo
113
+ return false unless inspec.file('/proc/cpuinfo').content =~ /QEMU Virtual CPU|Common KVM processor|Common 32-bit KVM processor/
114
+ @virtualization_data[:system] = 'kvm'
115
+ @virtualization_data[:role] = 'guest'
116
+ true
117
+ end
118
+
119
+ # Detect KVM systems via /sys
120
+ # guests will have the hypervisor cpu feature that hosts don't have
121
+ def detect_kvm_from_sys
122
+ return false unless inspec.file('/sys/devices/virtual/misc/kvm').exist?
123
+ if inspec.file('/proc/cpuinfo').content =~ /hypervisor/
124
+ @virtualization_data[:system] = 'kvm'
125
+ @virtualization_data[:role] = 'guest'
126
+ else
127
+ @virtualization_data[:system] = 'kvm'
128
+ @virtualization_data[:role] = 'host'
129
+ end
130
+ true
131
+ end
132
+
133
+ # Detect OpenVZ / Virtuozzo.
134
+ # http://wiki.openvz.org/BC_proc_entries
135
+ def detect_openvz
136
+ if inspec.file('/proc/bc/0').exist?
137
+ @virtualization_data[:system] = 'openvz'
138
+ @virtualization_data[:role] = 'host'
139
+ elsif inspec.file('/proc/vz').exist?
140
+ @virtualization_data[:system] = 'openvz'
141
+ @virtualization_data[:role] = 'guest'
142
+ else
143
+ return false
144
+ end
145
+ true
146
+ end
147
+
148
+ # Detect Parallels virtual machine from pci devices
149
+ def detect_parallels
150
+ return false unless inspec.file('/proc/bus/pci/devices').content =~ /1ab84000/
151
+ @virtualization_data[:system] = 'parallels'
152
+ @virtualization_data[:role] = 'guest'
153
+ true
154
+ end
155
+
156
+ # Detect Linux-VServer
157
+ def detect_linux_vserver
158
+ return false unless inspec.file('/proc/self/status').exist?
159
+ proc_self_status = inspec.file('/proc/self/status').content
160
+ vxid = proc_self_status.match(/^(s_context|VxID):\s*(\d+)$/)
161
+ return false unless vxid && vxid[2]
162
+ @virtualization_data[:system] = 'linux-vserver'
163
+ if vxid[2] == '0'
164
+ @virtualization_data[:role] = 'host'
165
+ else
166
+ @virtualization_data[:role] = 'guest'
167
+ end
168
+ true
169
+ end
170
+
171
+ # Detect LXC/Docker
172
+ #
173
+ # /proc/self/cgroup will look like this inside a docker container:
174
+ # <index #>:<subsystem>:/lxc/<hexadecimal container id>
175
+ #
176
+ # /proc/self/cgroup could have a name including alpha/digit/dashes
177
+ # <index #>:<subsystem>:/lxc/<named container id>
178
+ #
179
+ # /proc/self/cgroup could have a non-lxc cgroup name indicating other uses
180
+ # of cgroups. This is probably not LXC/Docker.
181
+ # <index #>:<subsystem>:/Charlie
182
+ #
183
+ # A host which supports cgroups, and has capacity to host lxc containers,
184
+ # will show the subsystems and root (/) namespace.
185
+ # <index #>:<subsystem>:/
186
+ #
187
+ # Full notes, https://tickets.opscode.com/browse/OHAI-551
188
+ # Kernel docs, https://www.kernel.org/doc/Documentation/cgroups
189
+ def detect_lxc_docker
190
+ return false unless inspec.file('/proc/self/cgroup').exist?
191
+ cgroup_content = inspec.file('/proc/self/cgroup').content
192
+ if cgroup_content =~ %r{^\d+:[^:]+:/(lxc|docker)/.+$} ||
193
+ cgroup_content =~ %r{^\d+:[^:]+:/[^/]+/(lxc|docker)-.+$} # rubocop:disable Style/MultilineOperationIndentation
194
+ @virtualization_data[:system] = $1 # rubocop:disable Style/PerlBackrefs
195
+ @virtualization_data[:role] = 'guest'
196
+ elsif lxc_version_exists? && cgroup_content =~ %r{\d:[^:]+:/$}
197
+ # lxc-version shouldn't be installed by default
198
+ # Even so, it is likely we are on an LXC capable host that is not being used as such
199
+ # So we're cautious here to not overwrite other existing values (OHAI-573)
200
+ unless @virtualization_data[:system] && @virtualization_data[:role]
201
+ @virtualization_data[:system] = 'lxc'
202
+ @virtualization_data[:role] = 'host'
203
+ end
204
+ else
205
+ return false
206
+ end
207
+ true
208
+ end
209
+
210
+ def detect_docker
211
+ return false unless inspec.file('/.dockerenv').exist? || inspec.file('/.dockerinit').exist?
212
+ @virtualization_data[:system] = 'docker'
213
+ @virtualization_data[:role] = 'guest'
214
+ true
215
+ end
216
+
217
+ # Detect LXD
218
+ # See https://github.com/lxc/lxd/blob/master/doc/dev-lxd.md
219
+ def detect_lxd
220
+ if inspec.file('/dev/lxd/sock').exist?
221
+ @virtualization_data[:system] = 'lxd'
222
+ @virtualization_data[:role] = 'guest'
223
+ elsif inspec.file('/var/lib/lxd/devlxd').exist?
224
+ @virtualization_data[:system] = 'lxd'
225
+ @virtualization_data[:role] = 'host'
226
+ else
227
+ return false
228
+ end
229
+ true
230
+ end
231
+
232
+ def collect_data_linux # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
233
+ # cache data in an instance var to avoid doing multiple detections for a single test
234
+ @virtualization_data ||= Hashie::Mash.new
235
+ return unless @virtualization_data.empty?
236
+
237
+ # each detect method will return true if it matched and was successfully
238
+ # able to populate @virtualization_data with stuff.
239
+ return if detect_xen
240
+ return if detect_virtualbox
241
+ return if detect_openstack
242
+ return if detect_kvm_from_cpuinfo
243
+ return if detect_kvm_from_sys
244
+ return if detect_openvz
245
+ return if detect_parallels
246
+ return if detect_linux_vserver
247
+ return if detect_lxc_docker
248
+ return if detect_docker
249
+ return if detect_lxd
250
+ end
251
+ end
252
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.27.0
4
+ version: 1.28.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-06-06 00:00:00.000000000 Z
11
+ date: 2017-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train
@@ -305,6 +305,7 @@ files:
305
305
  - docs/migration.md
306
306
  - docs/plugin_kitchen_inspec.md
307
307
  - docs/profiles.md
308
+ - docs/resources.md
308
309
  - docs/resources/apache_conf.md.erb
309
310
  - docs/resources/apt.md.erb
310
311
  - docs/resources/audit_policy.md.erb
@@ -331,6 +332,7 @@ files:
331
332
  - docs/resources/grub_conf.md.erb
332
333
  - docs/resources/host.md.erb
333
334
  - docs/resources/http.md.erb
335
+ - docs/resources/iis_app.md.erb
334
336
  - docs/resources/iis_site.md.erb
335
337
  - docs/resources/inetd_conf.md.erb
336
338
  - docs/resources/ini.md.erb
@@ -377,6 +379,7 @@ files:
377
379
  - docs/resources/user.md.erb
378
380
  - docs/resources/users.md.erb
379
381
  - docs/resources/vbscript.md.erb
382
+ - docs/resources/virtualization.md.erb
380
383
  - docs/resources/windows_feature.md.erb
381
384
  - docs/resources/windows_task.md.erb
382
385
  - docs/resources/wmi.md.erb
@@ -396,6 +399,7 @@ files:
396
399
  - examples/README.md
397
400
  - examples/inheritance/README.md
398
401
  - examples/inheritance/controls/example.rb
402
+ - examples/inheritance/inspec.lock
399
403
  - examples/inheritance/inspec.yml
400
404
  - examples/kitchen-ansible/.kitchen.yml
401
405
  - examples/kitchen-ansible/Gemfile
@@ -421,7 +425,11 @@ files:
421
425
  - examples/kitchen-puppet/test/integration/default/web_spec.rb
422
426
  - examples/meta-profile/README.md
423
427
  - examples/meta-profile/controls/example.rb
428
+ - examples/meta-profile/inspec.lock
424
429
  - examples/meta-profile/inspec.yml
430
+ - examples/meta-profile/vendor/0e6d170415e120af5f1dda113f96f7e0d156e49f82706ac41d13da00599f9b25.tar.gz
431
+ - examples/meta-profile/vendor/403580959915ea24bc176b9ebdc555aeda5e2c957604b48d5f32b43554423582.tar.gz
432
+ - examples/meta-profile/vendor/d08d3cc35debff04e708147cdd07739876c5d1c8357afb5e58adfaad92dd650f.tar.gz
425
433
  - examples/profile-attribute.yml
426
434
  - examples/profile-attribute/README.md
427
435
  - examples/profile-attribute/controls/example.rb
@@ -558,6 +566,7 @@ files:
558
566
  - lib/resources/grub_conf.rb
559
567
  - lib/resources/host.rb
560
568
  - lib/resources/http.rb
569
+ - lib/resources/iis_app.rb
561
570
  - lib/resources/iis_site.rb
562
571
  - lib/resources/inetd_conf.rb
563
572
  - lib/resources/ini.rb
@@ -601,6 +610,7 @@ files:
601
610
  - lib/resources/sys_info.rb
602
611
  - lib/resources/users.rb
603
612
  - lib/resources/vbscript.rb
613
+ - lib/resources/virtualization.rb
604
614
  - lib/resources/windows_feature.rb
605
615
  - lib/resources/windows_task.rb
606
616
  - lib/resources/wmi.rb