inspec 0.14.3 → 0.14.4

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: 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