inspec-core 2.2.35 → 2.2.41
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 +21 -10
- data/README.md +7 -5
- data/bin/inspec +0 -0
- data/docs/dsl_inspec.md +35 -39
- data/docs/glossary.md +15 -15
- data/docs/habitat.md +10 -9
- data/docs/inspec_and_friends.md +4 -4
- data/docs/matchers.md +1 -9
- data/docs/plugin_kitchen_inspec.md +34 -24
- data/docs/profiles.md +217 -156
- data/docs/reporters.md +13 -4
- data/docs/resources/command.md.erb +28 -0
- data/docs/resources/registry_key.md.erb +5 -2
- data/docs/resources/xinetd_conf.md.erb +1 -1
- data/docs/ruby_usage.md +4 -3
- data/lib/bundles/inspec-init.rb +4 -0
- data/lib/bundles/inspec-init/cli.rb +9 -72
- data/lib/bundles/inspec-init/renderer.rb +79 -0
- data/lib/inspec/base_cli.rb +25 -12
- data/lib/inspec/objects/describe.rb +8 -1
- data/lib/inspec/plugins/resource.rb +2 -2
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/command.rb +17 -2
- data/lib/resources/package.rb +26 -2
- metadata +3 -2
data/docs/reporters.md
CHANGED
@@ -116,12 +116,12 @@ This reporter includes all information from the rspec runner. Unlike the json re
|
|
116
116
|
|
117
117
|
This renders html code to view your tests in a browser. It includes all the test and summary information.
|
118
118
|
|
119
|
-
|
120
119
|
## Automate Reporter
|
121
120
|
|
122
121
|
The automate reporter type is a special reporter used with the Automate 2 suite. To use this reporter you must pass in the correct configuration via a json config `--json-config`.
|
123
122
|
|
124
123
|
Example config:
|
124
|
+
|
125
125
|
```json
|
126
126
|
"reporter": {
|
127
127
|
"automate" : {
|
@@ -135,27 +135,36 @@ Example config:
|
|
135
135
|
}
|
136
136
|
```
|
137
137
|
|
138
|
-
### Mandatory fields
|
138
|
+
### Mandatory fields
|
139
|
+
|
139
140
|
#### stdout
|
141
|
+
|
140
142
|
This will either suppress or show the automate report in the CLI screen on completion
|
141
143
|
|
142
144
|
#### url
|
145
|
+
|
143
146
|
This is your Automate 2 url. Append `data-collector/v0/` at the end.
|
144
147
|
|
145
148
|
#### token
|
149
|
+
|
146
150
|
This is your Automate 2 token. You can generate this token by navigating to the admin tab of A2 and then api keys.
|
147
151
|
|
148
152
|
### Optional fields
|
153
|
+
|
149
154
|
#### insecure
|
155
|
+
|
150
156
|
This will disable or enable the ssl check when accessing the Automate 2 instance.
|
151
157
|
|
152
|
-
PLEASE NOTE: These fields are ONLY needed if you do not have chef-client attached to a chef server running on your node. The fields below will be
|
158
|
+
PLEASE NOTE: These fields are ONLY needed if you do not have chef-client attached to a chef server running on your node. The fields below will be automatically pulled from the chef server.
|
153
159
|
|
154
160
|
#### node_name
|
161
|
+
|
155
162
|
This will be the node name which shows up in Automate 2.
|
156
163
|
|
157
164
|
#### node_uuid
|
165
|
+
|
158
166
|
This overrides the node uuid sent up to Automate 2. On non-chef nodes we will try to generate a static node uuid for you from your hardware. This will almost never be needed unless your working with a unique virtual setup.
|
159
167
|
|
160
168
|
#### environment
|
161
|
-
|
169
|
+
|
170
|
+
This will set the environment metadata for Automate 2.
|
@@ -124,6 +124,34 @@ Wix includes several tools -- such as `candle` (preprocesses and compiles source
|
|
124
124
|
end
|
125
125
|
end
|
126
126
|
|
127
|
+
### Redacting Sensitive Commands
|
128
|
+
|
129
|
+
By default the command that is ran is shown in the InSpec output. This can be problematic if the command contains sensitive arguments such as a password. These sensitive parts can be redacted by passing in `redact_regex` and a regular expression to redact. Optionally, you can use 2 capture groups to fine tune what is redacted.
|
130
|
+
|
131
|
+
The following examples show how to use `redact_regex`:
|
132
|
+
|
133
|
+
# Example without capture groups
|
134
|
+
describe command('myapp -p secretpassword -d no_redact', redact_regex: /-p .* -d/) do
|
135
|
+
its('exit_status') { should cmp 0 }
|
136
|
+
end
|
137
|
+
|
138
|
+
# Result (no capture groups used)
|
139
|
+
Command: `myapp REDACTED no_redact`
|
140
|
+
✔ exit_status should cmp == 0
|
141
|
+
|
142
|
+
# Example with capture groups
|
143
|
+
# Each set of parenthesis is a capture group.
|
144
|
+
# Anything in the two capture groups will not be 'REDACTED'
|
145
|
+
describe command('myapp -p secretpassword -d no_redact', redact_regex: /(-p ).*( -d)/) do
|
146
|
+
its('exit_status') { should cmp 0 }
|
147
|
+
end
|
148
|
+
|
149
|
+
# Result (capture groups used)
|
150
|
+
Command: `myapp -p REDACTED -d no_redact`
|
151
|
+
✔ exit_status should cmp == 0
|
152
|
+
|
153
|
+
> For more info/help on regular expressions, we recommend [RegExr](https://regexr.com/)
|
154
|
+
|
127
155
|
<br>
|
128
156
|
|
129
157
|
## Matchers
|
@@ -149,10 +149,13 @@ The `name` matcher tests the value for the specified registry setting:
|
|
149
149
|
|
150
150
|
|
151
151
|
<p class="warning">
|
152
|
-
Any name with a dot will not work as expected: <code>its('explorer.exe') { should eq 'test' }</code>.
|
152
|
+
Any name with a dot will not work as expected: <code>its('explorer.exe') { should eq 'test' }</code>. For details, see <a href="https://github.com/inspec/inspec/issues/1281">https://github.com/inspec/inspec/issues/1281</a>
|
153
153
|
</p>
|
154
154
|
|
155
155
|
# instead of:
|
156
156
|
# its('explorer.exe') { should eq 'test' }
|
157
|
-
# use
|
157
|
+
# either use have_property_value...
|
158
158
|
it { should have_property_value('explorer.exe', :string, 'test') }
|
159
|
+
|
160
|
+
# ...or provide the parts of the dot-separated name in an array
|
161
|
+
its(['explorer', 'exe']) { should eq 'test' }
|
@@ -13,7 +13,7 @@ Use the `xinetd_conf` InSpec audit resource to test services under `/etc/xinet.d
|
|
13
13
|
|
14
14
|
An `xinetd_conf` resource block declares settings found in a `xinetd.conf` file for the named service:
|
15
15
|
|
16
|
-
describe xinetd_conf('service_name') do
|
16
|
+
describe xinetd_conf.services('service_name') do
|
17
17
|
it { should be_enabled } # or be_disabled
|
18
18
|
its('setting') { should eq 'value' }
|
19
19
|
end
|
data/docs/ruby_usage.md
CHANGED
@@ -61,7 +61,6 @@ to process the output of `ls` executed on the target system, use
|
|
61
61
|
Similarly, use `inspec.file(PATH)` to access files or directories from
|
62
62
|
remote systems in your tests or custom resources.
|
63
63
|
|
64
|
-
|
65
64
|
## Using rubygems
|
66
65
|
|
67
66
|
Ruby gems are self-contained programs and libraries. If you create a custom
|
@@ -158,7 +157,8 @@ When writing tests you can not use standard ruby methods to shellout as it tries
|
|
158
157
|
However, the `command` resource has a `.stdout` method that will allow you to manipulate the results.
|
159
158
|
Using the above example, you could check the writes on several subdirectories.
|
160
159
|
|
161
|
-
### Example 1
|
160
|
+
### Example 1
|
161
|
+
|
162
162
|
```ruby
|
163
163
|
$ inspec shell
|
164
164
|
Welcome to the interactive InSpec Shell
|
@@ -179,7 +179,8 @@ Version: (not specified)
|
|
179
179
|
Test Summary: 1 successful, 0 failures, 0 skipped
|
180
180
|
```
|
181
181
|
|
182
|
-
### Example 2
|
182
|
+
### Example 2
|
183
|
+
|
183
184
|
```ruby
|
184
185
|
$ inspec shell
|
185
186
|
Welcome to the interactive InSpec Shell
|
data/lib/bundles/inspec-init.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'pathname'
|
4
|
+
require_relative 'renderer'
|
4
5
|
|
5
6
|
module Init
|
6
7
|
class CLI < Inspec::BaseCLI
|
@@ -15,85 +16,21 @@ module Init
|
|
15
16
|
namespace
|
16
17
|
end
|
17
18
|
|
18
|
-
#
|
19
|
+
# Look in the 'template' directory, and register a subcommand
|
20
|
+
# for each template directory found there.
|
19
21
|
template_dir = File.join(File.dirname(__FILE__), 'templates')
|
20
22
|
Dir.glob(File.join(template_dir, '*')) do |template|
|
21
|
-
|
23
|
+
template_name = Pathname.new(template).relative_path_from(Pathname.new(template_dir)).to_s
|
22
24
|
|
23
25
|
# register command for the template
|
24
|
-
desc "#{
|
26
|
+
desc "#{template_name} NAME", "Create a new #{template_name}"
|
25
27
|
option :overwrite, type: :boolean, default: false,
|
26
|
-
|
27
|
-
define_method
|
28
|
-
|
28
|
+
desc: 'Overwrites existing directory'
|
29
|
+
define_method template_name.to_sym do |name_for_new_structure|
|
30
|
+
renderer = Init::Renderer.new(self, options)
|
31
|
+
renderer.render_with_values(template_name, name: name_for_new_structure)
|
29
32
|
end
|
30
33
|
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
# 1. iterate over all files
|
35
|
-
# 2. read content in erb
|
36
|
-
# 3. write to target
|
37
|
-
def generator(type, attributes = {}, options = {}) # rubocop:disable Metrics/AbcSize
|
38
|
-
# path of this script
|
39
|
-
dir = File.dirname(__FILE__)
|
40
|
-
# look for template directory
|
41
|
-
base_dir = File.join(dir, 'templates', type)
|
42
|
-
# prepare glob for all subdirectories and files
|
43
|
-
template = File.join(base_dir, '**', '{*,.*}')
|
44
|
-
# Use the name attribute to define the path to the profile.
|
45
|
-
profile_path = attributes[:name]
|
46
|
-
# Use slashes (\, /) to split up the name into an Array then use the last entry
|
47
|
-
# to reset the name of the profile.
|
48
|
-
attributes[:name] = attributes[:name].split(%r{\\|\/}).last
|
49
|
-
# Generate the full target path on disk
|
50
|
-
target = Pathname.new(Dir.pwd).join(profile_path)
|
51
|
-
puts "Create new #{type} at #{mark_text(target)}"
|
52
|
-
|
53
|
-
# check that the directory does not exist
|
54
|
-
if File.exist?(target) && !options['overwrite']
|
55
|
-
error "#{mark_text(target)} exists already, use --overwrite"
|
56
|
-
exit 1
|
57
|
-
end
|
58
|
-
|
59
|
-
# ensure that target directory is available
|
60
|
-
FileUtils.mkdir_p(target)
|
61
|
-
|
62
|
-
# iterate over files and write to target path
|
63
|
-
Dir.glob(template) do |file|
|
64
|
-
relative = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
|
65
|
-
destination = Pathname.new(target).join(relative)
|
66
|
-
if File.directory?(file)
|
67
|
-
li "Create directory #{mark_text(relative)}"
|
68
|
-
FileUtils.mkdir_p(destination)
|
69
|
-
elsif File.file?(file)
|
70
|
-
li "Create file #{mark_text(relative)}"
|
71
|
-
# read & render content
|
72
|
-
content = render(File.read(file), attributes)
|
73
|
-
# write file content
|
74
|
-
File.write(destination, content)
|
75
|
-
else
|
76
|
-
puts "Ignore #{file}, because its not an file or directoy"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
# This is a render helper to bind hash values to a ERB template
|
82
|
-
def render(content, hash)
|
83
|
-
# create a new binding class
|
84
|
-
cls = Class.new do
|
85
|
-
hash.each do |key, value|
|
86
|
-
define_method key.to_sym do
|
87
|
-
value
|
88
|
-
end
|
89
|
-
end
|
90
|
-
# expose binding
|
91
|
-
define_method :bind do
|
92
|
-
binding
|
93
|
-
end
|
94
|
-
end
|
95
|
-
ERB.new(content).result(cls.new.bind)
|
96
|
-
end
|
97
34
|
end
|
98
35
|
|
99
36
|
# register the subcommand to Inspec CLI registry
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Init
|
5
|
+
class Renderer
|
6
|
+
# Creates a renderer able to render the given template type
|
7
|
+
# 1. iterate over all files
|
8
|
+
# 2. read content in erb
|
9
|
+
# 3. write to full_destination_root_path
|
10
|
+
|
11
|
+
attr_reader :overwrite_mode, :ui
|
12
|
+
def initialize(cli_ui, cli_options = {})
|
13
|
+
@ui = cli_ui
|
14
|
+
@overwrite_mode = cli_options['overwrite']
|
15
|
+
end
|
16
|
+
|
17
|
+
# rubocop: disable Metrics/AbcSize
|
18
|
+
def render_with_values(template_type, template_values = {})
|
19
|
+
# look for template directory
|
20
|
+
base_dir = File.join(File.dirname(__FILE__), 'templates', template_type)
|
21
|
+
# prepare glob for all subdirectories and files
|
22
|
+
template_glob = File.join(base_dir, '**', '{*,.*}')
|
23
|
+
# Use the name attribute to define the path to the profile.
|
24
|
+
profile_path = template_values[:name]
|
25
|
+
# Use slashes (\, /) to split up the name into an Array then use the last entry
|
26
|
+
# to reset the name of the profile.
|
27
|
+
template_values[:name] = template_values[:name].split(%r{\\|\/}).last
|
28
|
+
# Generate the full full_destination_root_path path on disk
|
29
|
+
full_destination_root_path = Pathname.new(Dir.pwd).join(profile_path)
|
30
|
+
ui.plain_text "Create new #{template_type} at #{ui.mark_text(full_destination_root_path)}"
|
31
|
+
|
32
|
+
# check that the directory does not exist
|
33
|
+
if File.exist?(full_destination_root_path) && !overwrite_mode
|
34
|
+
ui.plain_text "#{ui.mark_text(full_destination_root_path)} exists already, use --overwrite"
|
35
|
+
ui.exit(1)
|
36
|
+
end
|
37
|
+
|
38
|
+
# ensure that full_destination_root_path directory is available
|
39
|
+
FileUtils.mkdir_p(full_destination_root_path)
|
40
|
+
|
41
|
+
# iterate over files and write to full_destination_root_path
|
42
|
+
Dir.glob(template_glob) do |file|
|
43
|
+
relative_destination_item_path = Pathname.new(file).relative_path_from(Pathname.new(base_dir))
|
44
|
+
full_destination_item_path = Pathname.new(full_destination_root_path).join(relative_destination_item_path)
|
45
|
+
if File.directory?(file)
|
46
|
+
ui.li "Create directory #{ui.mark_text(relative_destination_item_path)}"
|
47
|
+
FileUtils.mkdir_p(full_destination_item_path)
|
48
|
+
elsif File.file?(file)
|
49
|
+
ui.li "Create file #{ui.mark_text(relative_destination_item_path)}"
|
50
|
+
# read & render content
|
51
|
+
content = render(File.read(file), template_values)
|
52
|
+
# write file content
|
53
|
+
File.write(full_destination_item_path, content)
|
54
|
+
else
|
55
|
+
ui.plain_text "Ignore #{file}, because its not an file or directoy"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
# rubocop: enable Metrics/AbcSize
|
60
|
+
|
61
|
+
# This is a render helper to bind hash values to a ERB template
|
62
|
+
# ERB provides result_with_hash in ruby 2.5.0+, which does exactly this
|
63
|
+
def render(template_content, hash)
|
64
|
+
# create a new binding class
|
65
|
+
cls = Class.new do
|
66
|
+
hash.each do |key, value|
|
67
|
+
define_method key.to_sym do
|
68
|
+
value
|
69
|
+
end
|
70
|
+
end
|
71
|
+
# expose binding
|
72
|
+
define_method :bind do
|
73
|
+
binding
|
74
|
+
end
|
75
|
+
end
|
76
|
+
ERB.new(template_content).result(cls.new.bind)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -210,6 +210,31 @@ module Inspec
|
|
210
210
|
str
|
211
211
|
end
|
212
212
|
|
213
|
+
# These need to be public methods on any BaseCLI instance,
|
214
|
+
# but Thor interprets all methods as subcommands. The no_commands block
|
215
|
+
# treats them as regular methods.
|
216
|
+
no_commands do
|
217
|
+
def mark_text(text)
|
218
|
+
"\e[0;36m#{text}\e[0m"
|
219
|
+
end
|
220
|
+
|
221
|
+
def headline(title)
|
222
|
+
puts "\n== #{title}\n\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
def li(entry)
|
226
|
+
puts " #{mark_text('*')} #{entry}"
|
227
|
+
end
|
228
|
+
|
229
|
+
def exit(code)
|
230
|
+
Kernel.exit code
|
231
|
+
end
|
232
|
+
|
233
|
+
def plain_text(msg)
|
234
|
+
puts msg
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
213
238
|
private
|
214
239
|
|
215
240
|
def suppress_log_output?(opts)
|
@@ -363,17 +388,5 @@ module Inspec
|
|
363
388
|
end
|
364
389
|
o[:logger].level = get_log_level(o.log_level)
|
365
390
|
end
|
366
|
-
|
367
|
-
def mark_text(text)
|
368
|
-
"\e[0;36m#{text}\e[0m"
|
369
|
-
end
|
370
|
-
|
371
|
-
def headline(title)
|
372
|
-
puts "\n== #{title}\n\n"
|
373
|
-
end
|
374
|
-
|
375
|
-
def li(entry)
|
376
|
-
puts " #{mark_text('*')} #{entry}"
|
377
|
-
end
|
378
391
|
end
|
379
392
|
end
|
@@ -11,7 +11,14 @@ module Inspec
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def to_ruby
|
14
|
-
itsy =
|
14
|
+
itsy = 'it'
|
15
|
+
unless its.nil?
|
16
|
+
if its.is_a? Array
|
17
|
+
itsy = 'its(' + its.inspect + ')'
|
18
|
+
else
|
19
|
+
itsy = 'its(' + its.to_s.inspect + ')'
|
20
|
+
end
|
21
|
+
end
|
15
22
|
naughty = negated ? '_not' : ''
|
16
23
|
xpect = if expectation.nil?
|
17
24
|
''
|
@@ -90,8 +90,8 @@ module Inspec
|
|
90
90
|
|
91
91
|
def check_supports
|
92
92
|
status = inspec.platform.supported?(@supports)
|
93
|
-
|
94
|
-
|
93
|
+
fail_msg = "Resource #{@__resource_name__.capitalize} is not supported on platform #{inspec.platform.name}/#{inspec.platform.release}."
|
94
|
+
fail_resource(fail_msg) unless status
|
95
95
|
status
|
96
96
|
end
|
97
97
|
|
data/lib/inspec/version.rb
CHANGED
data/lib/resources/command.rb
CHANGED
@@ -22,11 +22,22 @@ module Inspec::Resources
|
|
22
22
|
|
23
23
|
attr_reader :command
|
24
24
|
|
25
|
-
def initialize(cmd)
|
25
|
+
def initialize(cmd, options = {})
|
26
26
|
if cmd.nil?
|
27
27
|
raise 'InSpec `command` was called with `nil` as the argument. This is not supported. Please provide a valid command instead.'
|
28
28
|
end
|
29
|
+
|
29
30
|
@command = cmd
|
31
|
+
|
32
|
+
if options[:redact_regex]
|
33
|
+
unless options[:redact_regex].is_a?(Regexp)
|
34
|
+
# Make sure command is replaced so sensitive output isn't shown
|
35
|
+
@command = 'ERROR'
|
36
|
+
raise Inspec::Exceptions::ResourceFailed,
|
37
|
+
'The `redact_regex` option must be a regular expression'
|
38
|
+
end
|
39
|
+
@redact_regex = options[:redact_regex]
|
40
|
+
end
|
30
41
|
end
|
31
42
|
|
32
43
|
def result
|
@@ -67,7 +78,11 @@ module Inspec::Resources
|
|
67
78
|
end
|
68
79
|
|
69
80
|
def to_s
|
70
|
-
"Command
|
81
|
+
output = "Command: `#{@command}`"
|
82
|
+
# Redact output if the `redact_regex` option is passed
|
83
|
+
# If no capture groups are passed then `\1` and `\2` are ignored
|
84
|
+
output.gsub!(@redact_regex, '\1REDACTED\2') unless @redact_regex.nil?
|
85
|
+
output
|
71
86
|
end
|
72
87
|
end
|
73
88
|
end
|