inspec 1.45.9 → 1.45.13
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/.rubocop.yml +54 -32
- data/CHANGELOG.md +29 -21
- data/Gemfile +1 -1
- data/docs/resources/ini.md.erb +14 -1
- data/docs/shell.md +1 -1
- data/inspec.gemspec +2 -2
- data/lib/bundles/inspec-artifact/cli.rb +3 -8
- data/lib/bundles/inspec-compliance/configuration.rb +5 -5
- data/lib/bundles/inspec-compliance/http.rb +2 -5
- data/lib/bundles/inspec-compliance/target.rb +6 -6
- data/lib/bundles/inspec-compliance/test/integration/default/cli.rb +1 -1
- data/lib/bundles/inspec-habitat/profile.rb +68 -74
- data/lib/bundles/inspec-supermarket/api.rb +7 -7
- data/lib/bundles/inspec-supermarket/cli.rb +1 -1
- data/lib/fetchers/git.rb +3 -8
- data/lib/fetchers/local.rb +7 -13
- data/lib/fetchers/url.rb +1 -1
- data/lib/inspec/backend.rb +0 -1
- data/lib/inspec/base_cli.rb +1 -1
- data/lib/inspec/cached_fetcher.rb +11 -12
- data/lib/inspec/cli.rb +0 -1
- data/lib/inspec/control_eval_context.rb +2 -2
- data/lib/inspec/dependencies/lockfile.rb +13 -15
- data/lib/inspec/dependencies/requirement.rb +1 -1
- data/lib/inspec/dependencies/resolver.rb +3 -5
- data/lib/inspec/dsl.rb +5 -5
- data/lib/inspec/dsl_shared.rb +1 -1
- data/lib/inspec/env_printer.rb +26 -26
- data/lib/inspec/metadata.rb +11 -10
- data/lib/inspec/objects/or_test.rb +4 -2
- data/lib/inspec/objects/test.rb +1 -1
- data/lib/inspec/profile.rb +2 -2
- data/lib/inspec/resource.rb +1 -3
- data/lib/inspec/rspec_json_formatter.rb +6 -8
- data/lib/inspec/shell.rb +51 -52
- data/lib/inspec/version.rb +1 -1
- data/lib/matchers/matchers.rb +1 -2
- data/lib/resources/audit_policy.rb +2 -2
- data/lib/resources/auditd.rb +6 -3
- data/lib/resources/dh_params.rb +1 -2
- data/lib/resources/docker.rb +2 -2
- data/lib/resources/docker_container.rb +4 -4
- data/lib/resources/elasticsearch.rb +2 -6
- data/lib/resources/etc_group.rb +2 -4
- data/lib/resources/groups.rb +14 -14
- data/lib/resources/host.rb +2 -3
- data/lib/resources/packages.rb +1 -1
- data/lib/resources/port.rb +1 -1
- data/lib/resources/postgres.rb +6 -6
- data/lib/resources/powershell.rb +1 -1
- data/lib/resources/service.rb +4 -5
- data/lib/resources/users.rb +58 -58
- data/lib/resources/vbscript.rb +10 -10
- data/lib/resources/virtualization.rb +3 -4
- data/lib/resources/x509_certificate.rb +1 -1
- data/lib/resources/yum.rb +1 -1
- data/lib/source_readers/inspec.rb +2 -1
- data/lib/utils/command_wrapper.rb +3 -8
- data/lib/utils/filter.rb +1 -1
- data/lib/utils/json_log.rb +2 -1
- data/lib/utils/latest_version.rb +5 -4
- data/lib/utils/object_traversal.rb +1 -1
- data/lib/utils/parser.rb +2 -2
- metadata +4 -4
data/lib/inspec/objects/test.rb
CHANGED
@@ -42,7 +42,7 @@ module Inspec
|
|
42
42
|
def describe_chain
|
43
43
|
return nil if @qualifier.empty?
|
44
44
|
|
45
|
-
resource =
|
45
|
+
resource = @qualifier.length > 1 ? @qualifier[0..-2] : [@qualifier[0]]
|
46
46
|
res = resource.map { |q| ruby_qualifier(q) }.join('.')
|
47
47
|
xres = nil
|
48
48
|
|
data/lib/inspec/profile.rb
CHANGED
@@ -106,7 +106,7 @@ module Inspec
|
|
106
106
|
# we share the backend between profiles.
|
107
107
|
#
|
108
108
|
# This will cause issues if a profile attempts to load a file via `inspec.profile.file`
|
109
|
-
train_options = options.
|
109
|
+
train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646
|
110
110
|
@backend = options[:backend].nil? ? Inspec::Backend.create(train_options) : options[:backend].dup
|
111
111
|
@runtime_profile = RuntimeProfile.new(self)
|
112
112
|
@backend.profile = @runtime_profile
|
@@ -421,7 +421,7 @@ module Inspec
|
|
421
421
|
[['inspec.yml', source_reader.metadata.content]] +
|
422
422
|
[['inspec.lock.deps', YAML.dump(deps)]]
|
423
423
|
|
424
|
-
files.
|
424
|
+
files.sort_by { |a| a[0] }
|
425
425
|
.map { |f| res << f[0] << "\0" << f[1] << "\0" }
|
426
426
|
|
427
427
|
res.digest.unpack('H*')[0]
|
data/lib/inspec/resource.rb
CHANGED
@@ -455,10 +455,10 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
455
455
|
end
|
456
456
|
|
457
457
|
def print_last_control_with_examples
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
458
|
+
return unless @last_control
|
459
|
+
|
460
|
+
print_control(@last_control)
|
461
|
+
@last_control.examples.each { |example| print_result(example) }
|
462
462
|
end
|
463
463
|
|
464
464
|
def last_control_is_anonymous?
|
@@ -634,8 +634,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
634
634
|
s = format('Profile Summary: %s, %s, %s',
|
635
635
|
format_with_color(success_color, success_str),
|
636
636
|
format_with_color(failed_color, failed_str),
|
637
|
-
format_with_color(skipped_color, skipped_str)
|
638
|
-
)
|
637
|
+
format_with_color(skipped_color, skipped_str))
|
639
638
|
output.puts(s) if summary['total'] > 0
|
640
639
|
end
|
641
640
|
|
@@ -651,8 +650,7 @@ class InspecRspecCli < InspecRspecJson # rubocop:disable Metrics/ClassLength
|
|
651
650
|
s = format('Test Summary: %s, %s, %s',
|
652
651
|
format_with_color(success_color, "#{summary['passed']} successful"),
|
653
652
|
format_with_color(failed_color, failed_str),
|
654
|
-
format_with_color(skipped_color, "#{summary['skipped']} skipped")
|
655
|
-
)
|
653
|
+
format_with_color(skipped_color, "#{summary['skipped']} skipped"))
|
656
654
|
|
657
655
|
output.puts(s)
|
658
656
|
end
|
data/lib/inspec/shell.rb
CHANGED
@@ -103,35 +103,34 @@ module Inspec
|
|
103
103
|
|
104
104
|
def print_target_info
|
105
105
|
ctx = @runner.backend
|
106
|
-
puts
|
107
|
-
You are currently running on:
|
106
|
+
puts <<~EOF
|
107
|
+
You are currently running on:
|
108
108
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
EOF
|
109
|
+
OS platform: #{mark ctx.os[:name] || 'unknown'}
|
110
|
+
OS family: #{mark ctx.os[:family] || 'unknown'}
|
111
|
+
OS release: #{mark ctx.os[:release] || 'unknown'}
|
112
|
+
EOF
|
113
113
|
end
|
114
114
|
|
115
115
|
def help(topic = nil)
|
116
116
|
if topic.nil?
|
117
117
|
|
118
|
-
puts
|
118
|
+
puts <<~EOF
|
119
|
+
Available commands:
|
119
120
|
|
120
|
-
|
121
|
+
`[resource]` - run resource on target machine
|
122
|
+
`help resources` - show all available resources that can be used as commands
|
123
|
+
`help [resource]` - information about a specific resource
|
124
|
+
`help matchers` - show information about common matchers
|
125
|
+
`exit` - exit the InSpec shell
|
121
126
|
|
122
|
-
|
123
|
-
`help resources` - show all available resources that can be used as commands
|
124
|
-
`help [resource]` - information about a specific resource
|
125
|
-
`help matchers` - show information about common matchers
|
126
|
-
`exit` - exit the InSpec shell
|
127
|
+
You can use resources in this environment to test the target machine. For example:
|
127
128
|
|
128
|
-
|
129
|
+
command('uname -a').stdout
|
130
|
+
file('/proc/cpuinfo').content => "value"
|
129
131
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
#{print_target_info}
|
134
|
-
EOF
|
132
|
+
#{print_target_info}
|
133
|
+
EOF
|
135
134
|
elsif topic == 'resources'
|
136
135
|
resources.sort.each do |resource|
|
137
136
|
puts " - #{resource}"
|
@@ -164,60 +163,60 @@ EOF
|
|
164
163
|
end
|
165
164
|
|
166
165
|
def print_matchers_help
|
167
|
-
puts
|
168
|
-
Matchers are used to compare resource values to expectations. While some
|
169
|
-
resources implement their own custom matchers, the following matchers are
|
170
|
-
common amongst all resources:
|
166
|
+
puts <<~EOL
|
167
|
+
Matchers are used to compare resource values to expectations. While some
|
168
|
+
resources implement their own custom matchers, the following matchers are
|
169
|
+
common amongst all resources:
|
171
170
|
|
172
|
-
#{mark 'be'}
|
171
|
+
#{mark 'be'}
|
173
172
|
|
174
|
-
The #{mark 'be'} matcher can be used to compare numeric values.
|
173
|
+
The #{mark 'be'} matcher can be used to compare numeric values.
|
175
174
|
|
176
|
-
|
175
|
+
its('size') { should be >= 10 }
|
177
176
|
|
178
|
-
#{mark 'cmp'}
|
177
|
+
#{mark 'cmp'}
|
179
178
|
|
180
|
-
The #{mark 'cmp'} matcher is like #{mark 'eq'} but less restrictive. It will try
|
181
|
-
to fit the resource value to the expectation.
|
179
|
+
The #{mark 'cmp'} matcher is like #{mark 'eq'} but less restrictive. It will try
|
180
|
+
to fit the resource value to the expectation.
|
182
181
|
|
183
|
-
"Protocol" likely returns a string, but cmp will ensure it's a number before
|
184
|
-
comparing:
|
182
|
+
"Protocol" likely returns a string, but cmp will ensure it's a number before
|
183
|
+
comparing:
|
185
184
|
|
186
|
-
|
187
|
-
|
185
|
+
its('Protocol') { should cmp 2 }
|
186
|
+
its('Protocol') { should cmp '2' }
|
188
187
|
|
189
|
-
"users" may return an array, but if it contains only one item, cmp will compare
|
190
|
-
it as a string or number as needed:
|
188
|
+
"users" may return an array, but if it contains only one item, cmp will compare
|
189
|
+
it as a string or number as needed:
|
191
190
|
|
192
|
-
|
191
|
+
its('users') { should cmp 'root' }
|
193
192
|
|
194
|
-
cmp is not case-sensitive:
|
193
|
+
cmp is not case-sensitive:
|
195
194
|
|
196
|
-
|
197
|
-
|
195
|
+
its('log_format') { should cmp 'raw' }
|
196
|
+
its('log_format') { should cmp 'RAW' }
|
198
197
|
|
199
|
-
#{mark 'eq'}
|
198
|
+
#{mark 'eq'}
|
200
199
|
|
201
|
-
The #{mark 'eq'} matcher tests for exact equality of two values. Value type
|
202
|
-
(string, number, etc.) is important and must be the same. For a less-restrictive
|
203
|
-
comparison matcher, use the #{mark 'cmp'} matcher.
|
200
|
+
The #{mark 'eq'} matcher tests for exact equality of two values. Value type
|
201
|
+
(string, number, etc.) is important and must be the same. For a less-restrictive
|
202
|
+
comparison matcher, use the #{mark 'cmp'} matcher.
|
204
203
|
|
205
|
-
|
204
|
+
its('RSAAuthentication') { should_not eq 'no' }
|
206
205
|
|
207
|
-
#{mark 'include'}
|
206
|
+
#{mark 'include'}
|
208
207
|
|
209
|
-
The #{mark 'include'} matcher tests to see if a value is included in a list.
|
208
|
+
The #{mark 'include'} matcher tests to see if a value is included in a list.
|
210
209
|
|
211
|
-
|
210
|
+
its('users') { should include 'my_user' }
|
212
211
|
|
213
|
-
#{mark 'match'}
|
212
|
+
#{mark 'match'}
|
214
213
|
|
215
|
-
The #{mark 'match'} matcher can be used to test a string for a match using a
|
216
|
-
regular expression.
|
214
|
+
The #{mark 'match'} matcher can be used to test a string for a match using a
|
215
|
+
regular expression.
|
217
216
|
|
218
|
-
|
217
|
+
its('content') { should_not match /^MyKey:\\s+some value/ }
|
219
218
|
|
220
|
-
For more examples, see: https://www.inspec.io/docs/reference/matchers/
|
219
|
+
For more examples, see: https://www.inspec.io/docs/reference/matchers/
|
221
220
|
|
222
221
|
EOL
|
223
222
|
end
|
data/lib/inspec/version.rb
CHANGED
data/lib/matchers/matchers.rb
CHANGED
@@ -266,7 +266,7 @@ end
|
|
266
266
|
# - compare strings case-insensitive
|
267
267
|
# - you expect a number (strings will be converted if possible)
|
268
268
|
#
|
269
|
-
RSpec::Matchers.define :cmp do |first_expected|
|
269
|
+
RSpec::Matchers.define :cmp do |first_expected| # rubocop:disable Metrics/BlockLength
|
270
270
|
|
271
271
|
def integer?(value)
|
272
272
|
!(value =~ /\A0+\Z|\A[1-9]\d*\Z/).nil?
|
@@ -324,7 +324,6 @@ RSpec::Matchers.define :cmp do |first_expected|
|
|
324
324
|
|
325
325
|
# fallback to simple operation
|
326
326
|
actual.method(op).call(expected)
|
327
|
-
|
328
327
|
rescue NameError => _
|
329
328
|
false
|
330
329
|
rescue ArgumentError
|
data/lib/resources/auditd.rb
CHANGED
@@ -101,7 +101,8 @@ module Inspec::Resources
|
|
101
101
|
'permissions' => perms,
|
102
102
|
'key' => key,
|
103
103
|
'fields_nokey' => fields_nokey,
|
104
|
-
},
|
104
|
+
},
|
105
|
+
)
|
105
106
|
end
|
106
107
|
|
107
108
|
def syscall_rules_for(line)
|
@@ -128,7 +129,8 @@ module Inspec::Resources
|
|
128
129
|
'permissions' => perms,
|
129
130
|
'exit' => exit_field,
|
130
131
|
'fields_nokey' => fields_nokey,
|
131
|
-
},
|
132
|
+
},
|
133
|
+
)
|
132
134
|
end
|
133
135
|
end
|
134
136
|
|
@@ -142,7 +144,8 @@ module Inspec::Resources
|
|
142
144
|
'file' => file,
|
143
145
|
'key' => key,
|
144
146
|
'permissions' => perms,
|
145
|
-
},
|
147
|
+
},
|
148
|
+
)
|
146
149
|
end
|
147
150
|
|
148
151
|
def to_s
|
data/lib/resources/dh_params.rb
CHANGED
@@ -26,8 +26,7 @@ class DhParams < Inspec.resource(1)
|
|
26
26
|
def initialize(filename)
|
27
27
|
@dh_params_path = filename
|
28
28
|
file = inspec.file(@dh_params_path)
|
29
|
-
return skip_resource
|
30
|
-
"#{@dh_params_path}" unless file.exist?
|
29
|
+
return skip_resource "Unable to find DH parameters file #{@dh_params_path}" unless file.exist?
|
31
30
|
|
32
31
|
begin
|
33
32
|
@dh_params = OpenSSL::PKey::DH.new file.content
|
data/lib/resources/docker.rb
CHANGED
@@ -146,7 +146,7 @@ module Inspec::Resources
|
|
146
146
|
# @see https://github.com/moby/moby/issues/20625, works for docker 1.13+
|
147
147
|
# raw_containers = inspec.command('docker ps -a --no-trunc --format \'{{ json . }}\'').stdout
|
148
148
|
# therefore we stick with older approach
|
149
|
-
labels = %w{Command CreatedAt ID Image Labels
|
149
|
+
labels = %w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status}
|
150
150
|
|
151
151
|
# Networks LocalVolumes work with 1.13+ only
|
152
152
|
if !version.empty? && Gem::Version.new(version['Client']['Version']) >= Gem::Version.new('1.13')
|
@@ -183,7 +183,7 @@ module Inspec::Resources
|
|
183
183
|
end
|
184
184
|
|
185
185
|
def ensure_container_keys(entry)
|
186
|
-
%w{Command CreatedAt ID Image Labels
|
186
|
+
%w{Command CreatedAt ID Image Labels Mounts Names Ports RunningFor Size Status Networks LocalVolumes}.each { |key|
|
187
187
|
entry[key.downcase] = nil if !entry.key?(key.downcase)
|
188
188
|
}
|
189
189
|
entry
|
@@ -63,10 +63,10 @@ module Inspec::Resources
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def command
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
return unless container_info.entries.length == 1
|
67
|
+
|
68
|
+
cmd = container_info.commands[0]
|
69
|
+
cmd.slice(1, cmd.length - 2)
|
70
70
|
end
|
71
71
|
|
72
72
|
def image
|
@@ -154,9 +154,7 @@ module Inspec::Resources
|
|
154
154
|
raise 'Connection refused - peer certificate issuer is not recognized'
|
155
155
|
end
|
156
156
|
|
157
|
-
|
158
|
-
raise "Error fetching Elastcsearch data from curl #{url}: #{cmd.stderr}"
|
159
|
-
end
|
157
|
+
raise "Error fetching Elastcsearch data from curl #{url}: #{cmd.stderr}" unless cmd.exit_status.zero?
|
160
158
|
end
|
161
159
|
|
162
160
|
def verify_json_payload!(content)
|
@@ -164,9 +162,7 @@ module Inspec::Resources
|
|
164
162
|
raise "#{content['error']['type']}: #{content['error']['reason']}"
|
165
163
|
end
|
166
164
|
|
167
|
-
if content['_nodes']['successful'].zero?
|
168
|
-
raise 'No successful nodes available in cluster'
|
169
|
-
end
|
165
|
+
raise 'No successful nodes available in cluster' if content['_nodes']['successful'].zero?
|
170
166
|
end
|
171
167
|
end
|
172
168
|
end
|
data/lib/resources/etc_group.rb
CHANGED
@@ -49,13 +49,11 @@ module Inspec::Resources
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def groups(filter = nil)
|
52
|
-
|
53
|
-
entries.map { |x| x['name'] } if !entries.nil?
|
52
|
+
(filter || @entries)&.map { |x| x['name'] }
|
54
53
|
end
|
55
54
|
|
56
55
|
def gids(filter = nil)
|
57
|
-
|
58
|
-
entries.map { |x| x['gid'] } if !entries.nil?
|
56
|
+
(filter || @entries)&.map { |x| x['gid'] }
|
59
57
|
end
|
60
58
|
|
61
59
|
def users(filter = nil)
|
data/lib/resources/groups.rb
CHANGED
@@ -157,20 +157,20 @@ module Inspec::Resources
|
|
157
157
|
class WindowsGroup < GroupInfo
|
158
158
|
# returns all local groups
|
159
159
|
def groups
|
160
|
-
script =
|
161
|
-
Function ConvertTo-SID { Param([byte[]]$BinarySID)
|
162
|
-
|
163
|
-
}
|
164
|
-
|
165
|
-
$Computername = $Env:Computername
|
166
|
-
$adsi = [ADSI]"WinNT://$Computername"
|
167
|
-
$groups = $adsi.Children | where {$_.SchemaClassName -eq 'group'} | ForEach {
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
}
|
173
|
-
$groups | ConvertTo-Json -Depth 3
|
160
|
+
script = <<~EOH
|
161
|
+
Function ConvertTo-SID { Param([byte[]]$BinarySID)
|
162
|
+
(New-Object System.Security.Principal.SecurityIdentifier($BinarySID,0)).Value
|
163
|
+
}
|
164
|
+
|
165
|
+
$Computername = $Env:Computername
|
166
|
+
$adsi = [ADSI]"WinNT://$Computername"
|
167
|
+
$groups = $adsi.Children | where {$_.SchemaClassName -eq 'group'} | ForEach {
|
168
|
+
$name = $_.Name[0]
|
169
|
+
$sid = ConvertTo-SID -BinarySID $_.ObjectSID[0]
|
170
|
+
$group =[ADSI]$_.Path
|
171
|
+
new-object psobject -property @{name = $group.Name[0]; gid = $sid; domain=$Computername}
|
172
|
+
}
|
173
|
+
$groups | ConvertTo-Json -Depth 3
|
174
174
|
EOH
|
175
175
|
cmd = inspec.powershell(script)
|
176
176
|
# cannot rely on exit code for now, successful command returns exit code 1
|
data/lib/resources/host.rb
CHANGED
@@ -70,9 +70,8 @@ module Inspec::Resources
|
|
70
70
|
end
|
71
71
|
|
72
72
|
missing_requirements = @host_provider.missing_requirements(protocol)
|
73
|
-
|
74
|
-
|
75
|
-
end
|
73
|
+
return skip_resource 'The following requirements are not met for this resource: ' \
|
74
|
+
"#{missing_requirements.join(', ')}" unless missing_requirements.empty?
|
76
75
|
end
|
77
76
|
|
78
77
|
def proto
|
data/lib/resources/packages.rb
CHANGED
@@ -97,7 +97,7 @@ module Inspec::Resources
|
|
97
97
|
class Rpms < PkgsManagement
|
98
98
|
def build_package_list
|
99
99
|
# use two spaces as delimiter in case any of the fields has a space in it
|
100
|
-
command = "rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\\n'"
|
100
|
+
command = "rpm -qa --queryformat '%{NAME} %{VERSION}-%{RELEASE}\\n'" # rubocop:disable Style/FormatStringToken
|
101
101
|
cmd = inspec.command(command)
|
102
102
|
all = cmd.stdout.split("\n")
|
103
103
|
return [] if all.nil?
|
data/lib/resources/port.rb
CHANGED
@@ -242,7 +242,7 @@ module Inspec::Resources
|
|
242
242
|
procs[proc_id] = [] unless procs.key?(proc_id)
|
243
243
|
|
244
244
|
# change address '*' to zero
|
245
|
-
host =
|
245
|
+
host = port_id =~ /^ipv6:/ ? '[::]' : '0.0.0.0' if host == '*'
|
246
246
|
# entrust URI to scrub the host and port
|
247
247
|
begin
|
248
248
|
uri = URI("addr://#{host}:#{port}")
|