inspec 0.9.2 → 0.9.3
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 +272 -49
- data/Gemfile +1 -0
- data/README.md +9 -0
- data/Rakefile +12 -0
- data/docs/resources.rst +56 -56
- data/examples/test-kitchen/test/integration/default/web_spec.rb +1 -1
- data/inspec.gemspec +8 -3
- data/lib/inspec/profile_context.rb +2 -1
- data/lib/inspec/runner.rb +8 -0
- data/lib/inspec/shell.rb +2 -2
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/command.rb +2 -0
- data/lib/resources/os_env.rb +38 -6
- data/lib/resources/port.rb +16 -4
- data/lib/resources/registry_key.rb +113 -17
- data/lib/resources/script.rb +1 -3
- data/lib/resources/security_policy.rb +1 -1
- data/lib/resources/service.rb +21 -15
- data/lib/utils/simpleconfig.rb +1 -1
- data/test/helper.rb +4 -2
- data/test/integration/cookbooks/os_prepare/recipes/default.rb +2 -0
- data/test/integration/cookbooks/os_prepare/recipes/file.rb +16 -12
- data/test/integration/cookbooks/os_prepare/recipes/registry_key.rb +69 -0
- data/test/integration/cookbooks/os_prepare/recipes/service.rb +12 -0
- data/test/integration/{default → test/integration/default}/_debug_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/apt_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/file_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/group_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/kernel_module_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/kernel_parameter_spec.rb +0 -0
- data/test/integration/{default → test/integration/default}/package_spec.rb +0 -0
- data/test/integration/test/integration/default/port_spec.rb +9 -0
- data/test/integration/test/integration/default/registry_key_spec.rb +53 -0
- data/test/integration/{default → test/integration/default}/service_spec.rb +14 -1
- data/test/integration/test/integration/default/user_spec.rb +62 -0
- data/test/unit/mock/cmd/$env-PATH +1 -0
- data/test/unit/mock/cmd/env +1 -0
- data/test/unit/mock/cmd/reg_schedule +6 -1
- data/test/unit/profile_context_test.rb +14 -3
- data/test/unit/resources/os_env_test.rb +6 -1
- data/test/unit/resources/registry_key_test.rb +2 -3
- metadata +32 -22
- data/test/integration/default/user_spec.rb +0 -44
- data/test/unit/mock/cmd/PATH +0 -1
data/inspec.gemspec
CHANGED
@@ -13,9 +13,14 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = 'https://github.com/chef/inspec'
|
14
14
|
spec.license = 'Apache 2.0'
|
15
15
|
|
16
|
-
spec.files = %w
|
17
|
-
|
18
|
-
|
16
|
+
spec.files = %w{
|
17
|
+
README.md Rakefile MAINTAINERS.toml MAINTAINERS.md LICENSE inspec.gemspec
|
18
|
+
Gemfile CHANGELOG.md .rubocop.yml
|
19
|
+
} + Dir.glob(
|
20
|
+
'{bin,docs,examples,lib,tasks,test}/**/*', File::FNM_DOTMATCH
|
21
|
+
).reject { |f| File.directory?(f) }
|
22
|
+
|
23
|
+
spec.executables = %w{ inspec }
|
19
24
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
25
|
spec.require_paths = ['lib']
|
21
26
|
|
@@ -5,6 +5,7 @@
|
|
5
5
|
require 'inspec/rule'
|
6
6
|
require 'inspec/dsl'
|
7
7
|
require 'rspec/core/dsl'
|
8
|
+
require 'securerandom'
|
8
9
|
|
9
10
|
module Inspec
|
10
11
|
class ProfileContext # rubocop:disable Metrics/ClassLength
|
@@ -110,7 +111,7 @@ module Inspec
|
|
110
111
|
define_method :describe do |*args, &block|
|
111
112
|
path = block.source_location[0]
|
112
113
|
line = block.source_location[1]
|
113
|
-
id = "#{File.basename(path)}:#{line}"
|
114
|
+
id = "#{File.basename(path)}:#{line} #{SecureRandom.hex}"
|
114
115
|
rule = rule_class.new(id, {}) do
|
115
116
|
describe(*args, &block)
|
116
117
|
end
|
data/lib/inspec/runner.rb
CHANGED
@@ -52,6 +52,14 @@ module Inspec
|
|
52
52
|
tests = items.find_all { |i| i[:type] == :test }
|
53
53
|
libs = items.find_all { |i| i[:type] == :library }
|
54
54
|
|
55
|
+
# Ensure each test directory exists on the $LOAD_PATH. This
|
56
|
+
# will ensure traditional RSpec-isms like `require 'spec_helper'`
|
57
|
+
# continue to work.
|
58
|
+
tests.flatten.each do |test|
|
59
|
+
test_directory = File.dirname(test[:ref])
|
60
|
+
$LOAD_PATH.unshift test_directory unless $LOAD_PATH.include?(test_directory)
|
61
|
+
end
|
62
|
+
|
55
63
|
# add all tests (raw) to the runtime
|
56
64
|
tests.flatten.each do |test|
|
57
65
|
add_content(test, libs)
|
data/lib/inspec/shell.rb
CHANGED
@@ -13,8 +13,8 @@ module Inspec
|
|
13
13
|
|
14
14
|
def start
|
15
15
|
# store context to run commands in this context
|
16
|
-
|
17
|
-
|
16
|
+
c = { content: 'binding.pry', ref: __FILE__, line: __LINE__ }
|
17
|
+
@runner.add_content(c, [])
|
18
18
|
@runner.run
|
19
19
|
end
|
20
20
|
|
data/lib/inspec/version.rb
CHANGED
data/lib/resources/command.rb
CHANGED
data/lib/resources/os_env.rb
CHANGED
@@ -11,24 +11,56 @@
|
|
11
11
|
# its(:split) { should_not include('.') }
|
12
12
|
# end
|
13
13
|
|
14
|
+
require 'utils/simpleconfig'
|
15
|
+
|
14
16
|
class OsEnv < Inspec.resource(1)
|
15
17
|
name 'os_env'
|
16
18
|
|
17
19
|
attr_reader :content
|
18
|
-
def initialize(env)
|
20
|
+
def initialize(env = nil)
|
19
21
|
@osenv = env
|
20
|
-
|
21
|
-
@content =
|
22
|
-
@content = nil if cmd.exit_status != 0
|
22
|
+
@content = nil
|
23
|
+
@content = value_for(env) unless env.nil?
|
23
24
|
end
|
24
25
|
|
25
26
|
def split
|
27
|
+
# we can't take advantage of `File::PATH_SEPARATOR` as code is
|
28
|
+
# evaluated on the host machine
|
29
|
+
path_separator = inspec.os.windows? ? ';' : ':'
|
26
30
|
# -1 is required to catch cases like dir1::dir2:
|
27
31
|
# where we have a trailing :
|
28
|
-
@content.nil? ? [] : @content.split(
|
32
|
+
@content.nil? ? [] : @content.split(path_separator, -1)
|
29
33
|
end
|
30
34
|
|
31
35
|
def to_s
|
32
|
-
|
36
|
+
if @osenv.nil?
|
37
|
+
'Environment variables'
|
38
|
+
else
|
39
|
+
"Environment variable #{@osenv}"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def value_for(env)
|
46
|
+
command = if inspec.os.windows?
|
47
|
+
"$Env:#{env}"
|
48
|
+
else
|
49
|
+
'env'
|
50
|
+
end
|
51
|
+
|
52
|
+
out = inspec.command(command)
|
53
|
+
|
54
|
+
unless out.exit_status == 0
|
55
|
+
skip_resource "Can't read environment variables on #{os[:family]}. "\
|
56
|
+
"Tried `#{command}` which returned #{out.exit_status}"
|
57
|
+
end
|
58
|
+
|
59
|
+
if inspec.os.windows?
|
60
|
+
out.stdout.strip
|
61
|
+
else
|
62
|
+
params = SimpleConfig.new(out.stdout).params
|
63
|
+
params[env]
|
64
|
+
end
|
33
65
|
end
|
34
66
|
end
|
data/lib/resources/port.rb
CHANGED
@@ -24,7 +24,7 @@ class Port < Inspec.resource(1)
|
|
24
24
|
@cache = nil
|
25
25
|
|
26
26
|
case inspec.os[:family]
|
27
|
-
when 'ubuntu', 'debian', 'redhat', 'fedora', 'arch'
|
27
|
+
when 'ubuntu', 'debian', 'redhat', 'fedora', 'centos', 'arch'
|
28
28
|
@port_manager = LinuxPorts.new(inspec)
|
29
29
|
when 'darwin'
|
30
30
|
@port_manager = DarwinPorts.new(inspec)
|
@@ -179,7 +179,7 @@ class LinuxPorts < PortsInfo
|
|
179
179
|
def parse_net_address(net_addr, protocol)
|
180
180
|
if protocol.eql?('tcp6') || protocol.eql?('udp6')
|
181
181
|
# prep for URI parsing, parse ip6 port
|
182
|
-
ip6 = /^(\S
|
182
|
+
ip6 = /^(\S+):(\d+)$/.match(net_addr)
|
183
183
|
ip6addr = ip6[1]
|
184
184
|
ip6addr = '::' if /^:::$/.match(ip6addr)
|
185
185
|
# build uri
|
@@ -193,16 +193,25 @@ class LinuxPorts < PortsInfo
|
|
193
193
|
port = ip_addr.port
|
194
194
|
end
|
195
195
|
[host, port]
|
196
|
+
rescue URI::InvalidURIError => e
|
197
|
+
warn "Could not parse #{net_addr}, #{e}"
|
198
|
+
nil
|
196
199
|
end
|
197
200
|
|
198
201
|
def parse_netstat_line(line)
|
199
202
|
# parse each line
|
200
203
|
# 1 - Proto, 2 - Recv-Q, 3 - Send-Q, 4 - Local Address, 5 - Foreign Address, 6 - State, 7 - Inode, 8 - PID/Program name
|
201
|
-
parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)
|
202
|
-
|
204
|
+
parsed = /^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/.match(line)
|
205
|
+
|
206
|
+
return {} if parsed.nil? || line.match(/^proto/i)
|
203
207
|
|
204
208
|
# parse ip4 and ip6 addresses
|
205
209
|
protocol = parsed[1].downcase
|
210
|
+
|
211
|
+
# detect protocol if not provided
|
212
|
+
protocol += '6' if parsed[4].count(':') > 1 && %w{tcp udp}.include?(protocol)
|
213
|
+
|
214
|
+
# extract host and port information
|
206
215
|
host, port = parse_net_address(parsed[4], protocol)
|
207
216
|
|
208
217
|
# extract PID
|
@@ -261,6 +270,9 @@ class FreeBsdPorts < PortsInfo
|
|
261
270
|
port = ip_addr.port
|
262
271
|
end
|
263
272
|
[host, port]
|
273
|
+
rescue URI::InvalidURIError => e
|
274
|
+
warn "Could not parse #{net_addr}, #{e}"
|
275
|
+
nil
|
264
276
|
end
|
265
277
|
|
266
278
|
def parse_sockstat_line(line)
|
@@ -22,33 +22,129 @@ class RegistryKey < Inspec.resource(1)
|
|
22
22
|
@reg_key = reg_key
|
23
23
|
end
|
24
24
|
|
25
|
-
def
|
26
|
-
|
27
|
-
command_result ||= inspec.command(cmd)
|
28
|
-
val = { exit_code: command_result.exit_status.to_i, data: command_result.stdout }
|
29
|
-
val
|
25
|
+
def exists?
|
26
|
+
!registry_value(@reg_key).nil?
|
30
27
|
end
|
31
28
|
|
32
|
-
def
|
33
|
-
val =
|
34
|
-
val
|
35
|
-
|
29
|
+
def has_value?(value)
|
30
|
+
val = registry_value(@reg_key)
|
31
|
+
!val.nil? && val['(default)'.to_s]['value'] == value ? true : false
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_property?(property_name, property_type = nil)
|
35
|
+
val = registry_value(@reg_key)
|
36
|
+
!val.nil? && !val[property_name.to_s].nil? && (property_type.nil? || val[property_name.to_s]['type'] == map2type(property_type)) ? true : false
|
37
|
+
end
|
38
|
+
|
39
|
+
# deactivate rubocop, because we need to stay compatible with Serverspe
|
40
|
+
# rubocop:disable Style/OptionalArguments
|
41
|
+
def has_property_value?(property_name, property_type = nil, value)
|
42
|
+
# rubocop:enable Style/OptionalArguments
|
43
|
+
val = registry_value(@reg_key)
|
44
|
+
|
45
|
+
# convert value to binary if required
|
46
|
+
value = value.bytes if !property_type.nil? && map2type(property_type) == 3 && !value.is_a?(Array)
|
47
|
+
|
48
|
+
!val.nil? && val[property_name.to_s]['value'] == value && (property_type.nil? || val[property_name.to_s]['type'] == map2type(property_type)) ? true : false
|
36
49
|
end
|
37
50
|
|
38
51
|
# returns nil, if not existant or value
|
39
52
|
def method_missing(meth)
|
40
53
|
# get data
|
41
|
-
val = registry_value(@reg_key
|
42
|
-
|
43
|
-
|
44
|
-
if (val[:exit_code] == 0)
|
45
|
-
return convert_value(val[:data])
|
46
|
-
else
|
47
|
-
return nil
|
48
|
-
end
|
54
|
+
val = registry_value(@reg_key)
|
55
|
+
return nil if val.nil?
|
56
|
+
val[meth.to_s]['value']
|
49
57
|
end
|
50
58
|
|
51
59
|
def to_s
|
52
60
|
"Registry Key #{@name}"
|
53
61
|
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def registry_value(path)
|
66
|
+
return @registy_cache if defined?(@registy_cache)
|
67
|
+
|
68
|
+
# load registry key and all properties
|
69
|
+
script = <<-EOH
|
70
|
+
$reg = Get-Item 'Registry::#{path}'
|
71
|
+
$object = New-Object -Type PSObject
|
72
|
+
$reg.Property | ForEach-Object {
|
73
|
+
$key = $_
|
74
|
+
if ("(default)".Equals($key)) { $key = '' }
|
75
|
+
$value = New-Object psobject -Property @{
|
76
|
+
"value" = $reg.GetValue($key);
|
77
|
+
"type" = $reg.GetValueKind($key);
|
78
|
+
}
|
79
|
+
$object | Add-Member –MemberType NoteProperty –Name $_ –Value $value
|
80
|
+
}
|
81
|
+
$object | ConvertTo-Json
|
82
|
+
EOH
|
83
|
+
|
84
|
+
cmd = inspec.script(script)
|
85
|
+
|
86
|
+
# cannot rely on exit code for now, successful command returns exit code 1
|
87
|
+
# return nil if cmd.exit_status != 0, try to parse json
|
88
|
+
begin
|
89
|
+
@registy_cache = JSON.parse(cmd.stdout)
|
90
|
+
rescue JSON::ParserError => _e
|
91
|
+
@registy_cache = nil
|
92
|
+
end
|
93
|
+
|
94
|
+
@registy_cache
|
95
|
+
end
|
96
|
+
|
97
|
+
# Registry key value types
|
98
|
+
# @see https://msdn.microsoft.com/en-us/library/windows/desktop/ms724884(v=vs.85).aspx
|
99
|
+
# REG_NONE 0
|
100
|
+
# REG_SZ 1
|
101
|
+
# REG_EXPAND_SZ 2
|
102
|
+
# REG_BINARY 3
|
103
|
+
# REG_DWORD 4
|
104
|
+
# REG_DWORD_LITTLE_ENDIAN 4
|
105
|
+
# REG_DWORD_BIG_ENDIAN 5
|
106
|
+
# REG_LINK 6
|
107
|
+
# REG_MULTI_SZ 7
|
108
|
+
# REG_RESOURCE_LIST 8
|
109
|
+
# REG_FULL_RESOURCE_DESCRIPTOR 9
|
110
|
+
# REG_RESOURCE_REQUIREMENTS_LIST 10
|
111
|
+
# REG_QWORD 11
|
112
|
+
# REG_QWORD_LITTLE_ENDIAN 11
|
113
|
+
def map2type(symbol)
|
114
|
+
options = {}
|
115
|
+
|
116
|
+
# chef symbols, we prefer those
|
117
|
+
options[:binary] = 3
|
118
|
+
options[:string] = 1
|
119
|
+
options[:multi_string] = 7
|
120
|
+
options[:expand_string] = 2
|
121
|
+
options[:dword] = 4
|
122
|
+
options[:dword_big_endian] = 5
|
123
|
+
options[:qword] = 11
|
124
|
+
|
125
|
+
# serverspec symbols
|
126
|
+
options[:type_string] = 1
|
127
|
+
options[:type_binary] = 3
|
128
|
+
options[:type_dword] = 4
|
129
|
+
options[:type_qword] = 11
|
130
|
+
options[:type_multistring] = 7
|
131
|
+
options[:type_expandstring] = 2
|
132
|
+
|
133
|
+
options[symbol]
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# for compatability with serverspec
|
138
|
+
# this is deprecated syntax and will be removed in future versions
|
139
|
+
class WindowsRegistryKey < RegistryKey
|
140
|
+
name 'windows_registry_key'
|
141
|
+
|
142
|
+
def initialize(name)
|
143
|
+
deprecated
|
144
|
+
super(name)
|
145
|
+
end
|
146
|
+
|
147
|
+
def deprecated
|
148
|
+
warn '[DEPRECATION] `yumrepo(reponame)` is deprecated. Please use `yum.repo(reponame)` instead.'
|
149
|
+
end
|
54
150
|
end
|
data/lib/resources/script.rb
CHANGED
@@ -6,7 +6,6 @@
|
|
6
6
|
|
7
7
|
class Script < Cmd
|
8
8
|
name 'script'
|
9
|
-
attr_accessor :command
|
10
9
|
|
11
10
|
def initialize(script)
|
12
11
|
case inspec.os[:family]
|
@@ -19,8 +18,7 @@ class Script < Cmd
|
|
19
18
|
else
|
20
19
|
return skip_resource 'The `script` resource is not supported on your OS yet.'
|
21
20
|
end
|
22
|
-
|
23
|
-
@command = cmd
|
21
|
+
super(cmd)
|
24
22
|
end
|
25
23
|
|
26
24
|
# we cannot determine if a command exists, because that does not work for scripts
|
data/lib/resources/service.rb
CHANGED
@@ -149,39 +149,45 @@ end
|
|
149
149
|
class Upstart < ServiceManager
|
150
150
|
def info(service_name)
|
151
151
|
# get the status of upstart service
|
152
|
-
|
153
|
-
|
152
|
+
status = inspec.command("initctl status #{service_name}")
|
153
|
+
|
154
|
+
# fallback for systemv services, those are not handled via `initctl`
|
155
|
+
return SysV.new(inspec).info(service_name) if status.exit_status.to_i != 0
|
154
156
|
|
155
157
|
# @see: http://upstart.ubuntu.com/cookbook/#job-states
|
156
158
|
# grep for running to indicate the service is there
|
157
|
-
|
158
|
-
|
159
|
+
running = !status.stdout[/running/].nil?
|
160
|
+
|
161
|
+
{
|
162
|
+
name: service_name,
|
163
|
+
description: nil,
|
164
|
+
installed: true,
|
165
|
+
running: running,
|
166
|
+
enabled: info_enabled(status, service_name),
|
167
|
+
type: 'upstart',
|
168
|
+
}
|
169
|
+
end
|
159
170
|
|
171
|
+
private
|
172
|
+
|
173
|
+
def info_enabled(status, service_name)
|
160
174
|
# check if a service is enabled
|
161
175
|
# http://upstart.ubuntu.com/cookbook/#determine-if-a-job-is-disabled
|
162
176
|
# $ initctl show-config $job | grep -q "^ start on" && echo enabled || echo disabled
|
163
177
|
# Ubuntu 10.04 show-config is not supported
|
164
178
|
# @see http://manpages.ubuntu.com/manpages/maverick/man8/initctl.8.html
|
165
179
|
config = inspec.command("initctl show-config #{service_name}")
|
166
|
-
|
167
|
-
!match_enabled.nil? ? (enabled = true) : (enabled = false)
|
180
|
+
enabled = !config.stdout[/^\s*start on/].nil?
|
168
181
|
|
169
182
|
# implement fallback for Ubuntu 10.04
|
170
183
|
if inspec.os[:family] == 'ubuntu' &&
|
171
184
|
inspec.os[:release].to_f >= 10.04 &&
|
172
185
|
inspec.os[:release].to_f < 12.04 &&
|
173
|
-
|
186
|
+
status.exit_status == 0
|
174
187
|
enabled = true
|
175
188
|
end
|
176
189
|
|
177
|
-
|
178
|
-
name: service_name,
|
179
|
-
description: nil,
|
180
|
-
installed: true,
|
181
|
-
running: running,
|
182
|
-
enabled: enabled,
|
183
|
-
type: 'upstart',
|
184
|
-
}
|
190
|
+
enabled
|
185
191
|
end
|
186
192
|
end
|
187
193
|
|