inspec 1.41.0 → 1.42.3

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: 111ac2cdb0069f50d575dfd47e2644489c3b8296
4
- data.tar.gz: b989512dea168e62bc8092c1e3df498a706badd3
3
+ metadata.gz: 6253ad47423d8b2a7bfc56409b61d31c0065aa6e
4
+ data.tar.gz: 7e3e02b74a6a6af95e62c09b62e949bf95eea242
5
5
  SHA512:
6
- metadata.gz: 1cca620d9852d2a6369bbc0bec4663514abcd80e102a7d7b75a3f62f61c1438df5810bc25d81453fda09cd1b72f59ab648bea045b72c95b42da7211b5dda3c4a
7
- data.tar.gz: efa759491e6cadb78ee7f97a2e626663a18548f7f3d60db9fc04d431ad19c5fe11f9427a0a124a05abca26242c12fefd54951b395dc30683253d72dbdc8ef88c
6
+ metadata.gz: 85007c9bc4574c090be7315dc133d21a364afd72af9115ea470fe026aeed18443d50271523ce2ea64d5c729315a0f0dd4433b807b23f4285ce3b8a1537174274
7
+ data.tar.gz: 8a1cc9ab25dd32116be49e3a05c67234040cb47d7db36629ff2fe38bfdb85684b3a9a9846900ea997054c8ac2ce140f4e27e0f4b5998980714382446e6d0a6de
@@ -1,41 +1,58 @@
1
1
  # Change Log
2
2
  <!-- usage documentation: http://expeditor-docs.es.chef.io/configuration/changelog/ -->
3
- <!-- latest_release 1.40.13 -->
4
- ## [v1.40.13](https://github.com/chef/inspec/tree/v1.40.13) (2017-10-07)
3
+ <!-- latest_release 1.42.3 -->
4
+ ## [v1.42.3](https://github.com/chef/inspec/tree/v1.42.3) (2017-10-18)
5
5
 
6
6
  #### Enhancements
7
- - Enhance cmp matcher to work with symbols, fix file documentation [#2224](https://github.com/chef/inspec/pull/2224) ([adamleff](https://github.com/adamleff))
7
+ - windows_hotfix resource: Replace WMI query with PowerShell cmdlet &quot;get-hotfix&quot; [#2252](https://github.com/chef/inspec/pull/2252) ([mattray](https://github.com/mattray))
8
8
  <!-- latest_release -->
9
9
 
10
- <!-- release_rollup since=1.40.0 -->
11
- ### Changes since 1.40.0 release
10
+ <!-- release_rollup since=1.41.0 -->
11
+ ### Changes since 1.41.0 release
12
+
13
+ #### Merged Pull Requests
14
+ - Squashed some unit test warnings [#2242](https://github.com/chef/inspec/pull/2242) ([username-is-already-taken2](https://github.com/username-is-already-taken2)) <!-- 1.41.9 -->
15
+ - Fix documentation of `split` matcher [#2240](https://github.com/chef/inspec/pull/2240) ([eramoto](https://github.com/eramoto)) <!-- 1.41.4 -->
16
+ - Update the profile tempate [#2238](https://github.com/chef/inspec/pull/2238) ([nathenharvey](https://github.com/nathenharvey)) <!-- 1.41.3 -->
12
17
 
13
18
  #### Bug Fixes
14
- - ssl resource: properly raise error when unable to determine if port is enabled [#2205](https://github.com/chef/inspec/pull/2205) ([jquick](https://github.com/jquick)) <!-- 1.40.12 -->
15
- - Fix loading profile files when executing multiple profiles [#2223](https://github.com/chef/inspec/pull/2223) ([adamleff](https://github.com/adamleff)) <!-- 1.40.11 -->
16
- - Support symbol keys in ObjectTraverser [#2221](https://github.com/chef/inspec/pull/2221) ([adamleff](https://github.com/adamleff)) <!-- 1.40.8 -->
17
- - Add nil check for sshd config file [#2217](https://github.com/chef/inspec/pull/2217) ([jquick](https://github.com/jquick)) <!-- 1.40.7 -->
19
+ - Fix `only_if` behavior when used outside controls [#2216](https://github.com/chef/inspec/pull/2216) ([jerryaldrichiii](https://github.com/jerryaldrichiii)) <!-- 1.41.8 -->
20
+ - Fix port ressource ss line parsing [#2243](https://github.com/chef/inspec/pull/2243) ([narkaTee](https://github.com/narkaTee)) <!-- 1.41.7 -->
21
+ - Support PAX-formatted tar files, standardize file lists [#2225](https://github.com/chef/inspec/pull/2225) ([adamleff](https://github.com/adamleff)) <!-- 1.41.2 -->
22
+ - Fix typo in error message in postgres resource [#2248](https://github.com/chef/inspec/pull/2248) ([rndmh3ro](https://github.com/rndmh3ro)) <!-- 1.42.2 -->
23
+ - Resolve the weird encoding issue within inspec shell [#2234](https://github.com/chef/inspec/pull/2234) ([username-is-already-taken2](https://github.com/username-is-already-taken2)) <!-- 1.41.10 -->
18
24
 
19
25
  #### Enhancements
20
- - Enhance cmp matcher to work with symbols, fix file documentation [#2224](https://github.com/chef/inspec/pull/2224) ([adamleff](https://github.com/adamleff)) <!-- 1.40.13 -->
21
- - processes resource: support busybox ps [#2222](https://github.com/chef/inspec/pull/2222) ([adamleff](https://github.com/adamleff)) <!-- 1.40.10 -->
22
- - Update shell resource help to return what is defined [#2219](https://github.com/chef/inspec/pull/2219) ([jquick](https://github.com/jquick)) <!-- 1.40.9 -->
23
- - Add output for port/protocol for host resource. [#2202](https://github.com/chef/inspec/pull/2202) ([jquick](https://github.com/jquick)) <!-- 1.40.3 -->
24
-
25
- #### Merged Pull Requests
26
- - Add Segment tag to enable Google Analytics [#2220](https://github.com/chef/inspec/pull/2220) ([hamburglar](https://github.com/hamburglar)) <!-- 1.40.6 -->
27
- - http resource: properly execute tests on remote target [#2209](https://github.com/chef/inspec/pull/2209) ([adamleff](https://github.com/adamleff)) <!-- 1.40.5 -->
28
- - Adding examples of using expect syntax [#2213](https://github.com/chef/inspec/pull/2213) ([adamleff](https://github.com/adamleff)) <!-- 1.40.4 -->
29
- - Add bsd platform family to etc_hosts resource [#2192](https://github.com/chef/inspec/pull/2192) ([ctbarrett](https://github.com/ctbarrett)) <!-- 1.40.2 -->
30
- - Clean-up kitchen-inspec reference doc [#2208](https://github.com/chef/inspec/pull/2208) ([nathenharvey](https://github.com/nathenharvey)) <!-- 1.40.1 -->
26
+ - windows_hotfix resource: Replace WMI query with PowerShell cmdlet &quot;get-hotfix&quot; [#2252](https://github.com/chef/inspec/pull/2252) ([mattray](https://github.com/mattray)) <!-- 1.42.3 -->
27
+ - Extend Windows ACL matchers [#1744](https://github.com/chef/inspec/pull/1744) ([TheLonelyGhost](https://github.com/TheLonelyGhost)) <!-- 1.42.1 -->
28
+ - Add inspec habitat profile setup command [#2239](https://github.com/chef/inspec/pull/2239) ([adamleff](https://github.com/adamleff)) <!-- 1.42.0 -->
29
+ - Add missed &#39;html&#39; to &#39;format&#39; option explanation and arrange formatters in alphabetical order [#2244](https://github.com/chef/inspec/pull/2244) ([strangeman](https://github.com/strangeman)) <!-- 1.41.6 -->
30
+ - Uses netstat to detect open ports on AIX [#2210](https://github.com/chef/inspec/pull/2210) ([cattywampus](https://github.com/cattywampus)) <!-- 1.41.1 -->
31
+ - etc_fstab resource: properly namespace the resource, add nfs_file_systems documentation [#2190](https://github.com/chef/inspec/pull/2190) ([jburns12](https://github.com/jburns12)) <!-- 1.41.5 -->
31
32
  <!-- release_rollup -->
32
33
 
33
34
  <!-- latest_stable_release -->
35
+ ## [v1.41.0](https://github.com/chef/inspec/tree/v1.41.0) (2017-10-09)
36
+
37
+ #### Enhancements
38
+ - Add bsd platform family to etc_hosts resource [#2192](https://github.com/chef/inspec/pull/2192) ([ctbarrett](https://github.com/ctbarrett))
39
+ - http resource: properly execute tests on remote target [#2209](https://github.com/chef/inspec/pull/2209) ([adamleff](https://github.com/adamleff))
40
+ - Add output for port/protocol for host resource. [#2202](https://github.com/chef/inspec/pull/2202) ([jquick](https://github.com/jquick))
41
+ - Update shell resource help to return what is defined [#2219](https://github.com/chef/inspec/pull/2219) ([jquick](https://github.com/jquick))
42
+ - processes resource: support busybox ps [#2222](https://github.com/chef/inspec/pull/2222) ([adamleff](https://github.com/adamleff))
43
+ - Enhance cmp matcher to work with symbols, fix file documentation [#2224](https://github.com/chef/inspec/pull/2224) ([adamleff](https://github.com/adamleff))
44
+
45
+ #### Bug Fixes
46
+ - Add nil check for sshd config file [#2217](https://github.com/chef/inspec/pull/2217) ([jquick](https://github.com/jquick))
47
+ - Support symbol keys in ObjectTraverser [#2221](https://github.com/chef/inspec/pull/2221) ([adamleff](https://github.com/adamleff))
48
+ - Fix loading profile files when executing multiple profiles [#2223](https://github.com/chef/inspec/pull/2223) ([adamleff](https://github.com/adamleff))
49
+ - ssl resource: properly raise error when unable to determine if port is enabled [#2205](https://github.com/chef/inspec/pull/2205) ([jquick](https://github.com/jquick))
50
+ <!-- latest_stable_release -->
51
+
34
52
  ## [v1.40.0](https://github.com/chef/inspec/tree/v1.40.0) (2017-09-28)
35
53
 
36
54
  #### New Resources
37
55
  - firewalld resource: inspect the status and configuration of firewalld [#2074](https://github.com/chef/inspec/pull/2074) ([dromazmj](https://github.com/dromazmj))
38
- <!-- latest_stable_release -->
39
56
 
40
57
  ## [v1.39.0](https://github.com/chef/inspec/tree/v1.39.0) (2017-09-25)
41
58
 
@@ -100,7 +100,7 @@ The following examples show how to use this InSpec resource.
100
100
 
101
101
  ### Check all partitions that have type of 'nfs'.
102
102
 
103
- nfs_systems = etc_fstab.nfs_file_systems
103
+ nfs_systems = etc_fstab.nfs_file_systems.entries
104
104
  nfs_systems.each do |partition|
105
105
  describe partition do
106
106
  its('mount_options') { should include 'nosuid' }
@@ -70,14 +70,8 @@ The `content` matcher return the value of the environment variable:
70
70
 
71
71
  ### split
72
72
 
73
- The `split` splits the content with the `:` deliminator:
73
+ The `split` matcher splits the value of the environment variable with the `:` deliminator (use the `;` deliminator if Windows):
74
74
 
75
- its('split') { should include (':') }
75
+ its('split') { should include ('/usr/bin') }
76
76
 
77
- or:
78
-
79
- its('split') { should_not include ('.') }
80
-
81
- Use `-1` to test for cases where there is a trailing colon (`:`), such as `dir1::dir2:`:
82
-
83
- its('split') { should include ('-1') }
77
+ Note: the `split` matcher returns an array including `""` for cases where there is a trailing colon (`:`), such as `dir1::dir2:`
@@ -7,14 +7,19 @@ module Habitat
7
7
  class HabitatProfileCLI < Thor
8
8
  namespace 'habitat profile'
9
9
 
10
- desc 'create PATH', 'Create a Habitat artifact for the profile found at PATH'
10
+ desc 'create PATH', 'Create a one-time Habitat artifact for the profile found at PATH'
11
11
  option :output_dir, type: :string, required: false,
12
12
  desc: 'Directory in which to save the generated Habitat artifact. Default: current directory'
13
13
  def create(path)
14
14
  Habitat::Profile.create(path, options)
15
15
  end
16
16
 
17
- desc 'upload PATH', 'Create a Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
17
+ desc 'setup PATH', 'Configure the profile at PATH for Habitat, including a plan and hooks'
18
+ def setup(path)
19
+ Habitat::Profile.setup(path)
20
+ end
21
+
22
+ desc 'upload PATH', 'Create a one-time Habitat artifact for the profile found at PATH, and upload it to a Habitat Depot'
18
23
  def upload(path)
19
24
  Habitat::Profile.upload(path, options)
20
25
  end
@@ -17,6 +17,10 @@ module Habitat
17
17
  creator.delete_work_dir
18
18
  end
19
19
 
20
+ def self.setup(path)
21
+ new(path).setup
22
+ end
23
+
20
24
  def self.upload(path, options = {})
21
25
  uploader = new(path, options)
22
26
  uploader.upload
@@ -41,10 +45,11 @@ module Habitat
41
45
  verify_profile
42
46
  vendor_profile_dependencies
43
47
  copy_profile_to_work_dir
44
- create_plan
45
- create_run_hook
46
- create_settings_file
47
- create_default_config
48
+ create_habitat_directories(work_dir)
49
+ create_plan(work_dir)
50
+ create_run_hook(work_dir)
51
+ create_settings_file(work_dir)
52
+ create_default_config(work_dir)
48
53
 
49
54
  # returns the path to the .hart file in the work directory
50
55
  build_hart
@@ -80,6 +85,18 @@ module Habitat
80
85
  FileUtils.rm_rf(work_dir) if Dir.exist?(work_dir)
81
86
  end
82
87
 
88
+ def setup
89
+ Habitat::Log.info("Setting up profile at #{path} for Habitat...")
90
+ create_profile_object
91
+ verify_profile
92
+ vendor_profile_dependencies
93
+ create_habitat_directories(path)
94
+ create_plan(path)
95
+ create_run_hook(path)
96
+ create_settings_file(path)
97
+ create_default_config(path)
98
+ end
99
+
83
100
  private
84
101
 
85
102
  def create_profile_object
@@ -151,20 +168,26 @@ module Habitat
151
168
  return @work_dir if @work_dir
152
169
 
153
170
  @work_dir ||= Dir.mktmpdir('inspec-habitat-exporter')
154
- Dir.mkdir(File.join(@work_dir, 'src'))
155
- Dir.mkdir(File.join(@work_dir, 'habitat'))
156
- Dir.mkdir(File.join(@work_dir, 'habitat', 'config'))
157
- Dir.mkdir(File.join(@work_dir, 'habitat', 'hooks'))
158
171
  Habitat::Log.debug("Generated work directory #{@work_dir}")
159
172
 
160
173
  @work_dir
161
174
  end
162
175
 
176
+ def create_habitat_directories(parent_directory)
177
+ [
178
+ File.join(parent_directory, 'habitat'),
179
+ File.join(parent_directory, 'habitat', 'config'),
180
+ File.join(parent_directory, 'habitat', 'hooks'),
181
+ ].each do |dir|
182
+ Dir.mkdir(dir) unless Dir.exist?(dir)
183
+ end
184
+ end
185
+
163
186
  def copy_profile_to_work_dir
164
187
  Habitat::Log.info('Copying profile contents to the work directory...')
165
188
  profile.files.each do |f|
166
189
  src = File.join(profile.root_path, f)
167
- dst = File.join(work_dir, 'src', f)
190
+ dst = File.join(work_dir, f)
168
191
  if File.directory?(f)
169
192
  Habitat::Log.debug("Creating directory #{dst}")
170
193
  FileUtils.mkdir_p(dst)
@@ -175,26 +198,26 @@ module Habitat
175
198
  end
176
199
  end
177
200
 
178
- def create_plan
179
- plan_file = File.join(work_dir, 'habitat', 'plan.sh')
201
+ def create_plan(directory)
202
+ plan_file = File.join(directory, 'habitat', 'plan.sh')
180
203
  Habitat::Log.info("Generating Habitat plan at #{plan_file}...")
181
204
  File.write(plan_file, plan_contents)
182
205
  end
183
206
 
184
- def create_run_hook
185
- run_hook_file = File.join(work_dir, 'habitat', 'hooks', 'run')
207
+ def create_run_hook(directory)
208
+ run_hook_file = File.join(directory, 'habitat', 'hooks', 'run')
186
209
  Habitat::Log.info("Generating a Habitat run hook at #{run_hook_file}...")
187
210
  File.write(run_hook_file, run_hook_contents)
188
211
  end
189
212
 
190
- def create_settings_file
191
- settings_file = File.join(work_dir, 'habitat', 'config', 'settings.sh')
213
+ def create_settings_file(directory)
214
+ settings_file = File.join(directory, 'habitat', 'config', 'settings.sh')
192
215
  Habitat::Log.info("Generating a settings file at #{settings_file}...")
193
216
  File.write(settings_file, "SLEEP_TIME={{cfg.sleep_time}}\n")
194
217
  end
195
218
 
196
- def create_default_config
197
- default_toml = File.join(work_dir, 'habitat', 'default.toml')
219
+ def create_default_config(directory)
220
+ default_toml = File.join(directory, 'habitat', 'default.toml')
198
221
  Habitat::Log.info("Generating Habitat's default.toml configuration...")
199
222
  File.write(default_toml, 'sleep_time = 300')
200
223
  end
@@ -308,7 +331,7 @@ EOL
308
331
  plan += <<-EOL
309
332
 
310
333
  do_build() {
311
- cp -vr $PLAN_CONTEXT/../src/* $HAB_CACHE_SRC_PATH/$pkg_dirname
334
+ cp -vr $PLAN_CONTEXT/../* $HAB_CACHE_SRC_PATH/$pkg_dirname
312
335
  }
313
336
 
314
337
  do_install() {
@@ -321,6 +344,7 @@ do_install() {
321
344
  profile_contents=(${profile_contents[@]/$item/})
322
345
  done
323
346
 
347
+ mkdir ${pkg_prefix}/dist
324
348
  cp -r ${profile_contents[@]} ${pkg_prefix}/dist/
325
349
  }
326
350
  EOL
@@ -1,3 +1,3 @@
1
1
  # Example InSpec Profile
2
2
 
3
- This example shows the implementation of an InSpec [profile](../../docs/profiles.rst).
3
+ This example shows the implementation of an InSpec profile.
@@ -1,5 +1,5 @@
1
1
  # encoding: utf-8
2
- # copyright: 2015, The Authors
2
+ # copyright: 2017, The Authors
3
3
 
4
4
  title 'sample section'
5
5
 
@@ -186,7 +186,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
186
186
  option :command, aliases: :c,
187
187
  desc: 'A single command string to run instead of launching the shell'
188
188
  option :format, type: :string, default: nil, hide: true,
189
- desc: 'Which formatter to use: cli, progress, documentation, json, json-min, junit'
189
+ desc: 'Which formatter to use: cli, documentation, html, json, json-min, junit, progress'
190
190
  def shell_func
191
191
  diagnose
192
192
  o = opts.dup
@@ -35,7 +35,7 @@ module Inspec
35
35
  # @param profile_context [Inspec::ProfileContext]
36
36
  # @param outer_dsl [OuterDSLClass]
37
37
  # @return [ProfileContextClass]
38
- def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
38
+ def self.create(profile_context, resources_dsl) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
39
39
  rule_class = rule_context(resources_dsl)
40
40
  profile_context_owner = profile_context
41
41
  profile_id = profile_context.profile_id
@@ -45,12 +45,14 @@ module Inspec
45
45
  include Inspec::DSL::RequireOverride
46
46
  include resources_dsl
47
47
 
48
+ attr_accessor :skip_file
49
+
48
50
  def initialize(backend, conf, dependencies, require_loader)
49
51
  @backend = backend
50
52
  @conf = conf
51
53
  @dependencies = dependencies
52
54
  @require_loader = require_loader
53
- @skip_profile = false
55
+ @skip_file = false
54
56
  end
55
57
 
56
58
  define_method :title do |arg|
@@ -113,7 +115,7 @@ module Inspec
113
115
  end
114
116
 
115
117
  define_method :register_control do |control, &block|
116
- if @skip_profile || !profile_context_owner.profile_supports_os?
118
+ if @skip_file || !profile_context_owner.profile_supports_os?
117
119
  ::Inspec::Rule.set_skip_rule(control, true)
118
120
  end
119
121
 
@@ -129,9 +131,17 @@ module Inspec
129
131
  profile_context_owner.unregister_rule(id)
130
132
  end
131
133
 
132
- def only_if
133
- return unless block_given?
134
- @skip_profile ||= !yield
134
+ define_method :only_if do |&block|
135
+ return unless block
136
+ return if @skip_file == true || block.yield == true
137
+
138
+ # Apply `set_skip_rule` for other rules in the same file
139
+ profile_context_owner.rules.values.each do |r|
140
+ sources_match = r.source_file == block.source_location[0]
141
+ Inspec::Rule.set_skip_rule(r, true) if sources_match
142
+ end
143
+
144
+ @skip_file = true
135
145
  end
136
146
 
137
147
  alias_method :rule, :control
@@ -137,7 +137,16 @@ module Inspec
137
137
  @contents = {}
138
138
  @files = []
139
139
  walk_tar(@path) do |tar|
140
- @files = tar.map(&:full_name).find_all { |x| !x.empty? }
140
+ @files = tar.find_all(&:file?)
141
+
142
+ # delete all entries with no name
143
+ @files = @files.find_all { |x| !x.full_name.empty? }
144
+
145
+ # delete all entries that have a PaxHeader
146
+ @files = @files.delete_if { |x| x.full_name.include?('PaxHeader/') }
147
+
148
+ # replace all items of the array simply with the relative filename of the file
149
+ @files.map! { |x| Pathname.new(x.full_name).relative_path_from(Pathname.new('.')).to_s }
141
150
  end
142
151
  end
143
152
 
@@ -157,7 +166,7 @@ module Inspec
157
166
  # NB `TarReader` includes `Enumerable` beginning with Ruby 2.x
158
167
  walk_tar(@path) do |tar|
159
168
  tar.each do |entry|
160
- next unless entry.file? && file == entry.full_name
169
+ next unless entry.file? && [file, "./#{file}"].include?(entry.full_name)
161
170
  res = entry.read
162
171
  break
163
172
  end
@@ -182,8 +191,19 @@ module Inspec
182
191
  if @prefix.nil?
183
192
  raise "Could not determine path prefix for #{parent}"
184
193
  end
185
- @files = parent.files.find_all { |x| x.start_with?(prefix) && x != prefix }
194
+
195
+ # select all files that begin with the prefix, and strip off the prefix from the file.
196
+ #
197
+ # strip off any leading top-level relative path (./) which is common in
198
+ # PAX-formatted tar files. Do not do any translation of the path if the
199
+ # path is an absolute path.
200
+ @files = parent.files
201
+ .find_all { |x| x.start_with?(prefix) && x != prefix }
186
202
  .map { |x| x[prefix.length..-1] }
203
+ .map do |x|
204
+ path = Pathname.new(x)
205
+ path.absolute? ? path.to_s : path.relative_path_from(Pathname.new('.')).to_s
206
+ end
187
207
  end
188
208
 
189
209
  def abs_path(file)
@@ -127,6 +127,8 @@ module Inspec
127
127
  end
128
128
 
129
129
  def load_control_file(*args)
130
+ # Set `skip_file` to `false` between file loads to prevent skips from spanning multiple control files
131
+ control_eval_context.skip_file = false
130
132
  load_with_context(control_eval_context, *args)
131
133
  end
132
134
  alias load load_control_file
@@ -94,6 +94,10 @@ module Inspec
94
94
  @tags
95
95
  end
96
96
 
97
+ def source_file
98
+ @__file
99
+ end
100
+
97
101
  # Skip all checks if only_if is false
98
102
  #
99
103
  # @param [Type] &block returns true if tests are added, false otherwise
@@ -42,7 +42,7 @@ module Inspec
42
42
 
43
43
  # configure pry shell prompt
44
44
  Pry.config.prompt_name = 'inspec'
45
- Pry.prompt = [proc { "#{readline_ignore("\e[0;32m")}#{Pry.config.prompt_name}> #{readline_ignore("\e[0m")}" }]
45
+ Pry.prompt = [proc { "#{readline_ignore("\e[1m\e[32m")}#{Pry.config.prompt_name}> #{readline_ignore("\e[0m")}" }]
46
46
 
47
47
  # Add a help menu as the default intro
48
48
  Pry.hooks.add_hook(:before_session, 'inspec_intro') do
@@ -79,7 +79,7 @@ module Inspec
79
79
  end
80
80
 
81
81
  def mark(x)
82
- "#{readline_ignore("\033[1m")}#{x}#{readline_ignore("\033[0m")}"
82
+ "\e[1m\e[39m#{x}\e[0m"
83
83
  end
84
84
 
85
85
  def print_example(example)
@@ -106,8 +106,8 @@ module Inspec
106
106
  puts <<EOF
107
107
  You are currently running on:
108
108
 
109
- OS platform: #{mark ctx.os[:name] || 'unknown'}
110
- OS family: #{mark ctx.os[:family] || 'unknown'}
109
+ OS platform: #{mark ctx.os[:name] || 'unknown'}
110
+ OS family: #{mark ctx.os[:family] || 'unknown'}
111
111
  OS release: #{mark ctx.os[:release] || 'unknown'}
112
112
  EOF
113
113
  end
@@ -4,5 +4,5 @@
4
4
  # author: Christoph Hartmann
5
5
 
6
6
  module Inspec
7
- VERSION = '1.41.0'.freeze
7
+ VERSION = '1.42.3'.freeze
8
8
  end
@@ -4,104 +4,99 @@
4
4
 
5
5
  require 'utils/parser'
6
6
 
7
- class EtcFstab < Inspec.resource(1)
8
- name 'etc_fstab'
9
- desc 'Use the etc_fstab InSpec audit resource to check the configuration of the etc/fstab file.'
10
- example "
11
- removable_media = etc_fstab.removable_media_file_systems
12
- removable_media.each do |media|
13
- describe media do
14
- its ('mount_options') { should include 'nosuid' }
7
+ module Inspec::Resources
8
+ class EtcFstab < Inspec.resource(1)
9
+ name 'etc_fstab'
10
+ desc 'Use the etc_fstab InSpec audit resource to check the configuration of the etc/fstab file.'
11
+ example "
12
+ nfs_systems = etc_fstab.nfs_file_systems.entries
13
+ nfs_systems.each do |file_system|
14
+ describe file_system do
15
+ its ('mount_options') { should include 'nosuid' }
16
+ its ('mount_options') { should include 'noexec' }
17
+ its ('mount_options') { should include 'sec=krb5:krb5i:krb5p }
18
+ end
15
19
  end
16
- end
17
20
 
18
- nfs_systems = etc_fstab.nfs_file_systems
19
- nfs_systems.each do |file_system|
20
- describe file_system do
21
- its ('mount_options') { should include 'nosuid' }
22
- its ('mount_options') { should include 'noexec' }
23
- its ('mount_options') { should include '\'sec=krb5:krb5i:krb5p\'' }
21
+ describe etc_fstab do
22
+ its ('home_mount_options') { should include 'nosuid' }
24
23
  end
25
- end
26
-
27
- describe etc_fstab do
28
- its ('home_mount_options') { should include 'nosuid' }
29
- end
30
- "
31
-
32
- attr_reader :params
24
+ "
33
25
 
34
- include CommentParser
26
+ attr_reader :params
35
27
 
36
- def initialize(fstab_path = nil)
37
- return skip_resource 'The `etc_fstab` resource is not supported on your OS.' unless inspec.os.linux?
38
- @conf_path = fstab_path || '/etc/fstab'
39
- @files_contents = {}
40
- @content = nil
41
- @params = nil
42
- read_content
43
- end
28
+ include CommentParser
44
29
 
45
- filter = FilterTable.create
46
- filter.add_accessor(:where)
47
- .add_accessor(:entries)
48
- .add(:device_name, field: 'device_name')
49
- .add(:mount_point, field: 'mount_point')
50
- .add(:file_system_type, field: 'file_system_type')
51
- .add(:mount_options, field: 'mount_options')
52
- .add(:dump_options, field: 'dump_options')
53
- .add(:file_system_options, field: 'file_system_options')
54
- .add(:configured?) { |x| x.entries.any? }
55
-
56
- filter.connect(self, :params)
57
-
58
- def nfs_file_systems
59
- where { file_system_type.match(/nfs/) }
60
- end
30
+ def initialize(fstab_path = nil)
31
+ return skip_resource 'The `etc_fstab` resource is not supported on your OS.' unless inspec.os.linux?
32
+ @conf_path = fstab_path || '/etc/fstab'
33
+ @files_contents = {}
34
+ @content = nil
35
+ @params = nil
36
+ read_content
37
+ end
61
38
 
62
- def home_mount_options
63
- return nil unless where { mount_point == '/home' }.configured?
64
- where { mount_point == '/home' }.entries[0].mount_options
65
- end
39
+ filter = FilterTable.create
40
+ filter.add_accessor(:where)
41
+ .add_accessor(:entries)
42
+ .add(:device_name, field: 'device_name')
43
+ .add(:mount_point, field: 'mount_point')
44
+ .add(:file_system_type, field: 'file_system_type')
45
+ .add(:mount_options, field: 'mount_options')
46
+ .add(:dump_options, field: 'dump_options')
47
+ .add(:file_system_options, field: 'file_system_options')
48
+ .add(:configured?) { |x| x.entries.any? }
49
+
50
+ filter.connect(self, :params)
51
+
52
+ def nfs_file_systems
53
+ where { file_system_type.match(/nfs/) }
54
+ end
66
55
 
67
- private
56
+ def home_mount_options
57
+ return nil unless where { mount_point == '/home' }.configured?
58
+ where { mount_point == '/home' }.entries[0].mount_options
59
+ end
68
60
 
69
- def read_content
70
- @content = ''
71
- @params = {}
72
- @content = read_file(@conf_path)
73
- @params = parse_conf(@content)
74
- end
61
+ private
75
62
 
76
- def parse_conf(content)
77
- content.map do |line|
78
- data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
79
- parse_line(data) unless data == ''
80
- end.compact
81
- end
63
+ def read_content
64
+ @content = ''
65
+ @params = {}
66
+ @content = read_file(@conf_path)
67
+ @params = parse_conf(@content)
68
+ end
82
69
 
83
- def parse_line(line)
84
- attributes = line.split
85
- {
86
- 'device_name' => attributes[0],
87
- 'mount_point' => attributes[1],
88
- 'file_system_type' => attributes[2],
89
- 'mount_options' => attributes[3].split(','),
90
- 'dump_options' => attributes[4].to_i,
91
- 'file_system_options' => attributes[5].to_i,
92
- }
93
- end
70
+ def parse_conf(content)
71
+ content.map do |line|
72
+ data, = parse_comment_line(line, comment_char: '#', standalone_comments: false)
73
+ parse_line(data) unless data == ''
74
+ end.compact
75
+ end
94
76
 
95
- def read_file(conf_path = @conf_path)
96
- file = inspec.file(conf_path)
97
- if !file.file?
98
- return skip_resource "Can't find \"#{@conf_path}\""
77
+ def parse_line(line)
78
+ attributes = line.split
79
+ {
80
+ 'device_name' => attributes[0],
81
+ 'mount_point' => attributes[1],
82
+ 'file_system_type' => attributes[2],
83
+ 'mount_options' => attributes[3].split(','),
84
+ 'dump_options' => attributes[4].to_i,
85
+ 'file_system_options' => attributes[5].to_i,
86
+ }
99
87
  end
100
88
 
101
- raw_conf = file.content
102
- if raw_conf.empty? && !file.empty?
103
- return skip_resource("File is empty or unable to read file at path:\"#{@conf_path}\"")
89
+ def read_file(conf_path = @conf_path)
90
+ file = inspec.file(conf_path)
91
+ if !file.file?
92
+ return skip_resource "Can't find \"#{@conf_path}\""
93
+ end
94
+
95
+ raw_conf = file.content
96
+ if raw_conf.empty? && !file.empty?
97
+ return skip_resource("File is empty or unable to read file at path:\"#{@conf_path}\"")
98
+ end
99
+ raw_conf.lines
104
100
  end
105
- raw_conf.lines
106
101
  end
107
102
  end
@@ -84,6 +84,13 @@ module Inspec::Resources
84
84
  file_permission_granted?('execute', by_usergroup, by_specific_user)
85
85
  end
86
86
 
87
+ def allowed?(permission, opts = {})
88
+ return false unless exist?
89
+ return skip_resource '`allowed?` is not supported on your OS yet.' if @perms_provider.nil?
90
+
91
+ file_permission_granted?(permission, opts[:by], opts[:by_user])
92
+ end
93
+
87
94
  def mounted?(expected_options = nil, identical = false)
88
95
  mounted = file.mounted
89
96
 
@@ -206,18 +213,82 @@ module Inspec::Resources
206
213
  end
207
214
 
208
215
  def check_file_permission_by_user(access_type, user, path)
209
- access_rule = case access_type
210
- when 'read'
211
- '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'Read\', \'ListDirectory\')'
212
- when 'write'
213
- '@(\'FullControl\', \'Modify\', \'Write\')'
214
- when 'execute'
215
- '@(\'FullControl\', \'Modify\', \'ReadAndExecute\', \'ExecuteFile\')'
216
- else
217
- raise 'Invalid access_type provided'
218
- end
216
+ access_rule = translate_perm_names(access_type)
217
+ access_rule = convert_to_powershell_array(access_rule)
218
+
219
219
  cmd = inspec.command("@(@((Get-Acl '#{path}').access | Where-Object {$_.AccessControlType -eq 'Allow' -and $_.IdentityReference -eq '#{user}' }) | Where-Object {($_.FileSystemRights.ToString().Split(',') | % {$_.trim()} | ? {#{access_rule} -contains $_}) -ne $null}) | measure | % { $_.Count }")
220
220
  cmd.stdout.chomp == '0' ? false : true
221
221
  end
222
+
223
+ private
224
+
225
+ def convert_to_powershell_array(arr)
226
+ if arr.empty?
227
+ '@()'
228
+ else
229
+ %{@('#{arr.join("', '")}')}
230
+ end
231
+ end
232
+
233
+ # Translates a developer-friendly string into a list of acceptable
234
+ # FileSystemRights that match it, because Windows has a fun heirarchy
235
+ # of permissions that are able to be noted in multiple ways.
236
+ #
237
+ # See also: https://www.codeproject.com/Reference/871338/AccessControl-FileSystemRights-Permissions-Table
238
+ def translate_perm_names(access_type)
239
+ names = translate_common_perms(access_type)
240
+ names ||= translate_granular_perms(access_type)
241
+ names ||= translate_uncommon_perms(access_type)
242
+ raise 'Invalid access_type provided' unless names
243
+ end
244
+
245
+ def translate_common_perms(access_type)
246
+ case access_type
247
+ when 'full-control'
248
+ %w{FullControl}
249
+ when 'modify'
250
+ translate_perm_names('full-control') + %w{Modify}
251
+ when 'read'
252
+ translate_perm_names('modify') + %w{ReadAndExecute Read}
253
+ when 'write'
254
+ translate_perm_names('modify') + %w{Write}
255
+ when 'execute'
256
+ translate_perm_names('modify') + %w{ReadAndExecute ExecuteFile Traverse}
257
+ when 'delete'
258
+ translate_perm_names('modify') + %w{Delete}
259
+ end
260
+ end
261
+
262
+ def translate_uncommon_perms(access_type)
263
+ case access_type
264
+ when 'delete-subdirectories-and-files'
265
+ translate_perm_names('full-control') + %w{DeleteSubdirectoriesAndFiles}
266
+ when 'change-permissions'
267
+ translate_perm_names('full-control') + %w{ChangePermissions}
268
+ when 'take-ownership'
269
+ translate_perm_names('full-control') + %w{TakeOwnership}
270
+ end
271
+ end
272
+
273
+ def translate_granular_perms(access_type)
274
+ case access_type
275
+ when 'write-data', 'create-files'
276
+ translate_perm_names('write') + %w{WriteData CreateFiles}
277
+ when 'append-data', 'create-directories'
278
+ translate_perm_names('write') + %w{CreateDirectories AppendData}
279
+ when 'write-extended-attributes'
280
+ translate_perm_names('write') + %w{WriteExtendedAttributes}
281
+ when 'write-attributes'
282
+ translate_perm_names('write') + %w{WriteAttributes}
283
+ when 'read-data', 'list-directory'
284
+ translate_perm_names('read') + %w{ReadData ListDirectory}
285
+ when 'read-attributes'
286
+ translate_perm_names('read') + %w{ReadAttributes}
287
+ when 'read-extended-attributes'
288
+ translate_perm_names('read') + %w{ReadExtendedAttributes}
289
+ when 'read-permissions'
290
+ translate_perm_names('read') + %w{ReadPermissions}
291
+ end
292
+ end
222
293
  end
223
294
  end
@@ -59,9 +59,11 @@ module Inspec::Resources
59
59
  os = inspec.os
60
60
  if os.linux?
61
61
  LinuxPorts.new(inspec)
62
- elsif %w{darwin aix}.include?(os[:family])
62
+ elsif os.aix?
63
63
  # AIX: see http://www.ibm.com/developerworks/aix/library/au-lsof.html#resources
64
64
  # and https://www-01.ibm.com/marketing/iwm/iwm/web/reg/pick.do?source=aixbp
65
+ AixPorts.new(inspec)
66
+ elsif os.darwin?
65
67
  # Darwin: https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man8/lsof.8.html
66
68
  LsofPorts.new(inspec)
67
69
  elsif os.windows?
@@ -263,6 +265,121 @@ module Inspec::Resources
263
265
  end
264
266
  end
265
267
 
268
+ class AixPorts < PortsInfo
269
+ def info
270
+ ports_via_netstat || ports_via_lsof
271
+ end
272
+
273
+ def ports_via_lsof
274
+ return nil unless inspec.command('lsof').exist?
275
+ LsofPorts.new(inspec).info
276
+ end
277
+
278
+ def ports_via_netstat
279
+ return nil unless inspec.command('netstat').exist?
280
+
281
+ cmd = inspec.command('netstat -Aan | grep LISTEN')
282
+ return nil unless cmd.exit_status.to_i.zero?
283
+
284
+ ports = []
285
+ # parse all lines
286
+ cmd.stdout.each_line do |line|
287
+ port_info = parse_netstat_line(line)
288
+
289
+ # only push protocols we are interested in
290
+ next unless %w{tcp tcp6 udp udp6}.include?(port_info['protocol'])
291
+ ports.push(port_info)
292
+ end
293
+
294
+ ports
295
+ end
296
+
297
+ def parse_netstat_line(line)
298
+ # parse each line
299
+ # 1 - Socket, 2 - Proto, 3 - Receive-Q, 4 - Send-Q, 5 - Local address, 6 - Foreign Address, 7 - State
300
+ parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)?\s+(\S+)/.match(line)
301
+ return {} if parsed.nil?
302
+
303
+ # parse ip4 and ip6 addresses
304
+ protocol = parsed[2].downcase
305
+
306
+ # detect protocol if not provided
307
+ protocol += '6' if parsed[5].count(':') > 1 && %w{tcp udp}.include?(protocol)
308
+ protocol.chop! if %w{tcp4 upd4}.include?(protocol)
309
+
310
+ # extract host and port information
311
+ host, port = parse_net_address(parsed[5], protocol)
312
+ return {} if host.nil?
313
+
314
+ # extract PID
315
+ cmd = inspec.command("rmsock #{parsed[1]} tcpcb")
316
+ parsed_pid = /^The socket (\S+) is being held by proccess (\d+) \((\S+)\)/.match(cmd.stdout)
317
+ return {} if parsed_pid.nil?
318
+ process = parsed_pid[3]
319
+ pid = parsed_pid[2]
320
+ pid = pid.to_i if pid =~ /^\d+$/
321
+
322
+ {
323
+ 'port' => port,
324
+ 'address' => host,
325
+ 'protocol' => protocol,
326
+ 'process' => process,
327
+ 'pid' => pid,
328
+ }
329
+ end
330
+
331
+ def parse_net_address(net_addr, protocol)
332
+ # local/foreign addresses on AIX use a '.' to separate the addresss
333
+ # from the port
334
+ address, _sep, port = net_addr.rpartition('.')
335
+ if protocol.eql?('tcp6') || protocol.eql?('udp6')
336
+ ip6addr = address
337
+ # AIX uses the wildcard character for ipv6 addresses listening on
338
+ # all interfaces.
339
+ ip6addr = '::' if ip6addr =~ /^\*$/
340
+
341
+ # v6 addresses need to end in a double-colon when using
342
+ # shorthand notation. netstat ends with a single colon.
343
+ # IPAddr will fail to properly parse an address unless it
344
+ # uses a double-colon for short-hand notation.
345
+ ip6addr += ':' if ip6addr =~ /\w:$/
346
+
347
+ begin
348
+ ip_parser = IPAddr.new(ip6addr)
349
+ rescue IPAddr::InvalidAddressError
350
+ # This IP is not parsable. There appears to be a bug in netstat
351
+ # output that truncates link-local IP addresses:
352
+ # example: udp6 0 0 fe80::42:acff:fe11::123 :::* 0 54550 3335/ntpd
353
+ # actual link address: inet6 fe80::42:acff:fe11:5/64 scope link
354
+ #
355
+ # in this example, the "5" is truncated making the netstat output
356
+ # an invalid IP address.
357
+ return [nil, nil]
358
+ end
359
+
360
+ # Check to see if this is a IPv4 address in a tcp6/udp6 line.
361
+ # If so, don't put brackets around the IP or URI won't know how
362
+ # to properly handle it.
363
+ # example: f000000000000000 tcp6 0 0 127.0.0.1.8005 *.* LISTEN
364
+ if ip_parser.ipv4?
365
+ ip_addr = URI("addr://#{ip6addr}:#{port}")
366
+ host = ip_addr.host
367
+ else
368
+ ip_addr = URI("addr://[#{ip6addr}]:#{port}")
369
+ host = ip_addr.host[1..ip_addr.host.size-2]
370
+ end
371
+ else
372
+ ip4addr = address
373
+ # In AIX the wildcard character is used to match all interfaces
374
+ ip4addr = '0.0.0.0' if ip4addr =~ /^\*$/
375
+ ip_addr = URI("addr://#{ip4addr}:#{port}")
376
+ host = ip_addr.host
377
+ end
378
+
379
+ [host, port.to_i]
380
+ end
381
+ end
382
+
266
383
  # extract port information from netstat
267
384
  class LinuxPorts < PortsInfo # rubocop:disable Metrics/ClassLength
268
385
  ALLOWED_PROTOCOLS = %w{tcp tcp6 udp udp6}.freeze
@@ -430,13 +547,33 @@ module Inspec::Resources
430
547
  # the netstat-provided data
431
548
  host = '0.0.0.0' if host == '*'
432
549
 
433
- # parse the process name from the processes information
434
- process_match = parsed[6].match(/users:\(\(\"(\S+)\"/)
435
- process = process_match.nil? ? nil : process_match[1]
436
-
437
- # parse the PID from the processes information
438
- pid_match = parsed[6].match(/pid=(\d+)/)
439
- pid = pid_match.nil? ? nil : pid_match[1].to_i
550
+ # in case process list parsing is not successfull
551
+ process = nil
552
+ pid = nil
553
+
554
+ # parse process and pid from the process list
555
+ #
556
+ # remove the "users:((" and "))" parts
557
+ # input: users:((\"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8))
558
+ # res: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
559
+ process_list_match = parsed[6].match(/users:\(\((.+)\)\)/)
560
+ if process_list_match
561
+ # list entires are seperated by "," the braces can also be removed
562
+ # input: \"nginx\",pid=583,fd=8),(\"nginx\",pid=582,fd=8),(\"nginx\",pid=580,fd=8),(\"nginx\",pid=579,fd=8
563
+ # res: ["\"nginx\",pid=583,fd=8", "\"nginx\",pid=582,fd=8", "\"nginx\",pid=580,fd=8", "\"nginx\",pid=579,fd=8"]
564
+ process_list = process_list_match[1].split('),(')
565
+ # To stay backwards compatible with netstat we need to select
566
+ # the last element in the resulting array.
567
+ # res: "\"nginx\",pid=579,fd=8"
568
+
569
+ # parse the process name from the process list
570
+ process_match = process_list.last.match(/^\"(\S+)\"/)
571
+ process = process_match.nil? ? nil : process_match[1]
572
+
573
+ # parse the PID from the process list
574
+ pid_match = process_list.last.match(/pid=(\d+)/)
575
+ pid = pid_match.nil? ? nil : pid_match[1].to_i
576
+ end
440
577
 
441
578
  {
442
579
  'port' => port,
@@ -86,7 +86,7 @@ module Inspec::Resources
86
86
  if data_dir_loc.nil?
87
87
  warn 'Unable to find the PostgreSQL data_dir in expected location(s), please
88
88
  execute "psql -t -A -p <port> -h <host> -c "show hba_file";" as the PostgreSQL
89
- DBA to find the non-starndard data_dir location.'
89
+ DBA to find the non-standard data_dir location.'
90
90
  end
91
91
  data_dir_loc
92
92
  end
@@ -18,7 +18,7 @@ module Inspec::Resources
18
18
  @content = nil
19
19
  os = inspec.os
20
20
  return skip_resource 'The `windows_hotfix` resource is not a feature of your OS.' unless os.windows?
21
- query = "Get-WmiObject -class \"win32_quickfixengineering\" -filter \"HotFixID = '" + @id + "'\""
21
+ query = "get-hotfix -id #{@id}"
22
22
  cmd = inspec.powershell(query)
23
23
  @content = cmd.stdout
24
24
  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.41.0
4
+ version: 1.42.3
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-10-09 00:00:00.000000000 Z
11
+ date: 2017-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: train