inspec 0.14.3 → 0.14.4

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: 7a5fe5ef68c859de078978c41fac03fa6164c5ff
4
- data.tar.gz: e9a4ad5bb3a9808801ad153d50843eeb8f6fbcee
3
+ metadata.gz: b08bf257cb8949cae2b2a72aa26ba4e992724ca9
4
+ data.tar.gz: 6e8c50d7a6025d2714d012c7fe262b4be1087860
5
5
  SHA512:
6
- metadata.gz: 4bd1406a6eccaf2fe72cdab36d4e8b467653f14767a6006ea01e9768d5d7e1329b7a81e65308d404f8df61b900e5a4280addd849e5c8fa673a204e79ae2c9e37
7
- data.tar.gz: 2226db16772df0467cca929a0ee68c5b3925450831f15630b0ac09003dacf55579a1cc67acdd252de04d44846e557d70b4959a3f795ad5ea2226d3ce2c9bbb42
6
+ metadata.gz: c58d8262678198e8c88647dbf1e8f204f1a9c9da268ecc439b76d234c346f403b717ca4d0c7e54941848fa64341ec3af85c86167b7250adcec6200dbbed9b43d
7
+ data.tar.gz: 5505c2d86085b0cb45e4d6c9562fb44b462354e45e001265681cba5d48d6c3f1950573470ce25b59c6c56c1413c90f8a5c5bfd6f3b3603298d754b1af0136334
data/CHANGELOG.md CHANGED
@@ -1,16 +1,32 @@
1
1
  # Change Log
2
2
 
3
- ## [0.14.3](https://github.com/chef/inspec/tree/0.14.3) (2016-02-24)
4
- [Full Changelog](https://github.com/chef/inspec/compare/v0.14.2...0.14.3)
3
+ ## [0.14.4](https://github.com/chef/inspec/tree/0.14.4) (2016-02-26)
4
+ [Full Changelog](https://github.com/chef/inspec/compare/v0.14.3...0.14.4)
5
5
 
6
6
  **Implemented enhancements:**
7
7
 
8
- - cmp matcher should compare expected string == number [\#487](https://github.com/chef/inspec/pull/487) ([chris-rock](https://github.com/chris-rock))
8
+ - add `describe.one`: collection of tests with at least one passing [\#497](https://github.com/chef/inspec/pull/497) ([arlimus](https://github.com/arlimus))
9
9
 
10
10
  **Merged pull requests:**
11
11
 
12
+ - don't crash on empty metadata during finalize [\#500](https://github.com/chef/inspec/pull/500) ([arlimus](https://github.com/arlimus))
13
+ - add xinetd\_conf resource [\#499](https://github.com/chef/inspec/pull/499) ([arlimus](https://github.com/arlimus))
14
+
15
+ ## [v0.14.3](https://github.com/chef/inspec/tree/v0.14.3) (2016-02-24)
16
+ [Full Changelog](https://github.com/chef/inspec/compare/v0.14.2...v0.14.3)
17
+
18
+ **Implemented enhancements:**
19
+
20
+ - cmp matcher should compare expected string == number [\#487](https://github.com/chef/inspec/pull/487) ([chris-rock](https://github.com/chris-rock))
21
+
22
+ **Fixed bugs:**
23
+
12
24
  - expose inspec errors during profile read [\#492](https://github.com/chef/inspec/pull/492) ([arlimus](https://github.com/arlimus))
13
25
 
26
+ **Merged pull requests:**
27
+
28
+ - 0.14.3 [\#493](https://github.com/chef/inspec/pull/493) ([arlimus](https://github.com/arlimus))
29
+
14
30
  ## [v0.14.2](https://github.com/chef/inspec/tree/v0.14.2) (2016-02-22)
15
31
  [Full Changelog](https://github.com/chef/inspec/compare/v0.14.1...v0.14.2)
16
32
 
data/docs/dsl_inspec.rst CHANGED
@@ -45,9 +45,22 @@ where
45
45
  * ``its('Port')`` is the matcher; ``{ should eq('22') }`` is the test. A ``describe`` block must contain at least one matcher, but may contain as many as required
46
46
 
47
47
 
48
- Author Tests
49
- -----------------------------------------------------
50
- It is recommended that test files are located in the ``/tests`` directory. When writing controls, the ``impact``, ``title``, ``desc`` metadata are _optional_, but are highly recommended.
48
+ Advanced concepts
49
+ =====================================================
50
+
51
+ With inspec it is possible to check if at least one of a collection of checks is true. For example: If a setting is configured in two different locations, you may want to test if either configuration A or configuration B have been set. This is accomplished via ``describe.one``. It defines a block of tests with at least one valid check.
52
+
53
+ .. code-block:: ruby
54
+
55
+ describe.one do
56
+ describe ConfigurationA do
57
+ its('setting_1') { should eq true }
58
+ end
59
+
60
+ describe ConfigurationB do
61
+ its('setting_2') { should eq true }
62
+ end
63
+ end
51
64
 
52
65
  Examples
53
66
  =====================================================
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ # author: Dominik Richter
3
+ # author: Christoph Hartmann
4
+
5
+ module Inspec
6
+ class DescribeBase
7
+ def initialize(action)
8
+ @action = action
9
+ @checks = []
10
+ end
11
+
12
+ # Evaluate the given block and collect all checks. These will be registered
13
+ # with the callback function under the 'describe.one' name.
14
+ #
15
+ # @param [Proc] ruby block containing checks (e.g. via describe)
16
+ # @return [nil]
17
+ def one(&block)
18
+ return unless block_given?
19
+ instance_eval(&block)
20
+ @action.call('describe.one', @checks, nil)
21
+ end
22
+
23
+ def describe(*args, &block)
24
+ @checks.push(['describe', args, block])
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+ # copyright: 2016, Chef Software Inc.
3
+ # author: Dominik Richter
4
+ # author: Christoph Hartmann
5
+
6
+ require 'rspec/expectations'
7
+
8
+ module Inspec
9
+ class Expect
10
+ attr_reader :calls, :value, :block
11
+ def initialize(value, &block)
12
+ @value = value
13
+ @block = block
14
+ @calls = []
15
+ end
16
+
17
+ def to(*args, &block)
18
+ @calls.push([:to, args, block, caller])
19
+ end
20
+
21
+ def not_to(*args, &block)
22
+ @calls.push([:not_to, args, block, caller])
23
+ end
24
+
25
+ def example_group
26
+ that = self
27
+
28
+ opts = { 'caller' => calls[0][3] }
29
+ if !calls[0][3].nil? && !calls[0][3].empty? &&
30
+ (m = calls[0][3][0].match(/^([^:]*):(\d+):/))
31
+ opts['file_path'] = m[0]
32
+ opts['line_number'] = m[1]
33
+ end
34
+
35
+ RSpec::Core::ExampleGroup.describe(that.value, opts) do
36
+ that.calls.each do |method, args, block, clr|
37
+ it(nil, caller: clr) do
38
+ x = expect(that.value, &that.block).method(method)
39
+ x.call(*args, &block)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -140,6 +140,7 @@ module Inspec
140
140
  end
141
141
 
142
142
  def self.finalize(metadata, profile_id)
143
+ return nil if metadata.nil?
143
144
  param = metadata.params || {}
144
145
  param['name'] = profile_id.to_s unless profile_id.to_s.empty?
145
146
  param['version'] = param['version'].to_s unless param['version'].nil?
@@ -85,7 +85,7 @@ module Inspec
85
85
  #
86
86
  # @param outer_dsl [OuterDSLClass]
87
87
  # @return [ProfileContextClass]
88
- def create_context(resources_dsl, rule_class)
88
+ def create_context(resources_dsl, rule_class) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
89
89
  profile_context_owner = self
90
90
 
91
91
  # rubocop:disable Lint/NestedMethodDefinition
@@ -117,13 +117,15 @@ module Inspec
117
117
  alias_method :rule, :control
118
118
 
119
119
  define_method :describe do |*args, &block|
120
- path = block.source_location[0]
121
- line = block.source_location[1]
122
- id = "(generated from #{File.basename(path)}:#{line} #{SecureRandom.hex})"
120
+ loc = block_location(block, caller[0])
121
+ id = "(generated from #{loc} #{SecureRandom.hex})"
122
+
123
+ res = nil
123
124
  rule = rule_class.new(id, {}) do
124
- describe(*args, &block)
125
+ res = describe(*args, &block)
125
126
  end
126
127
  profile_context_owner.register_rule(rule, &block)
128
+ res
127
129
  end
128
130
 
129
131
  # TODO: mock method for attributes; import attribute handling
@@ -141,6 +143,17 @@ module Inspec
141
143
  return unless block_given?
142
144
  @skip_profile = !yield
143
145
  end
146
+
147
+ private
148
+
149
+ def block_location(block, alternate_caller)
150
+ if block.nil?
151
+ alternate_caller[/^(.+:\d+):in .+$/, 1] || 'unknown'
152
+ else
153
+ path, line = block.source_location
154
+ "#{File.basename(path)}:#{line}"
155
+ end
156
+ end
144
157
  end
145
158
  # rubocop:enable all
146
159
  end
@@ -93,6 +93,7 @@ require 'resources/shadow'
93
93
  require 'resources/ssh_conf'
94
94
  require 'resources/user'
95
95
  require 'resources/windows_feature'
96
+ require 'resources/xinetd'
96
97
  require 'resources/yum'
97
98
 
98
99
  # file formats, depend on json implementation
data/lib/inspec/rule.rb CHANGED
@@ -4,47 +4,11 @@
4
4
  # author: Dominik Richter
5
5
  # author: Christoph Hartmann
6
6
 
7
- require 'rspec/expectations'
8
7
  require 'method_source'
8
+ require 'inspec/describe'
9
+ require 'inspec/expect'
9
10
 
10
11
  module Inspec
11
- class ExpectationTarget
12
- attr_reader :calls, :value, :block
13
- def initialize(value, &block)
14
- @value = value
15
- @block = block
16
- @calls = []
17
- end
18
-
19
- def to(*args, &block)
20
- @calls.push([:to, args, block, caller])
21
- end
22
-
23
- def not_to(*args, &block)
24
- @calls.push([:not_to, args, block, caller])
25
- end
26
-
27
- def example_group
28
- that = self
29
-
30
- opts = { 'caller' => calls[0][3] }
31
- if !calls[0][3].nil? && !calls[0][3].empty? &&
32
- (m = calls[0][3][0].match(/^([^:]*):(\d+):/))
33
- opts['file_path'] = m[0]
34
- opts['line_number'] = m[1]
35
- end
36
-
37
- RSpec::Core::ExampleGroup.describe(that.value, opts) do
38
- that.calls.each do |method, args, block, clr|
39
- it(nil, caller: clr) do
40
- x = expect(that.value, &that.block).method(method)
41
- x.call(*args, &block)
42
- end
43
- end
44
- end
45
- end
46
- end
47
-
48
12
  class Rule
49
13
  include ::RSpec::Matchers
50
14
 
@@ -83,13 +47,32 @@ module Inspec
83
47
  @desc
84
48
  end
85
49
 
86
- def describe(value, &block)
87
- @checks.push(['describe', [value], block])
50
+ # Describe will add one or more tests to this control. There is 2 ways
51
+ # of calling it:
52
+ #
53
+ # describe resource do ... end
54
+ #
55
+ # or
56
+ #
57
+ # describe.one do ... end
58
+ #
59
+ # @param [any] Resource to be describe, string, or nil
60
+ # @param [Proc] An optional block containing tests for the described resource
61
+ # @return [nil|DescribeBase] if called without arguments, returns DescribeBase
62
+ def describe(*values, &block)
63
+ if values.empty? && !block_given?
64
+ dsl = self.class.ancestors[1]
65
+ Class.new(DescribeBase) do
66
+ include dsl
67
+ end.new(method(:add_check))
68
+ else
69
+ add_check('describe', values, block)
70
+ end
88
71
  end
89
72
 
90
73
  def expect(value, &block)
91
- target = ExpectationTarget.new(value, &block)
92
- @checks.push(['expect', [value], target])
74
+ target = Inspec::Expect.new(value, &block)
75
+ add_check('expect', [value], target)
93
76
  target
94
77
  end
95
78
 
@@ -148,6 +131,10 @@ module Inspec
148
131
 
149
132
  private
150
133
 
134
+ def add_check(describe_or_expect, values, block)
135
+ @checks.push([describe_or_expect, values, block])
136
+ end
137
+
151
138
  # Idio(ma)tic unindent
152
139
  # TODO: replace this
153
140
  #
data/lib/inspec/runner.rb CHANGED
@@ -13,7 +13,7 @@ require 'inspec/metadata'
13
13
  # spec requirements
14
14
 
15
15
  module Inspec
16
- class Runner
16
+ class Runner # rubocop:disable Metrics/ClassLength
17
17
  extend Forwardable
18
18
  attr_reader :backend, :rules
19
19
  def initialize(conf = {})
@@ -96,13 +96,17 @@ module Inspec
96
96
 
97
97
  private
98
98
 
99
- def get_check_example(method_name, arg, block)
99
+ def block_source_info(block)
100
+ return {} if block.nil? || !block.respond_to?(:source_location)
100
101
  opts = {}
101
- if !block.nil? && block.respond_to?(:source_location)
102
- file_path, line = block.source_location
103
- opts['file_path'] = file_path
104
- opts['line_number'] = line
105
- end
102
+ file_path, line = block.source_location
103
+ opts['file_path'] = file_path
104
+ opts['line_number'] = line
105
+ opts
106
+ end
107
+
108
+ def get_check_example(method_name, arg, block)
109
+ opts = block_source_info(block)
106
110
 
107
111
  if !arg.empty? &&
108
112
  arg[0].respond_to?(:resource_skipped) &&
@@ -117,6 +121,16 @@ module Inspec
117
121
  return @test_collector.example_group(*arg, opts, &block)
118
122
  when 'expect'
119
123
  return block.example_group
124
+ when 'describe.one'
125
+ tests = arg.map do |x|
126
+ @test_collector.example_group(x[1][0], block_source_info(x[2]), &x[2])
127
+ end
128
+ return nil if tests.empty?
129
+ ok_tests = tests.find_all(&:run)
130
+ # return all tests if none succeeds; we will just report full failure
131
+ return tests if ok_tests.empty?
132
+ # otherwise return all working tests
133
+ return ok_tests
120
134
  else
121
135
  fail "A rule was registered with #{method_name.inspect}, "\
122
136
  "which isn't understood and cannot be processed."
@@ -128,10 +142,11 @@ module Inspec
128
142
  def register_rule(rule_id, rule)
129
143
  @rules[rule_id] = rule
130
144
  checks = rule.instance_variable_get(:@checks)
131
- checks.each do |m, a, b|
132
- # resource skipping
133
- example = get_check_example(m, a, b)
145
+ examples = checks.map do |m, a, b|
146
+ get_check_example(m, a, b)
147
+ end.flatten.compact
134
148
 
149
+ examples.each do |example|
135
150
  # TODO: Remove this!! It is very dangerous to do this here.
136
151
  # The goal of this is to make the audit DSL available to all
137
152
  # describe blocks. Right now, these blocks are executed outside
@@ -140,7 +155,6 @@ module Inspec
140
155
  # scope.
141
156
  dsl = Inspec::Resource.create_dsl(backend)
142
157
  example.send(:include, dsl)
143
-
144
158
  @test_collector.add_test(example, rule_id)
145
159
  end
146
160
  end
@@ -3,5 +3,5 @@
3
3
  # author: Christoph Hartmann
4
4
 
5
5
  module Inspec
6
- VERSION = '0.14.3'.freeze
6
+ VERSION = '0.14.4'.freeze
7
7
  end
@@ -0,0 +1,142 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ require 'utils/parser'
6
+
7
+ class XinetdConf < Inspec.resource(1) # rubocop:disable Metrics/ClassLength
8
+ name 'xinetd_conf'
9
+ desc 'Xinetd services configuration.'
10
+ example "
11
+ describe xinetd_conf.services('chargen') do
12
+ its('socket_types') { should include 'dgram' }
13
+ end
14
+
15
+ describe xinetd_conf.services('chargen').socket_types('dgram') do
16
+ it { should be_disabled }
17
+ end
18
+ "
19
+
20
+ include XinetdParser
21
+
22
+ def initialize(conf_path = '/etc/xinetd.conf', opts = {})
23
+ @conf_path = conf_path
24
+ @params = opts[:params] unless opts[:params].nil?
25
+ @filters = opts[:filters] || ''
26
+ @contents = {}
27
+ end
28
+
29
+ def to_s
30
+ "Xinetd config #{@conf_path}"
31
+ end
32
+
33
+ def services(condition = nil)
34
+ condition.nil? ? params['services'].keys : filter(service: condition)
35
+ end
36
+
37
+ def ids(condition = nil)
38
+ condition.nil? ? services_field('id') : filter(id: condition)
39
+ end
40
+
41
+ def socket_types(condition = nil)
42
+ condition.nil? ? services_field('socket_type') : filter(socket_type: condition)
43
+ end
44
+
45
+ def types(condition = nil)
46
+ condition.nil? ? services_field('type') : filter(type: condition)
47
+ end
48
+
49
+ def wait(condition = nil)
50
+ condition.nil? ? services_field('wait') : filter(wait: condition)
51
+ end
52
+
53
+ def disabled?
54
+ filter(disable: 'no').services.empty?
55
+ end
56
+
57
+ def enabled?
58
+ filter(disable: 'yes').services.empty?
59
+ end
60
+
61
+ def params
62
+ return @params if defined?(@params)
63
+ return @params = {} if read_content.nil?
64
+ flat_params = parse_xinetd(read_content)
65
+ @params = { 'services' => {} }
66
+ flat_params.each do |k, v|
67
+ name = k[/^service (.+)$/, 1]
68
+ if name.nil?
69
+ @params[k] = v
70
+ else
71
+ @params['services'][name] = v
72
+ end
73
+ end
74
+ @params
75
+ end
76
+
77
+ def filter(conditions = {})
78
+ res = params.dup
79
+ filters = ''
80
+ conditions.each do |k, v|
81
+ v = v.to_s if v.is_a? Integer
82
+ filters += " #{k} = #{v.inspect}"
83
+ res['services'] = filter_by(res['services'], k.to_s, v)
84
+ end
85
+ XinetdConf.new(@conf_path, params: res, filters: filters)
86
+ end
87
+
88
+ private
89
+
90
+ # Retrieve the provided field from all configured services.
91
+ #
92
+ # @param [String] field name, e.g. `socket_type`
93
+ # @return [Array[String]] all values of this field across services
94
+ def services_field(field)
95
+ params['services'].values.compact.flatten
96
+ .map { |x| x.params[field] }.flatten.compact
97
+ end
98
+
99
+ def match_condition(sth, condition)
100
+ case sth
101
+ # this does Regex-matching as well as string comparison
102
+ when condition
103
+ true
104
+ else
105
+ false
106
+ end
107
+ end
108
+
109
+ # Filter services by a criteria. This allows for search queries for
110
+ # certain values.
111
+ #
112
+ # @param [Hash] service collection
113
+ # @param [String] search key you want to query
114
+ # @param [Any] search value that the key should match
115
+ # @return [Hash] filtered service collection
116
+ def filter_by(services, k, v)
117
+ if k == 'service'
118
+ return Hash[services.find_all { |name, _| match_condition(v, name) }]
119
+ end
120
+ Hash[services.map { |name, service_arr|
121
+ found = service_arr.find_all { |service|
122
+ match_condition(service.params[k], v)
123
+ }
124
+ found.empty? ? nil : [name, found]
125
+ }.compact]
126
+ end
127
+
128
+ def read_content(path = @conf_path)
129
+ return @contents[path] if @contents.key?(path)
130
+ file = inspec.file(path)
131
+ if !file.file?
132
+ return skip_resource "Can't find file \"#{path}\""
133
+ end
134
+
135
+ @contents[path] = file.content
136
+ if @contents[path].empty? && file.size > 0
137
+ return skip_resource "Can't read file \"#{path}\""
138
+ end
139
+
140
+ @contents[path]
141
+ end
142
+ end
data/lib/utils/parser.rb CHANGED
@@ -177,3 +177,56 @@ module SolarisNetstatParser
177
177
  line.match(Regexp.new(arr.join))
178
178
  end
179
179
  end
180
+
181
+ module XinetdParser
182
+ def xinetd_include_dir(dir)
183
+ return [] if dir.nil?
184
+
185
+ unless inspec.file(dir).directory?
186
+ return skip_resource "Cannot read folder in #{dir}"
187
+ end
188
+
189
+ files = inspec.command("find #{dir} -type f").stdout.split("\n")
190
+ files.map { |file| parse_xinetd(read_content(file)) }
191
+ end
192
+
193
+ def parse_xinetd(raw) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
194
+ return {} if raw.nil?
195
+ res = {}
196
+ cur_group = nil
197
+ simple_conf = []
198
+ rest = raw
199
+ until rest.empty?
200
+ nl = rest.index("\n") || (rest.length-1)
201
+ comment = rest.index('#') || (rest.length-1)
202
+ dst_idx = (comment < nl) ? comment : nl
203
+ inner_line = (dst_idx == 0) ? '' : rest[0..dst_idx-1].strip
204
+ rest = rest[nl+1..-1]
205
+ next if inner_line.empty?
206
+
207
+ if inner_line == '}'
208
+ res[cur_group] = SimpleConfig.new(simple_conf.join("\n"))
209
+ cur_group = nil
210
+ elsif rest.lstrip[0] == '{'
211
+ cur_group = inner_line
212
+ simple_conf = []
213
+ rest = rest[rest.index("\n")+1..-1]
214
+ elsif cur_group.nil?
215
+ others = xinetd_include_dir(inner_line[/includedir (.+)/, 1])
216
+
217
+ # complex merging of included configurations, as multiple services
218
+ # may be defined with the same name but different configuration
219
+ others.each { |ores|
220
+ ores.each { |k, v|
221
+ res[k] ||= []
222
+ res[k].push(v)
223
+ }
224
+ }
225
+ else
226
+ simple_conf.push(inner_line)
227
+ end
228
+ end
229
+
230
+ res
231
+ end
232
+ end
data/test/helper.rb CHANGED
@@ -111,6 +111,10 @@ class MockLoader
111
111
  '/etc/apache2/apache2.conf' => mockfile.call('apache2.conf'),
112
112
  '/etc/apache2/ports.conf' => mockfile.call('ports.conf'),
113
113
  '/etc/apache2/conf-enabled/serve-cgi-bin.conf' => mockfile.call('serve-cgi-bin.conf'),
114
+ '/etc/xinetd.conf' => mockfile.call('xinetd.conf'),
115
+ '/etc/xinetd.d' => mockfile.call('xinetd.d'),
116
+ '/etc/xinetd.d/chargen-stream' => mockfile.call('xinetd.d_chargen-stream'),
117
+ '/etc/xinetd.d/chargen-dgram' => mockfile.call('xinetd.d_chargen-dgram'),
114
118
  }
115
119
 
116
120
  # create all mock commands
@@ -225,6 +229,8 @@ class MockLoader
225
229
  'pkg info system/file-system/zfs' => cmd.call('pkg-info-system-file-system-zfs'),
226
230
  # port netstat on solaris 10 & 11
227
231
  'netstat -an -f inet -f inet6' => cmd.call('s11-netstat-an-finet-finet6'),
232
+ # xinetd configuration
233
+ 'find /etc/xinetd.d -type f' => cmd.call('find-xinetd.d'),
228
234
  }
229
235
 
230
236
  @backend
@@ -0,0 +1,2 @@
1
+ /etc/xinetd.d/chargen-stream
2
+ /etc/xinetd.d/chargen-dgram
@@ -0,0 +1,9 @@
1
+ defaults
2
+ {
3
+ # comments...
4
+ # enabled =
5
+ log_type = SYSLOG daemon info
6
+ instances = 50
7
+ }
8
+
9
+ includedir /etc/xinetd.d
File without changes
@@ -0,0 +1,9 @@
1
+ service chargen
2
+ {
3
+ disable = no
4
+ # comment
5
+ id = chargen-dgram
6
+ type = INTERNAL
7
+ wait = yes
8
+ socket_type = dgram
9
+ }
@@ -0,0 +1,9 @@
1
+ service chargen
2
+ {
3
+ disable = yes
4
+ # disable = no
5
+ id = chargen-stream
6
+ type = INTERNAL
7
+ wait = yes
8
+ socket_type = stream
9
+ }
@@ -5,10 +5,64 @@
5
5
  require 'helper'
6
6
  require 'inspec/profile_context'
7
7
 
8
+ class Module
9
+ include Minitest::Spec::DSL
10
+ end
11
+
12
+ module DescribeOneTest
13
+ it 'loads an empty describe.one' do
14
+ profile.load(format(context_format, 'describe.one'))
15
+ get_checks.must_equal([])
16
+ end
17
+
18
+ it 'loads an empty describe.one block' do
19
+ profile.load(format(context_format, 'describe.one do; end'))
20
+ get_checks.must_equal([['describe.one', [], nil]])
21
+ end
22
+
23
+ it 'loads a simple describe.one block' do
24
+ profile.load(format(context_format, '
25
+ describe.one do
26
+ describe true do; it { should eq true }; end
27
+ end'))
28
+ c = get_checks[0]
29
+ c[0].must_equal 'describe.one'
30
+ childs = c[1]
31
+ childs.length.must_equal 1
32
+ childs[0][0].must_equal 'describe'
33
+ childs[0][1].must_equal [true]
34
+ end
35
+
36
+ it 'loads a complex describe.one block' do
37
+ profile.load(format(context_format, '
38
+ describe.one do
39
+ describe 0 do; it { should eq true }; end
40
+ describe 1 do; it { should eq true }; end
41
+ describe 2 do; it { should eq true }; end
42
+ end'))
43
+ c = get_checks[0]
44
+ c[0].must_equal 'describe.one'
45
+ childs = c[1]
46
+ childs.length.must_equal 3
47
+ childs.each_with_index do |ci, idx|
48
+ ci[0].must_equal 'describe'
49
+ ci[1].must_equal [idx]
50
+ end
51
+ end
52
+ end
53
+
8
54
  describe Inspec::ProfileContext do
9
55
  let(:backend) { MockLoader.new.backend }
10
56
  let(:profile) { Inspec::ProfileContext.new(nil, backend) }
11
57
 
58
+ def get_rule
59
+ profile.rules.values[0]
60
+ end
61
+
62
+ def get_checks
63
+ get_rule.instance_variable_get(:@checks)
64
+ end
65
+
12
66
  it 'must be able to load empty content' do
13
67
  profile.load('', 'dummy', 1).must_be_nil
14
68
  end
@@ -18,6 +72,10 @@ describe Inspec::ProfileContext do
18
72
  proc { profile.load(call) }
19
73
  end
20
74
 
75
+ let(:context_format) { '%s' }
76
+
77
+ include DescribeOneTest
78
+
21
79
  it 'must provide os resource' do
22
80
  load('print os[:family]').must_output 'ubuntu'
23
81
  end
@@ -30,6 +88,13 @@ describe Inspec::ProfileContext do
30
88
  load('print command("").stdout').must_output ''
31
89
  end
32
90
 
91
+ it 'supports empty describe calls' do
92
+ load('describe').must_output ''
93
+ profile.rules.keys.length.must_equal 1
94
+ profile.rules.keys[0].must_match /^\(generated from unknown:1 [0-9a-f]+\)$/
95
+ profile.rules.values[0].must_be_kind_of Inspec::Rule
96
+ end
97
+
33
98
  it 'provides the describe keyword in the global DSL' do
34
99
  load('describe true do; it { should_eq true }; end')
35
100
  .must_output ''
@@ -61,6 +126,13 @@ describe Inspec::ProfileContext do
61
126
 
62
127
  describe 'rule DSL' do
63
128
  let(:rule_id) { rand.to_s }
129
+ let(:context_format) { "rule #{rule_id.inspect} do\n%s\nend" }
130
+
131
+ def get_rule
132
+ profile.rules[rule_id]
133
+ end
134
+
135
+ include DescribeOneTest
64
136
 
65
137
  it 'doesnt add any checks if none are provided' do
66
138
  profile.load("rule #{rule_id.inspect}")
@@ -68,17 +140,20 @@ describe Inspec::ProfileContext do
68
140
  rule.instance_variable_get(:@checks).must_equal([])
69
141
  end
70
142
 
143
+ describe 'supports empty describe blocks' do
144
+ it 'doesnt crash, but doesnt add anything either' do
145
+ profile.load(format(context_format, 'describe'))
146
+ profile.rules.keys.must_include(rule_id)
147
+ get_checks.must_equal([])
148
+ end
149
+ end
150
+
71
151
  describe 'adds a check via describe' do
72
- let(:cmd) {<<-EOF
73
- rule #{rule_id.inspect} do
74
- describe os[:family] { it { must_equal 'ubuntu' } }
75
- end
76
- EOF
77
- }
78
152
  let(:check) {
79
- profile.load(cmd)
80
- rule = profile.rules[rule_id]
81
- rule.instance_variable_get(:@checks)[0]
153
+ profile.load(format(context_format,
154
+ "describe os[:family] { it { must_equal 'ubuntu' } }"
155
+ ))
156
+ get_checks[0]
82
157
  }
83
158
 
84
159
  it 'registers the check with describe' do
@@ -95,16 +170,11 @@ describe Inspec::ProfileContext do
95
170
  end
96
171
 
97
172
  describe 'adds a check via expect' do
98
- let(:cmd) {<<-EOF
99
- rule #{rule_id.inspect} do
100
- expect(os[:family]).to eq('ubuntu')
101
- end
102
- EOF
103
- }
104
173
  let(:check) {
105
- profile.load(cmd)
106
- rule = profile.rules[rule_id]
107
- rule.instance_variable_get(:@checks)[0]
174
+ profile.load(format(context_format,
175
+ "expect(os[:family]).to eq('ubuntu')"
176
+ ))
177
+ get_checks[0]
108
178
  }
109
179
 
110
180
  it 'registers the check with describe' do
@@ -116,23 +186,18 @@ describe Inspec::ProfileContext do
116
186
  end
117
187
 
118
188
  it 'registers the check with the provided proc' do
119
- check[2].must_be_kind_of Inspec::ExpectationTarget
189
+ check[2].must_be_kind_of Inspec::Expect
120
190
  end
121
191
  end
122
192
 
123
193
  describe 'adds a check via describe + expect' do
124
- let(:cmd) {<<-EOF
125
- rule #{rule_id.inspect} do
126
- describe 'the actual test' do
127
- expect(os[:family]).to eq('ubuntu')
128
- end
129
- end
130
- EOF
131
- }
132
194
  let(:check) {
133
- profile.load(cmd)
134
- rule = profile.rules[rule_id]
135
- rule.instance_variable_get(:@checks)[0]
195
+ profile.load(format(context_format,
196
+ "describe 'the actual test' do
197
+ expect(os[:family]).to eq('ubuntu')
198
+ end"
199
+ ))
200
+ get_checks[0]
136
201
  }
137
202
 
138
203
  it 'registers the check with describe' do
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ require 'helper'
6
+ require 'inspec/resource'
7
+
8
+ describe 'Inspec::Resources::XinetdConf' do
9
+ let(:resource) { load_resource('xinetd_conf') }
10
+ it 'reads default params' do
11
+ d = resource.params['defaults']
12
+ _(d).must_be_kind_of SimpleConfig
13
+ _(d.params['instances']).must_equal '50'
14
+ _(d.params['log_type']).must_equal 'SYSLOG daemon info'
15
+ _(d.params.length).must_equal 2
16
+ end
17
+
18
+ describe 'with services from child configs' do
19
+ it 'has one service name' do
20
+ _(resource.services).must_equal %w{chargen}
21
+ end
22
+
23
+ it 'has multiple service definitions' do
24
+ _(resource.ids).must_equal %w{chargen-stream chargen-dgram}
25
+ end
26
+
27
+ it 'can filter by name' do
28
+ _(resource.services('not here').params['services']).must_be_empty
29
+ end
30
+
31
+ it 'can chain filters' do
32
+ one = resource.services('chargen').socket_types('dgram')
33
+ _(one.params['services'].length).must_equal 1
34
+ _(one.ids).must_equal %w{chargen-dgram}
35
+ end
36
+
37
+ it 'checks if all are disabled on one disabled service' do
38
+ one = resource.ids('chargen-stream')
39
+ _(one.disabled?).must_equal true
40
+ end
41
+
42
+ it 'checks if all are disabled on multiple mixed' do
43
+ _(resource.disabled?).must_equal false
44
+ end
45
+
46
+ it 'checks if all are enabled on one enabled service' do
47
+ one = resource.ids(/dgram$/)
48
+ _(one.enabled?).must_equal true
49
+ end
50
+
51
+ it 'checks if all are enabled on one enabled service' do
52
+ one = resource.ids(/stream$/)
53
+ _(one.enabled?).must_equal false
54
+ end
55
+
56
+ it 'checks if all are enabled on multiple mixed' do
57
+ _(resource.enabled?).must_equal false
58
+ end
59
+ end
60
+ 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: 0.14.3
4
+ version: 0.14.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-24 00:00:00.000000000 Z
11
+ date: 2016-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: r-train
@@ -237,7 +237,9 @@ files:
237
237
  - lib/inspec/archive/tar.rb
238
238
  - lib/inspec/archive/zip.rb
239
239
  - lib/inspec/backend.rb
240
+ - lib/inspec/describe.rb
240
241
  - lib/inspec/dsl.rb
242
+ - lib/inspec/expect.rb
241
243
  - lib/inspec/fetcher.rb
242
244
  - lib/inspec/log.rb
243
245
  - lib/inspec/metadata.rb
@@ -309,6 +311,7 @@ files:
309
311
  - lib/resources/ssh_conf.rb
310
312
  - lib/resources/user.rb
311
313
  - lib/resources/windows_feature.rb
314
+ - lib/resources/xinetd.rb
312
315
  - lib/resources/yaml.rb
313
316
  - lib/resources/yum.rb
314
317
  - lib/source_readers/flat.rb
@@ -409,6 +412,7 @@ files:
409
412
  - test/unit/mock/cmd/find-apache2-ports-conf
410
413
  - test/unit/mock/cmd/find-etc-rc-d-name-S
411
414
  - test/unit/mock/cmd/find-net-interface
415
+ - test/unit/mock/cmd/find-xinetd.d
412
416
  - test/unit/mock/cmd/gem-list-local-a-q-rubocop
413
417
  - test/unit/mock/cmd/get-net-tcpconnection
414
418
  - test/unit/mock/cmd/get-netadapter-binding-bridge
@@ -475,6 +479,10 @@ files:
475
479
  - test/unit/mock/files/shadow
476
480
  - test/unit/mock/files/ssh_config
477
481
  - test/unit/mock/files/sshd_config
482
+ - test/unit/mock/files/xinetd.conf
483
+ - test/unit/mock/files/xinetd.d/.gitkeep
484
+ - test/unit/mock/files/xinetd.d_chargen-dgram
485
+ - test/unit/mock/files/xinetd.d_chargen-stream
478
486
  - test/unit/mock/profiles/complete-metadata/inspec.yml
479
487
  - test/unit/mock/profiles/complete-profile/controls/filesystem_spec.rb
480
488
  - test/unit/mock/profiles/complete-profile/inspec.yml
@@ -533,6 +541,7 @@ files:
533
541
  - test/unit/resources/ssh_conf_test.rb
534
542
  - test/unit/resources/user_test.rb
535
543
  - test/unit/resources/windows_feature.rb
544
+ - test/unit/resources/xinetd_test.rb
536
545
  - test/unit/resources/yaml_test.rb
537
546
  - test/unit/resources/yum_test.rb
538
547
  - test/unit/source_reader_test.rb
@@ -652,6 +661,7 @@ test_files:
652
661
  - test/unit/mock/cmd/find-apache2-ports-conf
653
662
  - test/unit/mock/cmd/find-etc-rc-d-name-S
654
663
  - test/unit/mock/cmd/find-net-interface
664
+ - test/unit/mock/cmd/find-xinetd.d
655
665
  - test/unit/mock/cmd/gem-list-local-a-q-rubocop
656
666
  - test/unit/mock/cmd/get-net-tcpconnection
657
667
  - test/unit/mock/cmd/get-netadapter-binding-bridge
@@ -718,6 +728,10 @@ test_files:
718
728
  - test/unit/mock/files/shadow
719
729
  - test/unit/mock/files/ssh_config
720
730
  - test/unit/mock/files/sshd_config
731
+ - test/unit/mock/files/xinetd.conf
732
+ - test/unit/mock/files/xinetd.d/.gitkeep
733
+ - test/unit/mock/files/xinetd.d_chargen-dgram
734
+ - test/unit/mock/files/xinetd.d_chargen-stream
721
735
  - test/unit/mock/profiles/complete-metadata/inspec.yml
722
736
  - test/unit/mock/profiles/complete-profile/controls/filesystem_spec.rb
723
737
  - test/unit/mock/profiles/complete-profile/inspec.yml
@@ -776,6 +790,7 @@ test_files:
776
790
  - test/unit/resources/ssh_conf_test.rb
777
791
  - test/unit/resources/user_test.rb
778
792
  - test/unit/resources/windows_feature.rb
793
+ - test/unit/resources/xinetd_test.rb
779
794
  - test/unit/resources/yaml_test.rb
780
795
  - test/unit/resources/yum_test.rb
781
796
  - test/unit/source_reader_test.rb