inspec 1.27.0 → 1.28.0

Sign up to get free protection for your applications and to get access to all the features.
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