inspec 0.15.0 → 0.16.0
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 +4 -4
- data/CHANGELOG.md +40 -2
- data/Gemfile +2 -3
- data/README.md +2 -0
- data/Rakefile +8 -0
- data/bin/inspec +1 -157
- data/docs/resources.rst +79 -78
- data/examples/profile/controls/example.rb +3 -1
- data/lib/fetchers/mock.rb +27 -0
- data/lib/fetchers/tar.rb +3 -2
- data/lib/fetchers/zip.rb +3 -1
- data/lib/inspec/cli.rb +164 -0
- data/lib/inspec/plugins/resource.rb +6 -2
- data/lib/inspec/profile.rb +28 -17
- data/lib/inspec/resource.rb +5 -1
- data/lib/inspec/rspec_json_formatter.rb +42 -0
- data/lib/inspec/rule.rb +24 -1
- data/lib/inspec/runner.rb +15 -7
- data/lib/inspec/runner_mock.rb +6 -1
- data/lib/inspec/runner_rspec.rb +29 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/{script.rb → powershell.rb} +19 -5
- data/lib/resources/registry_key.rb +1 -1
- data/test/{integration/cookbooks → cookbooks}/os_prepare/files/empty.iso +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/files/example.csv +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/files/example.ini +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/files/example.json +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/files/example.yml +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/metadata.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/_runit_service_centos.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/_upstart_service_centos.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/apache.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/apt.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/auditctl.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/default.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/file.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/iptables.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/json_yaml_csv_ini.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/mount.rb +2 -2
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/package.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/postgres.rb +6 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/registry_key.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/recipes/service.rb +0 -0
- data/test/{integration/cookbooks → cookbooks}/os_prepare/templates/default/sv-default-svlog-run.erb +0 -0
- data/test/functional/command_test.rb +390 -0
- data/test/helper.rb +6 -0
- data/test/integration/{test/integration/default → default}/_debug_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/apache_conf_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/apt_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/auditd_rules_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/compare_matcher_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/csv_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/etc_group_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/file_spec.rb +3 -2
- data/test/integration/{test/integration/default → default}/group_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/ini_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/iptables_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/json_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/kernel_module_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/kernel_parameter_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/mount_spec.rb +1 -1
- data/test/integration/{test/integration/default → default}/os_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/package_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/port_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/postgres_session_spec.rb +0 -0
- data/test/integration/default/powershell_spec.rb +13 -0
- data/test/integration/{test/integration/default → default}/registry_key_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/secpol_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/service_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/user_spec.rb +0 -0
- data/test/integration/{test/integration/default → default}/yaml_spec.rb +0 -0
- data/test/unit/control_test.rb +58 -0
- data/test/unit/fetchers/mock_test.rb +43 -0
- data/test/unit/plugins/resource_test.rb +60 -0
- data/test/unit/resources/{script_test.rb → powershell_test.rb} +10 -1
- metadata +107 -101
- data/test/integration/.kitchen.ec2.yml +0 -75
- data/test/integration/.kitchen.yml +0 -45
- data/test/integration/Berksfile +0 -5
|
@@ -8,7 +8,9 @@ title '/tmp profile'
|
|
|
8
8
|
control "tmp-1.0" do # A unique ID for this control
|
|
9
9
|
impact 0.7 # The criticality, if this control fails.
|
|
10
10
|
title "Create /tmp directory" # A human-readable title
|
|
11
|
-
desc "An optional description..."
|
|
11
|
+
desc "An optional description..." # Describe why this is needed
|
|
12
|
+
ref "Document A-12", url: 'http://...' # Additional references
|
|
13
|
+
|
|
12
14
|
describe file('/tmp') do # The actual test
|
|
13
15
|
it { should be_directory }
|
|
14
16
|
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
# author: Dominik Richter
|
|
3
|
+
# author: Christoph Hartmann
|
|
4
|
+
|
|
5
|
+
module Fetchers
|
|
6
|
+
class Mock < Inspec.fetcher(1)
|
|
7
|
+
name 'mock'
|
|
8
|
+
priority 0
|
|
9
|
+
|
|
10
|
+
def self.resolve(target)
|
|
11
|
+
return nil unless target.is_a? Hash
|
|
12
|
+
new(target)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize(data)
|
|
16
|
+
@data = data
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def files
|
|
20
|
+
@data.keys
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def read(file)
|
|
24
|
+
@data[file]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
data/lib/fetchers/tar.rb
CHANGED
|
@@ -13,8 +13,9 @@ module Fetchers
|
|
|
13
13
|
attr_reader :files
|
|
14
14
|
|
|
15
15
|
def self.resolve(target)
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
unless target.is_a?(String) && File.file?(target) && target.end_with?('.tar.gz', '.tgz')
|
|
17
|
+
return nil
|
|
18
|
+
end
|
|
18
19
|
new(target)
|
|
19
20
|
end
|
|
20
21
|
|
data/lib/fetchers/zip.rb
CHANGED
|
@@ -12,7 +12,9 @@ module Fetchers
|
|
|
12
12
|
attr_reader :files
|
|
13
13
|
|
|
14
14
|
def self.resolve(target)
|
|
15
|
-
|
|
15
|
+
unless target.is_a?(String) && File.file?(target) && target.end_with?('.zip')
|
|
16
|
+
return nil
|
|
17
|
+
end
|
|
16
18
|
new(target)
|
|
17
19
|
end
|
|
18
20
|
|
data/lib/inspec/cli.rb
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
# Copyright 2015 Dominik Richter. All rights reserved.
|
|
4
|
+
# author: Dominik Richter
|
|
5
|
+
# author: Christoph Hartmann
|
|
6
|
+
|
|
7
|
+
require 'thor'
|
|
8
|
+
require 'json'
|
|
9
|
+
require 'pp'
|
|
10
|
+
require 'utils/base_cli'
|
|
11
|
+
require 'utils/json_log'
|
|
12
|
+
|
|
13
|
+
class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
14
|
+
class_option :diagnose, type: :boolean,
|
|
15
|
+
desc: 'Show diagnostics (versions, configurations)'
|
|
16
|
+
|
|
17
|
+
desc 'json PATH', 'read all tests in PATH and generate a JSON summary'
|
|
18
|
+
option :id, type: :string,
|
|
19
|
+
desc: 'Attach a profile ID to all test results'
|
|
20
|
+
option :output, aliases: :o, type: :string,
|
|
21
|
+
desc: 'Save the created profile to a path'
|
|
22
|
+
profile_options
|
|
23
|
+
def json(target)
|
|
24
|
+
diagnose
|
|
25
|
+
o = opts.dup
|
|
26
|
+
o[:ignore_supports] = true
|
|
27
|
+
|
|
28
|
+
profile = Inspec::Profile.for_target(target, o)
|
|
29
|
+
dst = o[:output].to_s
|
|
30
|
+
if dst.empty?
|
|
31
|
+
puts JSON.dump(profile.info)
|
|
32
|
+
else
|
|
33
|
+
if File.exist? dst
|
|
34
|
+
puts "----> updating #{dst}"
|
|
35
|
+
else
|
|
36
|
+
puts "----> creating #{dst}"
|
|
37
|
+
end
|
|
38
|
+
fdst = File.expand_path(dst)
|
|
39
|
+
File.write(fdst, JSON.dump(profile.info))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
desc 'check PATH', 'verify all tests at the specified PATH'
|
|
44
|
+
option :format, type: :string
|
|
45
|
+
profile_options
|
|
46
|
+
def check(path) # rubocop:disable Metrics/AbcSize
|
|
47
|
+
diagnose
|
|
48
|
+
o = opts.dup
|
|
49
|
+
# configure_logger(o) # we do not need a logger for check yet
|
|
50
|
+
o[:ignore_supports] = true # we check for integrity only
|
|
51
|
+
|
|
52
|
+
# run check
|
|
53
|
+
profile = Inspec::Profile.for_target(path, o)
|
|
54
|
+
result = profile.check
|
|
55
|
+
|
|
56
|
+
if opts['format'] == 'json'
|
|
57
|
+
puts JSON.generate(result)
|
|
58
|
+
else
|
|
59
|
+
headline('Summary')
|
|
60
|
+
%w{location profile controls timestamp valid}.each { |item|
|
|
61
|
+
puts "#{mark_text(item.to_s.capitalize + ':')} #{result[:summary][item.to_sym]}"
|
|
62
|
+
}
|
|
63
|
+
puts
|
|
64
|
+
|
|
65
|
+
%w{errors warnings}.each { |list|
|
|
66
|
+
headline(list.to_s.capitalize)
|
|
67
|
+
result[list.to_sym].each { |item|
|
|
68
|
+
puts "#{item[:file]}:#{item[:line]}:#{item[:column]}: #{item[:msg]} "
|
|
69
|
+
}
|
|
70
|
+
puts
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
exit 1 unless result[:summary][:valid]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
desc 'archive PATH', 'archive a profile to tar.gz (default) or zip'
|
|
77
|
+
profile_options
|
|
78
|
+
option :output, aliases: :o, type: :string,
|
|
79
|
+
desc: 'Save the archive to a path'
|
|
80
|
+
option :zip, type: :boolean, default: false,
|
|
81
|
+
desc: 'Generates a zip archive.'
|
|
82
|
+
option :tar, type: :boolean, default: false,
|
|
83
|
+
desc: 'Generates a tar.gz archive.'
|
|
84
|
+
option :overwrite, type: :boolean, default: false,
|
|
85
|
+
desc: 'Overwrite existing archive.'
|
|
86
|
+
option :ignore_errors, type: :boolean, default: false,
|
|
87
|
+
desc: 'Ignore profile warnings.'
|
|
88
|
+
def archive(path)
|
|
89
|
+
diagnose
|
|
90
|
+
|
|
91
|
+
o = opts.dup
|
|
92
|
+
o[:logger] = Logger.new(STDOUT)
|
|
93
|
+
o[:logger].level = get_log_level(o.log_level)
|
|
94
|
+
|
|
95
|
+
profile = Inspec::Profile.for_target(path, o)
|
|
96
|
+
result = profile.check
|
|
97
|
+
|
|
98
|
+
if result && !opts[:ignore_errors] == false
|
|
99
|
+
@logger.info 'Profile check failed. Please fix the profile before generating an archive.'
|
|
100
|
+
return exit 1
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# generate archive
|
|
104
|
+
exit 1 unless profile.archive(opts)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
desc 'exec PATHS', 'run all test files at the specified PATH.'
|
|
108
|
+
exec_options
|
|
109
|
+
def exec(*targets)
|
|
110
|
+
diagnose
|
|
111
|
+
run_tests(targets, opts)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
desc 'detect', 'detect the target OS'
|
|
115
|
+
target_options
|
|
116
|
+
def detect
|
|
117
|
+
diagnose
|
|
118
|
+
|
|
119
|
+
rel = File.join(File.dirname(__FILE__), *%w{.. utils detect.rb})
|
|
120
|
+
detect_util = File.expand_path(rel)
|
|
121
|
+
# exits on execution:
|
|
122
|
+
runner = Inspec::Runner.new(opts)
|
|
123
|
+
profile = Inspec::Profile.for_target(detect_util, opts)
|
|
124
|
+
runner.add_profile(profile)
|
|
125
|
+
exit runner.run
|
|
126
|
+
rescue RuntimeError => e
|
|
127
|
+
puts e.message
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
desc 'shell', 'open an interactive debugging shell'
|
|
131
|
+
target_options
|
|
132
|
+
option :format, type: :string, default: Inspec::NoSummaryFormatter, hide: true
|
|
133
|
+
def shell_func
|
|
134
|
+
diagnose
|
|
135
|
+
o = opts.dup
|
|
136
|
+
o[:logger] = Logger.new(STDOUT)
|
|
137
|
+
o[:logger].level = get_log_level(o.log_level)
|
|
138
|
+
|
|
139
|
+
runner = Inspec::Runner.new(o)
|
|
140
|
+
Inspec::Shell.new(runner).start
|
|
141
|
+
rescue RuntimeError => e
|
|
142
|
+
puts e.message
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
desc 'version', 'prints the version of this tool'
|
|
146
|
+
def version
|
|
147
|
+
puts Inspec::VERSION
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Load all plugins on startup
|
|
152
|
+
ctl = Inspec::PluginCtl.new
|
|
153
|
+
ctl.list.each { |x| ctl.load(x) }
|
|
154
|
+
|
|
155
|
+
# load CLI plugins before the Inspec CLI has been started
|
|
156
|
+
Inspec::Plugins::CLI.subcommands.each { |_subcommand, params|
|
|
157
|
+
Inspec::InspecCLI.register(
|
|
158
|
+
params[:klass],
|
|
159
|
+
params[:subcommand_name],
|
|
160
|
+
params[:usage],
|
|
161
|
+
params[:description],
|
|
162
|
+
params[:options],
|
|
163
|
+
)
|
|
164
|
+
}
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
# author: Dominik Richter
|
|
3
3
|
# author: Christoph Hartmann
|
|
4
4
|
|
|
5
|
+
require 'inspec/resource'
|
|
6
|
+
|
|
5
7
|
module Inspec
|
|
6
8
|
module Plugins
|
|
7
9
|
class Resource
|
|
@@ -50,8 +52,10 @@ module Inspec
|
|
|
50
52
|
end
|
|
51
53
|
# rubocop:enable Lint/NestedMethodDefinition
|
|
52
54
|
|
|
53
|
-
# add the resource to the registry by name
|
|
54
|
-
|
|
55
|
+
# add the resource to the registry by name with a newly-named registry class
|
|
56
|
+
klass_name = name.split('_').map(&:capitalize).join
|
|
57
|
+
Inspec::Resource::Registry.const_set(klass_name, cl)
|
|
58
|
+
Inspec::Resource.registry[name] = Inspec::Resource::Registry.const_get(klass_name)
|
|
55
59
|
end
|
|
56
60
|
|
|
57
61
|
# Define methods which are available to all resources
|
data/lib/inspec/profile.rb
CHANGED
|
@@ -173,26 +173,17 @@ module Inspec
|
|
|
173
173
|
|
|
174
174
|
# generates a archive of a folder profile
|
|
175
175
|
# assumes that the profile was checked before
|
|
176
|
-
def archive(opts)
|
|
177
|
-
profile_name = params[:name]
|
|
178
|
-
ext = opts[:zip] ? 'zip' : 'tar.gz'
|
|
179
|
-
|
|
180
|
-
if opts[:archive]
|
|
181
|
-
archive = Pathname.new(opts[:archive])
|
|
182
|
-
else
|
|
183
|
-
slug = profile_name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
|
|
184
|
-
archive = Pathname.new(Dir.pwd).join("#{slug}.#{ext}")
|
|
185
|
-
end
|
|
186
|
-
|
|
176
|
+
def archive(opts)
|
|
187
177
|
# check if file exists otherwise overwrite the archive
|
|
188
|
-
|
|
189
|
-
|
|
178
|
+
dst = archive_name(opts)
|
|
179
|
+
if dst.exist? && !opts[:overwrite]
|
|
180
|
+
@logger.info "Archive #{dst} exists already. Use --overwrite."
|
|
190
181
|
return false
|
|
191
182
|
end
|
|
192
183
|
|
|
193
184
|
# remove existing archive
|
|
194
|
-
File.delete(
|
|
195
|
-
@logger.info "Generate archive #{
|
|
185
|
+
File.delete(dst) if dst.exist?
|
|
186
|
+
@logger.info "Generate archive #{dst}."
|
|
196
187
|
|
|
197
188
|
# filter files that should not be part of the profile
|
|
198
189
|
# TODO ignore all .files, but add the files to debug output
|
|
@@ -207,12 +198,12 @@ module Inspec
|
|
|
207
198
|
# generate zip archive
|
|
208
199
|
require 'inspec/archive/zip'
|
|
209
200
|
zag = Inspec::Archive::ZipArchiveGenerator.new
|
|
210
|
-
zag.archive(root_path, files,
|
|
201
|
+
zag.archive(root_path, files, dst)
|
|
211
202
|
else
|
|
212
203
|
# generate tar archive
|
|
213
204
|
require 'inspec/archive/tar'
|
|
214
205
|
tag = Inspec::Archive::TarArchiveGenerator.new
|
|
215
|
-
tag.archive(root_path, files,
|
|
206
|
+
tag.archive(root_path, files, dst)
|
|
216
207
|
end
|
|
217
208
|
|
|
218
209
|
@logger.info 'Finished archive generation.'
|
|
@@ -221,6 +212,24 @@ module Inspec
|
|
|
221
212
|
|
|
222
213
|
private
|
|
223
214
|
|
|
215
|
+
# Create an archive name for this profile and an additional options
|
|
216
|
+
# configuration. Either use :output or generate the name from metadata.
|
|
217
|
+
#
|
|
218
|
+
# @param [Hash] configuration options
|
|
219
|
+
# @return [Pathname] path for the archive
|
|
220
|
+
def archive_name(opts)
|
|
221
|
+
if (name = opts[:output])
|
|
222
|
+
return Pathname.new(name)
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
name = params[:name] ||
|
|
226
|
+
fail('Cannot create an archive without a profile name! Please '\
|
|
227
|
+
'specify the name in metadata or use --output to create the archive.')
|
|
228
|
+
ext = opts[:zip] ? 'zip' : 'tar.gz'
|
|
229
|
+
slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_')
|
|
230
|
+
Pathname.new(Dir.pwd).join("#{slug}.#{ext}")
|
|
231
|
+
end
|
|
232
|
+
|
|
224
233
|
def load_params
|
|
225
234
|
params = @source_reader.metadata.params
|
|
226
235
|
params[:name] = @profile_id unless @profile_id.nil?
|
|
@@ -245,6 +254,8 @@ module Inspec
|
|
|
245
254
|
title: rule.title,
|
|
246
255
|
desc: rule.desc,
|
|
247
256
|
impact: rule.impact,
|
|
257
|
+
refs: rule.ref,
|
|
258
|
+
tags: rule.tag,
|
|
248
259
|
checks: rule.instance_variable_get(:@checks),
|
|
249
260
|
code: rule.instance_variable_get(:@__code),
|
|
250
261
|
source_location: rule.instance_variable_get(:@__source_location),
|
data/lib/inspec/resource.rb
CHANGED
|
@@ -8,6 +8,10 @@ require 'inspec/plugins'
|
|
|
8
8
|
|
|
9
9
|
module Inspec
|
|
10
10
|
class Resource
|
|
11
|
+
class Registry
|
|
12
|
+
# empty class for namespacing resource classes in the registry
|
|
13
|
+
end
|
|
14
|
+
|
|
11
15
|
def self.registry
|
|
12
16
|
@registry ||= {}
|
|
13
17
|
end
|
|
@@ -84,9 +88,9 @@ require 'resources/port'
|
|
|
84
88
|
require 'resources/postgres'
|
|
85
89
|
require 'resources/postgres_conf'
|
|
86
90
|
require 'resources/postgres_session'
|
|
91
|
+
require 'resources/powershell'
|
|
87
92
|
require 'resources/processes'
|
|
88
93
|
require 'resources/registry_key'
|
|
89
|
-
require 'resources/script'
|
|
90
94
|
require 'resources/security_policy'
|
|
91
95
|
require 'resources/service'
|
|
92
96
|
require 'resources/shadow'
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# author: Christoph Hartmann
|
|
4
4
|
|
|
5
5
|
require 'rspec/core'
|
|
6
|
+
require 'rspec/core/formatters/json_formatter'
|
|
6
7
|
|
|
7
8
|
# Extend the basic RSpec JSON Formatter
|
|
8
9
|
# to give us an ID in its output
|
|
@@ -26,3 +27,44 @@ module RSpec::Core::Formatters
|
|
|
26
27
|
end
|
|
27
28
|
end
|
|
28
29
|
end
|
|
30
|
+
|
|
31
|
+
class InspecRspecFormatter < RSpec::Core::Formatters::JsonFormatter
|
|
32
|
+
RSpec::Core::Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :close
|
|
33
|
+
|
|
34
|
+
def add_profile(profile)
|
|
35
|
+
@profiles ||= []
|
|
36
|
+
@profiles.push(profile)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def dump_summary(summary)
|
|
40
|
+
super(summary)
|
|
41
|
+
@output_hash[:profiles] = @profiles.map do |profile|
|
|
42
|
+
r = profile.params.dup
|
|
43
|
+
r.delete(:rules)
|
|
44
|
+
r
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def format_example(example)
|
|
51
|
+
res = {
|
|
52
|
+
id: example.metadata[:id],
|
|
53
|
+
title: example.metadata[:title],
|
|
54
|
+
desc: example.metadata[:desc],
|
|
55
|
+
code: example.metadata[:code],
|
|
56
|
+
impact: example.metadata[:impact],
|
|
57
|
+
status: example.execution_result.status.to_s,
|
|
58
|
+
code_desc: example.full_description,
|
|
59
|
+
ref: example.metadata['file_path'],
|
|
60
|
+
ref_line: example.metadata['line_number'],
|
|
61
|
+
run_time: example.execution_result.run_time,
|
|
62
|
+
start_time: example.execution_result.started_at.to_s,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# pending messages are embedded in the resources description
|
|
66
|
+
res[:pending] = example.metadata[:description] if res[:status] == 'pending'
|
|
67
|
+
|
|
68
|
+
res
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/inspec/rule.rb
CHANGED
|
@@ -9,7 +9,7 @@ require 'inspec/describe'
|
|
|
9
9
|
require 'inspec/expect'
|
|
10
10
|
|
|
11
11
|
module Inspec
|
|
12
|
-
class Rule
|
|
12
|
+
class Rule # rubocop:disable Metrics/ClassLength
|
|
13
13
|
include ::RSpec::Matchers
|
|
14
14
|
|
|
15
15
|
def initialize(id, _opts, &block)
|
|
@@ -20,6 +20,8 @@ module Inspec
|
|
|
20
20
|
@__source_location = __get_block_source_location(&block)
|
|
21
21
|
@title = nil
|
|
22
22
|
@desc = nil
|
|
23
|
+
@refs = []
|
|
24
|
+
@tags = {}
|
|
23
25
|
# not changeable by the user:
|
|
24
26
|
@profile_id = nil
|
|
25
27
|
@checks = []
|
|
@@ -47,6 +49,27 @@ module Inspec
|
|
|
47
49
|
@desc
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
def ref(ref = nil, opts = {})
|
|
53
|
+
return @refs if ref.nil? && opts.empty?
|
|
54
|
+
if opts.empty? && ref.is_a?(Hash)
|
|
55
|
+
opts = ref
|
|
56
|
+
else
|
|
57
|
+
opts[:ref] = ref
|
|
58
|
+
end
|
|
59
|
+
@refs.push(opts)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def tag(*args)
|
|
63
|
+
args.each do |arg|
|
|
64
|
+
if arg.is_a?(Hash)
|
|
65
|
+
@tags.merge!(arg)
|
|
66
|
+
else
|
|
67
|
+
@tags[arg] ||= nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
@tags
|
|
71
|
+
end
|
|
72
|
+
|
|
50
73
|
# Describe will add one or more tests to this control. There is 2 ways
|
|
51
74
|
# of calling it:
|
|
52
75
|
#
|