inspec-core 2.2.35 → 2.2.41
Sign up to get free protection for your applications and to get access to all the features.
- 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
|