inspec 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 70a0c204211c2bd4a689aff2790fe23999a5013a
4
- data.tar.gz: ee648ad90d3d80a6f2cd4f2c18207d39916a6b49
3
+ metadata.gz: 5e3b076ffed41d9148eb98065a025a6acb6a9f70
4
+ data.tar.gz: eb36dc73cf37bc30a46949943ba7db582ed82417
5
5
  SHA512:
6
- metadata.gz: c71d588c6547180d6f54e769c1580dc88d98634cc7100583e6723ea172014fb1f3feb7471791cbd9429b112bda7dbba840564e6ea65ec61faf36427847ca92e2
7
- data.tar.gz: 0d5750125ca6c6cce5ab69a001726dc7663ae95b2269cf8a162ff9e08037ae64ca3607a220af7ae11f47b2c623b3f17b99da92ebabd8650481ae6916b5286a18
6
+ metadata.gz: 09d87d6da242d31586af6d3e39e913223a141d6dbc39474c921636d3169a57bcf1b5029defc1d2f6a8f01c246fe3494846edfe9d00a27381f3aa13f8f335435a
7
+ data.tar.gz: d2806a4c8e637a1ec46cf3ed9ea5a40fc8dd32a6e3bb0de63f405d33f4de9a09d3f25faf36932d949b73c7d9ff1d11949e59963f6ab6116402890632facb0227
data/.rubocop.yml CHANGED
@@ -6,6 +6,7 @@ AllCops:
6
6
  - 'test/**/*'
7
7
  - 'examples/**/*'
8
8
  - 'vendor/**/*'
9
+ - 'lib/bundles/inspec-init/templates/**/*'
9
10
  Documentation:
10
11
  Enabled: false
11
12
  AlignParameters:
data/CHANGELOG.md CHANGED
@@ -1,7 +1,27 @@
1
1
  # Change Log
2
2
 
3
- ## [0.11.0](https://github.com/chef/inspec/tree/0.11.0) (2016-02-09)
4
- [Full Changelog](https://github.com/chef/inspec/compare/v0.10.1...0.11.0)
3
+ ## [0.12.0](https://github.com/chef/inspec/tree/0.12.0) (2016-02-15)
4
+ [Full Changelog](https://github.com/chef/inspec/compare/v0.11.0...0.12.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - add runlevel support for System V services [\#455](https://github.com/chef/inspec/pull/455) ([arlimus](https://github.com/arlimus))
9
+ - Add a init subcommand [\#454](https://github.com/chef/inspec/pull/454) ([chris-rock](https://github.com/chris-rock))
10
+
11
+ **Fixed bugs:**
12
+
13
+ - Windows 2008 isn't being detected. [\#346](https://github.com/chef/inspec/issues/346)
14
+ - Fix two minor logging and config bugs in CLI [\#452](https://github.com/chef/inspec/pull/452) ([srenatus](https://github.com/srenatus))
15
+ - bugfix: verify the target resolver before using it [\#449](https://github.com/chef/inspec/pull/449) ([arlimus](https://github.com/arlimus))
16
+ - Fix iptables on CentOS6 + more tests for iptables \(plus small code improvements\) [\#442](https://github.com/chef/inspec/pull/442) ([srenatus](https://github.com/srenatus))
17
+
18
+ **Merged pull requests:**
19
+
20
+ - rework target to resolver connection [\#447](https://github.com/chef/inspec/pull/447) ([arlimus](https://github.com/arlimus))
21
+ - separate directory resolver from target resolver [\#446](https://github.com/chef/inspec/pull/446) ([arlimus](https://github.com/arlimus))
22
+
23
+ ## [v0.11.0](https://github.com/chef/inspec/tree/v0.11.0) (2016-02-10)
24
+ [Full Changelog](https://github.com/chef/inspec/compare/v0.10.1...v0.11.0)
5
25
 
6
26
  **Implemented enhancements:**
7
27
 
@@ -11,12 +31,14 @@
11
31
  **Fixed bugs:**
12
32
 
13
33
  - File stats are not always working properly [\#430](https://github.com/chef/inspec/issues/430)
34
+ - Inspec iptables should have\_rule tests not working [\#420](https://github.com/chef/inspec/issues/420)
14
35
  - Integration test for apache config [\#406](https://github.com/chef/inspec/issues/406)
15
36
  - rework auditd\_rules resource [\#312](https://github.com/chef/inspec/issues/312)
16
37
  - resource/auditd\_rules: update rule list format [\#309](https://github.com/chef/inspec/issues/309)
17
38
 
18
39
  **Merged pull requests:**
19
40
 
41
+ - 0.11.0 [\#443](https://github.com/chef/inspec/pull/443) ([arlimus](https://github.com/arlimus))
20
42
  - Fix supermarket cli registration [\#441](https://github.com/chef/inspec/pull/441) ([chris-rock](https://github.com/chris-rock))
21
43
  - update to winrm 1.6.1 command scheme [\#439](https://github.com/chef/inspec/pull/439) ([arlimus](https://github.com/arlimus))
22
44
  - semantics: rename CLI plugins registry -\> commands [\#435](https://github.com/chef/inspec/pull/435) ([arlimus](https://github.com/arlimus))
data/bin/inspec CHANGED
@@ -84,7 +84,7 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
84
84
  def archive(path)
85
85
  diagnose
86
86
 
87
- o = options.dup
87
+ o = opts.dup
88
88
  o[:logger] = Logger.new(STDOUT)
89
89
  o[:logger].level = get_log_level(o.log_level)
90
90
 
@@ -129,8 +129,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
129
129
  option :format, type: :string, default: Inspec::NoSummaryFormatter, hide: true
130
130
  def shell_func
131
131
  diagnose
132
+ o = opts.dup
133
+ o[:logger] = Logger.new(STDOUT)
134
+ o[:logger].level = get_log_level(o.log_level)
132
135
 
133
- runner = Inspec::Runner.new(opts)
136
+ runner = Inspec::Runner.new(o)
134
137
  Inspec::Shell.new(runner).start
135
138
  rescue RuntimeError => e
136
139
  puts e.message
@@ -0,0 +1,8 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+ # author: Dominik Richter
4
+
5
+ libdir = File.dirname(__FILE__)
6
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
7
+
8
+ require 'inspec-init/cli'
@@ -0,0 +1,86 @@
1
+ # encoding: utf-8
2
+ # author: Christoph Hartmann
3
+
4
+ module Init
5
+ class CLI < Inspec::BaseCLI
6
+ namespace 'init'
7
+
8
+ # read template directoy
9
+ template_dir = File.join(File.dirname(__FILE__), 'templates')
10
+ Dir.glob(File.join(template_dir, '*')) do |template|
11
+ relative = Pathname.new(template).relative_path_from(Pathname.new(template_dir))
12
+
13
+ # register command for the template
14
+ desc "#{relative} NAME", "Create a new #{relative}"
15
+ option :overwrite, type: :boolean, default: false,
16
+ desc: 'Overwrites existing directory'
17
+ define_method relative.to_s.to_sym do |name|
18
+ generator(relative.to_s, { name: name }, options)
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ # 1. iterate over all files
25
+ # 2. read content in erb
26
+ # 3. write to target
27
+ def generator(type, attributes = {}, options = {}) # rubocop:disable Metrics/AbcSize
28
+ # path of this script
29
+ dir = File.dirname(__FILE__)
30
+ # look for template directory
31
+ base_dir = File.join(dir, 'templates', type)
32
+ # prepare glob for all subdirectories and files
33
+ template = File.join(base_dir, '**', '{*,.*}')
34
+ # generate target path
35
+ target = Pathname.new(Dir.pwd).join(attributes[:name])
36
+ puts "Create new #{type} at #{mark_text(target)}"
37
+
38
+ # check that the directory does not exist
39
+ if File.exist?(target) && !options['overwrite']
40
+ error "#{mark_text(target)} exists already, use --overwrite"
41
+ exit 1
42
+ end
43
+
44
+ # ensure that target directory is available
45
+ FileUtils.mkdir_p(target)
46
+
47
+ # iterate over files and write to target path
48
+ Dir.glob(template) do |file|
49
+ relative = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
50
+ destination = Pathname.new(target).join(relative)
51
+ if File.directory?(file)
52
+ li "Create directory #{mark_text(relative)}"
53
+ FileUtils.mkdir_p(destination)
54
+ elsif File.file?(file)
55
+ li "Create file #{mark_text(relative)}"
56
+ # read & render content
57
+ content = render(File.read(file), attributes)
58
+ # write file content
59
+ File.write(destination, content)
60
+ else
61
+ puts "Ignore #{file}, because its not an file or directoy"
62
+ end
63
+ end
64
+ end
65
+
66
+ # This is a render helper to bind hash values to a ERB template
67
+ def render(content, hash)
68
+ # create a new binding class
69
+ cls = Class.new do
70
+ hash.each do |key, value|
71
+ define_method key.to_sym do
72
+ value
73
+ end
74
+ end
75
+ # expose binding
76
+ define_method :bind do
77
+ binding
78
+ end
79
+ end
80
+ ERB.new(content).result(cls.new.bind)
81
+ end
82
+ end
83
+
84
+ # register the subcommand to Inspec CLI registry
85
+ Inspec::Plugins::CLI.add_subcommand(Init::CLI, 'init', 'init TEMPLATE ...', 'Scaffolds a new project', {})
86
+ end
@@ -0,0 +1,3 @@
1
+ # Example InSpec Profile
2
+
3
+ This example shows the implementation of an InSpec [profile](../../docs/profiles.rst).
@@ -0,0 +1,20 @@
1
+ # encoding: utf-8
2
+ # copyright: 2015, The Authors
3
+ # license: All rights reserved
4
+
5
+ title 'sample section'
6
+
7
+ # you can also use plain tests
8
+ describe file('/tmp') do
9
+ it { should be_directory }
10
+ end
11
+
12
+ # you add controls here
13
+ control 'tmp-1.0' do # A unique ID for this control
14
+ impact 0.7 # The criticality, if this control fails.
15
+ title 'Create /tmp directory' # A human-readable title
16
+ desc 'An optional description...'
17
+ describe file('/tmp') do # The actual test
18
+ it { should be_directory }
19
+ end
20
+ end
@@ -0,0 +1,8 @@
1
+ name: <%= name %>
2
+ title: InSpec Profile
3
+ maintainer: The Authors
4
+ copyright: The Authors
5
+ copyright_email: you@example.com
6
+ license: All Rights Reserved
7
+ summary: An InSpec Compliance Profile
8
+ version: 0.1.0
@@ -4,40 +4,30 @@
4
4
 
5
5
  require 'rubygems/package'
6
6
  require 'zlib'
7
+ require 'inspec/targets/dir'
7
8
 
8
9
  module Inspec::Targets
9
- class ArchiveHelper
10
- def resolve(target, _opts = {})
11
- files, rootdir = structure(target)
10
+ class ArchiveHelper < DirsResolver
11
+ attr_reader :files
12
+
13
+ def initialize(target, _opts = {})
14
+ @target = target
15
+ files, @rootdir = structure(target)
12
16
 
13
17
  # remove trailing slashes
14
18
  files = files.collect { |f| f.chomp('/') }
15
19
 
16
20
  # remove leading directory
17
- files = files.collect { |f|
18
- Pathname(f).relative_path_from(Pathname(rootdir)).to_s
21
+ root = Pathname(@rootdir)
22
+ @files = files.collect { |f|
23
+ Pathname(f).relative_path_from(root).to_s
19
24
  }
20
-
21
- helper = DirsHelper.get_handler(files)
22
- if helper.nil?
23
- fail "Don't know how to handle folder #{target}"
24
- end
25
-
26
- res = {
27
- test: collect(helper, files, :get_filenames),
28
- library: collect(helper, files, :get_libraries),
29
- metadata: collect(helper, files, :get_metadata),
30
- }.map { |as, list|
31
- content(target, list, rootdir, base_folder: target, as: as)
32
- }
33
-
34
- res.flatten
35
25
  end
36
26
 
37
- # FIXME(sr) dedup inspec/targets/folder
38
- def collect(helper, files, getter)
39
- return [] unless helper.respond_to? getter
40
- helper.method(getter).call(files)
27
+ def resolve(path, opts = {})
28
+ o = (opts || {})
29
+ o[:base_folder] = @target
30
+ content(@target, path, @rootdir, o)
41
31
  end
42
32
  end
43
33
  end
@@ -8,18 +8,47 @@ module Inspec
8
8
  module Targets
9
9
  extend Modulator
10
10
 
11
- def self.__resolve(items)
12
- items.map do |_|
13
- # @TODO
14
- end.flatten
11
+ # Retrieve the resolver for a given target. Resolvers are any type of
12
+ # instance, that can take a target in the #resolve method, and turn
13
+ # it into raw InSpec code. It will actually retrieve all file's
14
+ # contents, including libraries and metadata.
15
+ #
16
+ # @param [any] target any target you are pointing to; typically a string
17
+ # @return [Inspec::Targets::*] the resolver handling this target
18
+ def self.find_resolver(target)
19
+ modules.values.find { |m| m.handles?(target) }
20
+ end
21
+
22
+ # Retrieve the target handler, i.e. the component that is going to handle
23
+ # all aspects of a given target. Unlike the resolver, this provides
24
+ # access to an object, that understands the target and is able to read
25
+ # all contents, while providing additional methods which InSpec itself
26
+ # does not care about (think: plugins).
27
+ #
28
+ # There is a special case, where you might be pointing to a URL containing
29
+ # a ZIP file which has a full InSpec Profile embedded. This will be
30
+ # resolved to a directory helper (Inspec::Targets::DirsResolver). This
31
+ # method will retrieve the right handler for this directory and provide it
32
+ # with full access all contents (i.e. the DirsResolver will be embedded).
33
+ #
34
+ # @param [any] target any target you are pointing to; typically a string
35
+ # @return [Inspec::Targets::*] the handler for this target
36
+ def self.find_handler(target)
37
+ resolver = find_resolver(target)
38
+ return resolver unless resolver.is_a?(Module) &&
39
+ resolver.ancestors.include?(DirsResolver)
40
+ resolver.from_target(target).handler
15
41
  end
16
42
 
43
+ # Turn targets into actionable InSpec code, library-data and metadata.
44
+ #
45
+ # @param [any] targets any targets you are pointing to; typically strings
46
+ # @param [Hash] additional options for loading
47
+ # @return [Array] an array of resolved data, with :content, :ref, :type
17
48
  def self.resolve(targets, opts = {})
18
49
  Array(targets).map do |target|
19
- handler = modules.values.find { |m| m.handles?(target) }
20
- if handler.nil?
21
- fail "Don't know how to handle target: #{target}"
22
- end
50
+ handler = find_resolver(target) ||
51
+ fail("Don't know how to handle target: #{target}")
23
52
  handler.resolve(target, opts)
24
53
  end.flatten
25
54
  end
@@ -3,79 +3,142 @@
3
3
  # author: Christoph Hartmann
4
4
 
5
5
  module Inspec::Targets
6
+ # DirsHelper manages resolvers for directories. Wherever directories are,
7
+ # either a local folder, archive, or remote, this module provides all helpers
8
+ # than can resolve these.
9
+ #
10
+ # Resolvers provide these methods
11
+ # * handles?(paths) => true if the resolver is responsible for this target
12
+ # * get_filenames => Array[String] of files where tests/controls are found
13
+ # * get_metadata => String path with metadata information [optional]
14
+ # * get_libraries => Array[String] of library files [optional]
15
+ #
16
+ # Resolvers must register with this DirsHelper via #add_module .
6
17
  module DirsHelper
7
- # InSpec profile Loader
8
- # Previous versions used the `test` directory instead of the new `controls`
9
- # directory. Usage of the test directory is deprecated and not recommended
10
- # anymore. Support for `test` will be removed in InSpec 1.0
11
- # TODO: remove `test` support for InSpec 1.0
12
- class ProfileDir
13
- def handles?(paths)
14
- return true if paths.include?('inspec.yml')
15
- (
16
- !paths.grep(/^controls/).empty? ||
17
- !paths.grep(/^test/).empty?
18
- ) && paths.include?('metadata.rb')
19
- end
18
+ extend Modulator
20
19
 
21
- def get_libraries(paths)
22
- paths.find_all do |path|
23
- path.start_with?('libraries') && path.end_with?('.rb')
24
- end
25
- end
20
+ def self.get_handler(paths)
21
+ modules.values.reverse.find { |x| x.handles?(paths) }
22
+ end
23
+ end
26
24
 
27
- def get_filenames(paths)
28
- paths.find_all do |path|
29
- path.start_with?('controls', 'test') && path.end_with?('.rb')
30
- end
31
- end
25
+ # Base class for all directory resolvers. I.e. any target helper that
26
+ # takes a directory/archive/... and ultimately calls DirsHelper to resolve it.
27
+ #
28
+ # These resolvers must implement the required methods of this class.
29
+ class DirsResolver
30
+ def files
31
+ fail NotImplementedError, "Directory resolver #{self.class} must implement #files"
32
+ end
32
33
 
33
- def get_metadata(paths)
34
- return 'inspec.yml' if paths.include?('inspec.yml')
35
- return 'metadata.rb' if paths.include?('metadata.rb')
36
- end
34
+ def resolve(_path)
35
+ fail NotImplementedError, "Directory resolver #{self.class} must implement #content"
37
36
  end
38
37
 
39
- class ChefAuditDir
40
- def handles?(paths)
41
- paths.include?('recipes') and paths.include?('metadata.rb')
42
- end
38
+ def handler
39
+ DirsHelper.get_handler(files) ||
40
+ fail("Don't know how to handle #{self}")
41
+ end
42
+
43
+ def self.from_target(target)
44
+ new(target)
45
+ end
43
46
 
44
- def get_filenames(paths)
45
- paths.find_all do |x|
46
- x.start_with? 'recipes/' and x.end_with? '.rb'
47
- end
47
+ def self.resolve(target, opts)
48
+ r = new(target, opts)
49
+ handler = r.handler
50
+ files = r.files
51
+
52
+ res = []
53
+ {
54
+ test: __collect(handler, :get_filenames, files),
55
+ library: __collect(handler, :get_libraries, files),
56
+ metadata: __collect(handler, :get_metadata, files),
57
+ }.each do |as, list|
58
+ Array(list).each { |path|
59
+ res.push(r.resolve(path, as: as))
60
+ }
48
61
  end
62
+
63
+ return handler.resolve_contents(res) if handler.respond_to?(:resolve_contents)
64
+ res
65
+ end
66
+
67
+ def self.__collect(handler, getter, files)
68
+ return [] unless handler.respond_to? getter
69
+ handler.method(getter).call(files)
70
+ end
71
+ end
72
+
73
+ # InSpec profile Loader
74
+ # Previous versions used the `test` directory instead of the new `controls`
75
+ # directory. Usage of the test directory is deprecated and not recommended
76
+ # anymore. Support for `test` will be removed in InSpec 1.0
77
+ # TODO: remove `test` support for InSpec 1.0
78
+ class ProfileDir
79
+ def handles?(paths)
80
+ return true if paths.include?('inspec.yml')
81
+ (
82
+ !paths.grep(/^controls/).empty? ||
83
+ !paths.grep(/^test/).empty?
84
+ ) && paths.include?('metadata.rb')
49
85
  end
50
86
 
51
- class ServerspecDir
52
- def handles?(paths)
53
- paths.include?('spec')
87
+ def get_libraries(paths)
88
+ paths.find_all do |path|
89
+ path.start_with?('libraries') && path.end_with?('.rb')
54
90
  end
91
+ end
55
92
 
56
- def get_filenames(paths)
57
- paths.find_all do |path|
58
- path.start_with? 'spec' and path.end_with? '_spec.rb'
59
- end
93
+ def get_filenames(paths)
94
+ paths.find_all do |path|
95
+ path.start_with?('controls', 'test') && path.end_with?('.rb')
60
96
  end
61
97
  end
62
98
 
63
- class FlatDir
64
- def handles?(paths)
65
- get_filenames(paths).empty? == false
99
+ def get_metadata(paths)
100
+ return 'inspec.yml' if paths.include?('inspec.yml')
101
+ return 'metadata.rb' if paths.include?('metadata.rb')
102
+ end
103
+ end
104
+ DirsHelper.add_module('inspec-profile', ProfileDir.new)
105
+
106
+ class ChefAuditDir
107
+ def handles?(paths)
108
+ paths.include?('recipes') and paths.include?('metadata.rb')
109
+ end
110
+
111
+ def get_filenames(paths)
112
+ paths.find_all do |x|
113
+ x.start_with? 'recipes/' and x.end_with? '.rb'
66
114
  end
115
+ end
116
+ end
117
+ DirsHelper.add_module('chef-cookbook', ChefAuditDir.new)
118
+
119
+ class ServerspecDir
120
+ def handles?(paths)
121
+ paths.include?('spec')
122
+ end
67
123
 
68
- def get_filenames(paths)
69
- paths.find_all { |x| x.end_with?('.rb') and !x.include?('/') }
124
+ def get_filenames(paths)
125
+ paths.find_all do |path|
126
+ path.start_with? 'spec' and path.end_with? '_spec.rb'
70
127
  end
71
128
  end
129
+ end
130
+ DirsHelper.add_module('serverspec-folder', ServerspecDir.new)
72
131
 
73
- HANDLERS = [
74
- ProfileDir, ChefAuditDir, ServerspecDir, FlatDir
75
- ].map(&:new)
132
+ class FlatDir
133
+ def handles?(paths)
134
+ get_filenames(paths).empty? == false
135
+ end
76
136
 
77
- def self.get_handler(paths)
78
- HANDLERS.find { |x| x.handles? paths }
137
+ def get_filenames(paths)
138
+ # TODO: eventually remove the metadata.rb exception here
139
+ # when we have fully phased out metadata.rb in 1.0
140
+ paths.find_all { |x| x.end_with?('.rb') && !x.include?('/') && x != 'metadata.rb' }
79
141
  end
80
142
  end
143
+ DirsHelper.add_module('flat-ruby-folder', FlatDir.new)
81
144
  end
@@ -6,48 +6,33 @@ require 'inspec/targets/dir'
6
6
  require 'inspec/targets/file'
7
7
 
8
8
  module Inspec::Targets
9
- class FolderHelper
10
- def handles?(target)
9
+ class FolderHelper < DirsResolver
10
+ def self.handles?(target)
11
11
  File.directory?(target)
12
12
  end
13
13
 
14
- def resolve(target, _opts = {})
14
+ attr_reader :files
15
+
16
+ def initialize(target, _opts = {})
17
+ @base_folder = target
15
18
  # find all files in the folder
16
19
  files = Dir[File.join(target, '**', '*')]
17
20
  # remove the prefix
18
21
  prefix = File.join(target, '')
19
- files = files.map { |x| x.sub(prefix, '') }
20
- # get the dirs helper
21
- helper = DirsHelper.get_handler(files)
22
- if helper.nil?
23
- fail "Don't know how to handle folder #{target}"
24
- end
25
-
26
- # get all test file contents
27
- file_handler = Inspec::Targets.modules['file']
28
- res = {
29
- test: collect(helper, files, :get_filenames),
30
- library: collect(helper, files, :get_libraries),
31
- metadata: collect(helper, files, :get_metadata),
32
- }.map { |as, list|
33
- file_handler.resolve_all(list, base_folder: target, as: as)
34
- }
22
+ @files = files.map { |x| x.sub(prefix, '') }
23
+ @file_handler = Inspec::Targets.modules['file']
24
+ end
35
25
 
36
- # flatten the outer list layer
37
- res.inject(&:+)
26
+ def resolve(path, opts = {})
27
+ o = (opts || {})
28
+ o[:base_folder] = @base_folder
29
+ @file_handler.resolve(path, o)
38
30
  end
39
31
 
40
32
  def to_s
41
33
  'Folder Loader'
42
34
  end
43
-
44
- private
45
-
46
- def collect(helper, files, getter)
47
- return [] unless helper.respond_to? getter
48
- helper.method(getter).call(files)
49
- end
50
35
  end
51
36
 
52
- Inspec::Targets.add_module('folder', FolderHelper.new)
37
+ Inspec::Targets.add_module('folder', FolderHelper)
53
38
  end
@@ -8,7 +8,7 @@ require 'inspec/targets/archive'
8
8
 
9
9
  module Inspec::Targets
10
10
  class TarHelper < ArchiveHelper
11
- def handles?(target)
11
+ def self.handles?(target)
12
12
  File.file?(target) && target.end_with?('.tar.gz', '.tgz')
13
13
  end
14
14
 
@@ -30,22 +30,20 @@ module Inspec::Targets
30
30
  [files, rootdir]
31
31
  end
32
32
 
33
- def content(input, files, rootdir = nil, opts = {})
34
- content = []
33
+ def content(input, path, rootdir = nil, opts = {})
34
+ content = nil
35
35
  Gem::Package::TarReader.new(Zlib::GzipReader.open(input)) do |tar|
36
36
  tar.each do |entry|
37
37
  if entry.directory?
38
38
  # nothing to do
39
39
  elsif entry.file?
40
- if files.include?(entry.full_name.gsub(rootdir, ''))
41
- h = {
42
- # NB if some file is empty, return empty-string, not nil
43
- content: entry.read || '',
44
- type: opts[:as] || :test,
45
- ref: entry.full_name,
46
- }
47
- content.push(h)
48
- end
40
+ next unless path == entry.full_name.gsub(rootdir, '')
41
+ content = {
42
+ # NB if some file is empty, return empty-string, not nil
43
+ content: entry.read || '',
44
+ type: opts[:as] || :test,
45
+ ref: entry.full_name,
46
+ }
49
47
  elsif entry.header.typeflag == '2'
50
48
  # ignore symlinks for now
51
49
  end
@@ -59,5 +57,5 @@ module Inspec::Targets
59
57
  end
60
58
  end
61
59
 
62
- Inspec::Targets.add_module('tar', TarHelper.new)
60
+ Inspec::Targets.add_module('tar', TarHelper)
63
61
  end
@@ -8,22 +8,22 @@ require 'inspec/targets/archive'
8
8
 
9
9
  module Inspec::Targets
10
10
  class ZipHelper < ArchiveHelper
11
- def handles?(target)
11
+ def self.handles?(target)
12
12
  File.file?(target) and target.end_with?('.zip')
13
13
  end
14
14
 
15
- def content(input, files, rootdir = nil, opts = {})
16
- content = []
15
+ def content(input, path, rootdir = nil, opts = {})
16
+ content = nil
17
17
  ::Zip::InputStream.open(input) do |io|
18
18
  while (entry = io.get_next_entry)
19
- next if !files.include?(entry.name.gsub(rootdir, ''))
20
- h = {
19
+ next unless path == entry.name.gsub(rootdir, '')
20
+ content = {
21
21
  # NB if some file is empty, return empty-string, not nil
22
22
  content: io.read || '',
23
23
  type: opts[:as] || :test,
24
24
  ref: entry.name,
25
25
  }
26
- content.push(h)
26
+ abort
27
27
  end
28
28
  end
29
29
  content
@@ -51,5 +51,5 @@ module Inspec::Targets
51
51
  end
52
52
  end
53
53
 
54
- Inspec::Targets.add_module('zip', ZipHelper.new)
54
+ Inspec::Targets.add_module('zip', ZipHelper)
55
55
  end
@@ -3,5 +3,5 @@
3
3
  # author: Christoph Hartmann
4
4
 
5
5
  module Inspec
6
- VERSION = '0.11.0'.freeze
6
+ VERSION = '0.12.0'.freeze
7
7
  end
@@ -31,8 +31,8 @@ class IpTables < Inspec.resource(1)
31
31
  "
32
32
 
33
33
  def initialize(params = {})
34
- @table = params[:table] || nil
35
- @chain = params[:chain] || nil
34
+ @table = params[:table]
35
+ @chain = params[:chain]
36
36
 
37
37
  # we're done if we are on linux
38
38
  return if inspec.os.linux?
@@ -43,29 +43,26 @@ class IpTables < Inspec.resource(1)
43
43
  end
44
44
 
45
45
  def has_rule?(rule = nil, _table = nil, _chain = nil)
46
- found = false
47
- retrieve_rules.each { |line|
48
- # checks if the rule is part of the ruleset
49
- # for now, we expect an excact match
50
- found = true if line.casecmp(rule) == 0
51
- }
52
- found
46
+ # checks if the rule is part of the ruleset
47
+ # for now, we expect an exact match
48
+ retrieve_rules.any? { |line| line.casecmp(rule) == 0 }
53
49
  end
54
50
 
55
51
  def retrieve_rules
56
52
  return @iptables_cache if defined?(@iptables_cache)
57
53
 
58
54
  # construct iptables command to read all rules
59
- @table.nil? ? table_cmd = '' : table_cmd = " -t #{@table} "
60
- @chain.nil? ? chain_cmd = '' : chain_cmd = " #{@chain}"
61
- cmd = inspec.command(format('iptables %s -S %s', table_cmd, chain_cmd).strip)
55
+ table_cmd = "-t #{@table}" if @table
56
+ iptables_cmd = format('iptables %s -S %s', table_cmd, @chain).strip
57
+
58
+ cmd = inspec.command(iptables_cmd)
62
59
  return [] if cmd.exit_status.to_i != 0
63
60
 
64
61
  # split rules, returns array or rules
65
- @iptables_cache = cmd.stdout.chomp.split("\n")
62
+ @iptables_cache = cmd.stdout.split("\n").map(&:strip)
66
63
  end
67
64
 
68
65
  def to_s
69
- format('Iptables %s %s', @table.nil? ? '' : "table: #{@table}", @chain.nil? ? '' : "chain: #{@chain}").strip
66
+ format('Iptables %s %s', @table && "table: #{@table}", @chain && "chain: #{@chain}").strip
70
67
  end
71
68
  end
@@ -4,13 +4,59 @@
4
4
  # author: Stephan Renatus
5
5
  # license: All rights reserved
6
6
 
7
- # Usage:
8
- # describe service('dhcp') do
9
- # it { should be_enabled }
10
- # it { should be_installed }
11
- # it { should be_running }
12
- # end
13
- #
7
+ class Runlevels < Hash
8
+ attr_accessor :owner
9
+
10
+ def self.from_hash(owner, hash = {}, filter = nil)
11
+ res = Runlevels.new(owner)
12
+ filter = filter.first if filter.is_a?(Array) && filter.length <= 1
13
+
14
+ ks = case filter
15
+ when nil
16
+ hash.keys
17
+ when Regexp
18
+ hash.keys.find_all { |x| x.to_s =~ filter }
19
+ when Array
20
+ f = filter.map(&:to_s)
21
+ hash.keys.find_all { |x| f.include?(x.to_s) }
22
+ when Numeric
23
+ hash.keys.include?(filter) ? [filter] : []
24
+ else
25
+ hash.keys.find_all { |x| x == filter }
26
+ end
27
+
28
+ ks.each { |k| res[k] = hash[k] }
29
+ res
30
+ end
31
+
32
+ def initialize(owner, default = false)
33
+ @owner = owner
34
+ super(default)
35
+ end
36
+
37
+ def filter(f)
38
+ Runlevels.from_hash(owner, self, f)
39
+ end
40
+
41
+ # Check if all runlevels are enabled
42
+ #
43
+ # @return [boolean] true if all runlevels are enabled
44
+ def enabled?
45
+ values.all?
46
+ end
47
+
48
+ # Check if all runlevels are disabled
49
+ #
50
+ # @return [boolean] true if all runlevels are disabled
51
+ def disabled?
52
+ !values.any?
53
+ end
54
+
55
+ def to_s
56
+ "#{owner} runlevels #{keys.join(', ')}"
57
+ end
58
+ end
59
+
14
60
  # We detect the init system for each operating system, based on the operating
15
61
  # system.
16
62
  #
@@ -29,6 +75,10 @@ class Service < Inspec.resource(1)
29
75
  it { should be_enabled }
30
76
  it { should be_running }
31
77
  end
78
+
79
+ describe service('service_name').runlevels(3, 5) do
80
+ it { should be_enabled }
81
+ end
32
82
  "
33
83
 
34
84
  attr_reader :service_ctl
@@ -98,7 +148,7 @@ class Service < Inspec.resource(1)
98
148
  @cache ||= @service_mgmt.info(@service_name)
99
149
  end
100
150
 
101
- # verifies the service is enabled
151
+ # verifies if the service is enabled
102
152
  def enabled?(_level = nil)
103
153
  return false if info.nil?
104
154
  info[:enabled]
@@ -116,6 +166,12 @@ class Service < Inspec.resource(1)
116
166
  info[:running]
117
167
  end
118
168
 
169
+ # get all runlevels that are available and their configuration
170
+ def runlevels(*args)
171
+ return Runlevels.new(self) if info.nil? or info[:runlevels].nil?
172
+ Runlevels.from_hash(self, info[:runlevels], args)
173
+ end
174
+
119
175
  def to_s
120
176
  "Service #{@service_name}"
121
177
  end
@@ -275,6 +331,8 @@ class Upstart < ServiceManager
275
331
  end
276
332
 
277
333
  class SysV < ServiceManager
334
+ RUNLEVELS = { 0=>false, 1=>false, 2=>false, 3=>false, 4=>false, 5=>false, 6=>false }.freeze
335
+
278
336
  def initialize(service_name, service_ctl = nil)
279
337
  @service_ctl ||= 'service'
280
338
  super
@@ -296,10 +354,15 @@ class SysV < ServiceManager
296
354
  # on rhel via: 'chkconfig --list', is not installed by default
297
355
  # bash: for i in `find /etc/rc*.d -name S*`; do basename $i | sed -r 's/^S[0-9]+//'; done | sort | uniq
298
356
  enabled_services_cmd = inspec.command('find /etc/rc*.d -name S*')
299
- enabled_services = enabled_services_cmd.stdout.split("\n").select { |line|
300
- /(^.*#{service_name}.*)/.match(line)
301
- }
302
- enabled = !enabled_services.empty?
357
+ service_line = %r{rc(?<runlevel>[0-6])\.d/S[^/]*?#{Regexp.escape service_name}$}
358
+ all_services = enabled_services_cmd.stdout.split("\n").map { |line|
359
+ service_line.match(line)
360
+ }.compact
361
+ enabled = !all_services.empty?
362
+
363
+ # Determine a list of runlevels which this service is activated for
364
+ runlevels = RUNLEVELS.dup
365
+ all_services.each { |x| runlevels[x[:runlevel].to_i] = true }
303
366
 
304
367
  # check if service is really running
305
368
  # service throws an exit code if the service is not installed or
@@ -313,6 +376,7 @@ class SysV < ServiceManager
313
376
  installed: true,
314
377
  running: running,
315
378
  enabled: enabled,
379
+ runlevels: runlevels,
316
380
  type: 'sysv',
317
381
  }
318
382
  end
@@ -11,6 +11,7 @@ include_recipe('os_prepare::mount')
11
11
  include_recipe('os_prepare::service')
12
12
  include_recipe('os_prepare::package')
13
13
  include_recipe('os_prepare::registry_key')
14
+ include_recipe('os_prepare::iptables')
14
15
 
15
16
  # configure repos, eg. nginx
16
17
  include_recipe('os_prepare::apt')
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ # author: Stephan Renatus
3
+
4
+ case node['platform']
5
+ when 'ubuntu', 'rhel', 'centos', 'fedora'
6
+ execute 'iptables -A INPUT -i eth0 -p tcp -m tcp '\
7
+ '--dport 80 -m state --state NEW -m comment '\
8
+ '--comment "http on 80" -j ACCEPT'
9
+ execute 'iptables -N derby-cognos-web'
10
+ execute 'iptables -A INPUT -j derby-cognos-web'
11
+ execute 'iptables -A derby-cognos-web -p tcp -m tcp --dport 80 '\
12
+ '-m comment --comment "derby-cognos-web" -j ACCEPT'
13
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ case os[:family]
4
+ when 'ubuntu', 'fedora'
5
+ describe iptables do
6
+ it { should have_rule('-A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW -m comment --comment "http on 80" -j ACCEPT') }
7
+ it { should_not have_rule('-A INPUT -i eth1 -p tcp -m tcp --dport 80 -j ACCEPT') }
8
+
9
+ # single-word comments have their quotes dropped
10
+ it { should have_rule('-A derby-cognos-web -p tcp -m tcp --dport 80 -m comment --comment derby-cognos-web -j ACCEPT') }
11
+ end
12
+ when 'rhel', 'centos'
13
+ describe iptables do
14
+ it { should have_rule('-A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW -m comment --comment "http on 80" -j ACCEPT') }
15
+ it { should_not have_rule('-A INPUT -i eth1 -p tcp -m tcp --dport 80 -j ACCEPT') }
16
+ end
17
+
18
+ describe iptables do
19
+ it { should have_rule('-A derby-cognos-web -p tcp -m tcp --dport 80 -m comment --comment "derby-cognos-web" -j ACCEPT') }
20
+ end if os[:release] == 6
21
+
22
+ describe iptables do
23
+ it { should have_rule('-A derby-cognos-web -p tcp -m tcp --dport 80 -m comment --comment derby-cognos-web -j ACCEPT') }
24
+ end if os[:release] == 7
25
+ end
@@ -3,4 +3,4 @@
3
3
  -P OUTPUT ACCEPT
4
4
  -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
5
5
  -A INPUT -i eth0 -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT
6
- -A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW -j ACCEPT
6
+ -A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW -m comment --comment "http like its 1990" -j ACCEPT
@@ -14,6 +14,11 @@ describe 'Inspec::Resources::Iptables' do
14
14
  _(resource.has_rule?('-P OUTPUT DROP')).must_equal false
15
15
  end
16
16
 
17
+ it 'verify iptables with comments on ubuntu' do
18
+ resource = MockLoader.new(:ubuntu1404).load_resource('iptables')
19
+ _(resource.has_rule?('-A INPUT -i eth0 -p tcp -m tcp --dport 80 -m state --state NEW -m comment --comment "http like its 1990" -j ACCEPT')).must_equal true
20
+ end
21
+
17
22
  it 'verify iptables on windows' do
18
23
  resource = MockLoader.new(:windows).load_resource('iptables')
19
24
  _(resource.has_rule?('-P OUTPUT ACCEPT')).must_equal false
@@ -6,6 +6,7 @@ require 'helper'
6
6
  require 'inspec/resource'
7
7
 
8
8
  describe 'Inspec::Resources::Service' do
9
+ let(:runlevels) { {0=>false, 1=>false, 2=>true, 3=>true, 4=>true, 5=>true, 6=>false} }
9
10
 
10
11
  # windows
11
12
  it 'verify service parsing' do
@@ -58,7 +59,7 @@ describe 'Inspec::Resources::Service' do
58
59
  # centos 6 with sysv
59
60
  it 'verify centos 6 package parsing' do
60
61
  resource = MockLoader.new(:centos6).load_resource('service', 'sshd')
61
- srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, type: 'sysv' }
62
+ srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, runlevels: runlevels, type: 'sysv' }
62
63
  _(resource.info).must_equal srv
63
64
  _(resource.installed?).must_equal true
64
65
  _(resource.enabled?).must_equal true
@@ -67,7 +68,7 @@ describe 'Inspec::Resources::Service' do
67
68
 
68
69
  it 'verify centos 6 package parsing with default sysv_service' do
69
70
  resource = MockLoader.new(:centos6).load_resource('sysv_service', 'sshd')
70
- srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, type: 'sysv' }
71
+ srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, runlevels: runlevels, type: 'sysv' }
71
72
  _(resource.info).must_equal srv
72
73
  _(resource.installed?).must_equal true
73
74
  _(resource.enabled?).must_equal true
@@ -125,7 +126,7 @@ describe 'Inspec::Resources::Service' do
125
126
  # debian 7 with systemv
126
127
  it 'verify debian 7 package parsing' do
127
128
  resource = MockLoader.new(:debian7).load_resource('service', 'sshd')
128
- srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, type: 'sysv' }
129
+ srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, runlevels: runlevels, type: 'sysv' }
129
130
  _(resource.info).must_equal srv
130
131
  _(resource.installed?).must_equal true
131
132
  _(resource.enabled?).must_equal true
@@ -173,7 +174,7 @@ describe 'Inspec::Resources::Service' do
173
174
  # wrlinux
174
175
  it 'verify wrlinux package parsing' do
175
176
  resource = MockLoader.new(:wrlinux).load_resource('service', 'sshd')
176
- srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, type: 'sysv' }
177
+ srv = { name: 'sshd', description: nil, installed: true, running: true, enabled: true, runlevels: runlevels, type: 'sysv' }
177
178
  _(resource.info).must_equal srv
178
179
  _(resource.installed?).must_equal true
179
180
  _(resource.enabled?).must_equal true
@@ -187,4 +188,45 @@ describe 'Inspec::Resources::Service' do
187
188
  _(resource.installed?).must_equal false
188
189
  _(resource.info).must_equal nil
189
190
  end
191
+
192
+ # runlevel detection
193
+ describe 'runlevels on centos 6 (system V)' do
194
+ let(:service) { MockLoader.new(:centos6).load_resource('service', 'sshd') }
195
+
196
+ it 'grabs all runlevels' do
197
+ service.runlevels.keys.must_equal [0, 1, 2, 3, 4, 5, 6]
198
+ end
199
+
200
+ it 'grabs runlevels via filter nil' do
201
+ service.runlevels(nil).keys.must_equal [0, 1, 2, 3, 4, 5, 6]
202
+ end
203
+
204
+ it 'grabs runlevels by number' do
205
+ service.runlevels(3).keys.must_equal [3]
206
+ end
207
+
208
+ it 'grabs runlevels by multiple numbers' do
209
+ service.runlevels(3, 4, 8).keys.must_equal [3, 4]
210
+ end
211
+
212
+ it 'grabs runlevels via regex' do
213
+ service.runlevels(/[5-9]/).keys.must_equal [5, 6]
214
+ end
215
+
216
+ it 'checks enabled true if all services are enabled' do
217
+ service.runlevels(2, 4).enabled?.must_equal true
218
+ end
219
+
220
+ it 'checks enabled false if some services are not enabled' do
221
+ service.runlevels(1, 4).enabled?.must_equal false
222
+ end
223
+
224
+ it 'checks disabled true if all services are disabled' do
225
+ service.runlevels(0, 1).disabled?.must_equal true
226
+ end
227
+
228
+ it 'checks disabled false if some services are not disabled' do
229
+ service.runlevels(0, 4).enabled?.must_equal false
230
+ end
231
+ end
190
232
  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.11.0
4
+ version: 0.12.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Richter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-10 00:00:00.000000000 Z
11
+ date: 2016-02-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: r-train
@@ -223,6 +223,12 @@ files:
223
223
  - lib/bundles/inspec-compliance/cli.rb
224
224
  - lib/bundles/inspec-compliance/configuration.rb
225
225
  - lib/bundles/inspec-compliance/target.rb
226
+ - lib/bundles/inspec-init.rb
227
+ - lib/bundles/inspec-init/cli.rb
228
+ - lib/bundles/inspec-init/templates/profile/README.md
229
+ - lib/bundles/inspec-init/templates/profile/controls/example.rb
230
+ - lib/bundles/inspec-init/templates/profile/inspec.yml
231
+ - lib/bundles/inspec-init/templates/profile/libraries/.gitkeep
226
232
  - lib/bundles/inspec-supermarket/README.md
227
233
  - lib/bundles/inspec-supermarket/api.rb
228
234
  - lib/bundles/inspec-supermarket/cli.rb
@@ -339,6 +345,7 @@ files:
339
345
  - test/integration/cookbooks/os_prepare/recipes/auditctl.rb
340
346
  - test/integration/cookbooks/os_prepare/recipes/default.rb
341
347
  - test/integration/cookbooks/os_prepare/recipes/file.rb
348
+ - test/integration/cookbooks/os_prepare/recipes/iptables.rb
342
349
  - test/integration/cookbooks/os_prepare/recipes/json_yaml_csv_ini.rb
343
350
  - test/integration/cookbooks/os_prepare/recipes/mount.rb
344
351
  - test/integration/cookbooks/os_prepare/recipes/package.rb
@@ -356,6 +363,7 @@ files:
356
363
  - test/integration/test/integration/default/file_spec.rb
357
364
  - test/integration/test/integration/default/group_spec.rb
358
365
  - test/integration/test/integration/default/ini_spec.rb
366
+ - test/integration/test/integration/default/iptables_spec.rb
359
367
  - test/integration/test/integration/default/json_spec.rb
360
368
  - test/integration/test/integration/default/kernel_module_spec.rb
361
369
  - test/integration/test/integration/default/kernel_parameter_spec.rb
@@ -570,6 +578,7 @@ test_files:
570
578
  - test/integration/cookbooks/os_prepare/recipes/auditctl.rb
571
579
  - test/integration/cookbooks/os_prepare/recipes/default.rb
572
580
  - test/integration/cookbooks/os_prepare/recipes/file.rb
581
+ - test/integration/cookbooks/os_prepare/recipes/iptables.rb
573
582
  - test/integration/cookbooks/os_prepare/recipes/json_yaml_csv_ini.rb
574
583
  - test/integration/cookbooks/os_prepare/recipes/mount.rb
575
584
  - test/integration/cookbooks/os_prepare/recipes/package.rb
@@ -587,6 +596,7 @@ test_files:
587
596
  - test/integration/test/integration/default/file_spec.rb
588
597
  - test/integration/test/integration/default/group_spec.rb
589
598
  - test/integration/test/integration/default/ini_spec.rb
599
+ - test/integration/test/integration/default/iptables_spec.rb
590
600
  - test/integration/test/integration/default/json_spec.rb
591
601
  - test/integration/test/integration/default/kernel_module_spec.rb
592
602
  - test/integration/test/integration/default/kernel_parameter_spec.rb