inspec 0.33.2 → 0.34.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 +36 -2
- data/README.md +12 -13
- data/examples/meta-profile/README.md +11 -0
- data/examples/meta-profile/controls/example.rb +8 -0
- data/examples/meta-profile/inspec.yml +19 -0
- data/lib/bundles/inspec-compliance/target.rb +25 -8
- data/lib/bundles/inspec-supermarket/api.rb +12 -16
- data/lib/bundles/inspec-supermarket/target.rb +11 -6
- data/lib/fetchers/git.rb +162 -0
- data/lib/fetchers/local.rb +33 -16
- data/lib/fetchers/mock.rb +8 -4
- data/lib/fetchers/url.rb +56 -42
- data/lib/inspec/dependencies/lockfile.rb +28 -2
- data/lib/inspec/dependencies/requirement.rb +20 -51
- data/lib/inspec/dependencies/resolver.rb +30 -13
- data/lib/inspec/dependencies/vendor_index.rb +9 -42
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/fetcher.rb +1 -2
- data/lib/inspec/file_provider.rb +219 -0
- data/lib/inspec/plugins/fetcher.rb +59 -80
- data/lib/inspec/profile.rb +31 -18
- data/lib/inspec/resource.rb +2 -1
- data/lib/inspec/source_reader.rb +0 -3
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/security_policy.rb +67 -38
- data/lib/resources/sys_info.rb +26 -0
- data/lib/resources/{user.rb → users.rb} +237 -76
- metadata +9 -5
- data/lib/fetchers/tar.rb +0 -53
- data/lib/fetchers/zip.rb +0 -49
@@ -0,0 +1,219 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems/package'
|
3
|
+
require 'zlib'
|
4
|
+
require 'zip'
|
5
|
+
|
6
|
+
module Inspec
|
7
|
+
class FileProvider
|
8
|
+
def self.for_path(path)
|
9
|
+
if path.is_a?(Hash)
|
10
|
+
MockProvider.new(path)
|
11
|
+
elsif File.directory?(path)
|
12
|
+
DirProvider.new(path)
|
13
|
+
elsif File.exist?(path) && path.end_with?('.tar.gz', 'tgz')
|
14
|
+
TarProvider.new(path)
|
15
|
+
elsif File.exist?(path) && path.end_with?('.zip')
|
16
|
+
ZipProvider.new(path)
|
17
|
+
elsif File.exist?(path)
|
18
|
+
DirProvider.new(path)
|
19
|
+
else
|
20
|
+
fail "No file provider for the provided path: #{path}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def read(_file)
|
28
|
+
fail "#{self} does not implement `read(...)`. This is required."
|
29
|
+
end
|
30
|
+
|
31
|
+
def files
|
32
|
+
fail "Fetcher #{self} does not implement `files()`. This is required."
|
33
|
+
end
|
34
|
+
|
35
|
+
def relative_provider
|
36
|
+
RelativeFileProvider.new(self)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class MockProvider < FileProvider
|
41
|
+
attr_reader :files
|
42
|
+
def initialize(path)
|
43
|
+
@data = path[:mock]
|
44
|
+
@files = @data.keys
|
45
|
+
end
|
46
|
+
|
47
|
+
def read(file)
|
48
|
+
@data[file]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class DirProvider < FileProvider
|
53
|
+
attr_reader :files
|
54
|
+
def initialize(path)
|
55
|
+
@files = if File.file?(path)
|
56
|
+
[path]
|
57
|
+
else
|
58
|
+
Dir[File.join(path, '**', '*')]
|
59
|
+
end
|
60
|
+
@path = path
|
61
|
+
end
|
62
|
+
|
63
|
+
def read(file)
|
64
|
+
return nil unless files.include?(file)
|
65
|
+
return nil unless File.file?(file)
|
66
|
+
File.read(file)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class ZipProvider < FileProvider
|
71
|
+
attr_reader :files
|
72
|
+
|
73
|
+
def initialize(path)
|
74
|
+
@path = path
|
75
|
+
@contents = {}
|
76
|
+
@files = []
|
77
|
+
::Zip::InputStream.open(@path) do |io|
|
78
|
+
while (entry = io.get_next_entry)
|
79
|
+
@files.push(entry.name.sub(%r{/+$}, ''))
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def read(file)
|
85
|
+
@contents[file] ||= read_from_zip(file)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def read_from_zip(file)
|
91
|
+
return nil unless @files.include?(file)
|
92
|
+
res = nil
|
93
|
+
::Zip::InputStream.open(@path) do |io|
|
94
|
+
while (entry = io.get_next_entry)
|
95
|
+
next unless file == entry.name
|
96
|
+
res = io.read
|
97
|
+
break
|
98
|
+
end
|
99
|
+
end
|
100
|
+
res
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class TarProvider < FileProvider
|
105
|
+
attr_reader :files
|
106
|
+
|
107
|
+
def initialize(path)
|
108
|
+
@path = path
|
109
|
+
@contents = {}
|
110
|
+
@files = []
|
111
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(@path)) do |tar|
|
112
|
+
@files = tar.map(&:full_name)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def read(file)
|
117
|
+
@contents[file] ||= read_from_tar(file)
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def read_from_tar(file)
|
123
|
+
return nil unless @files.include?(file)
|
124
|
+
res = nil
|
125
|
+
# NB `TarReader` includes `Enumerable` beginning with Ruby 2.x
|
126
|
+
Gem::Package::TarReader.new(Zlib::GzipReader.open(@path)) do |tar|
|
127
|
+
tar.each do |entry|
|
128
|
+
next unless entry.file? && file == entry.full_name
|
129
|
+
res = entry.read
|
130
|
+
break
|
131
|
+
end
|
132
|
+
end
|
133
|
+
res
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
class RelativeFileProvider
|
138
|
+
BLACKLIST_FILES = [
|
139
|
+
'/pax_global_header',
|
140
|
+
'pax_global_header',
|
141
|
+
].freeze
|
142
|
+
|
143
|
+
attr_reader :files
|
144
|
+
attr_reader :prefix
|
145
|
+
attr_reader :parent
|
146
|
+
|
147
|
+
def initialize(parent_provider)
|
148
|
+
@parent = parent_provider
|
149
|
+
@prefix = get_prefix(parent.files)
|
150
|
+
if @prefix.nil?
|
151
|
+
fail "Could not determine path prefix for #{parent}"
|
152
|
+
end
|
153
|
+
@files = parent.files.find_all { |x| x.start_with?(prefix) && x != prefix }
|
154
|
+
.map { |x| x[prefix.length..-1] }
|
155
|
+
end
|
156
|
+
|
157
|
+
def abs_path(file)
|
158
|
+
return nil if file.nil?
|
159
|
+
prefix + file
|
160
|
+
end
|
161
|
+
|
162
|
+
def read(file)
|
163
|
+
parent.read(abs_path(file))
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def get_prefix(fs)
|
169
|
+
return '' if fs.empty?
|
170
|
+
|
171
|
+
# filter backlisted files
|
172
|
+
fs -= BLACKLIST_FILES
|
173
|
+
|
174
|
+
sorted = fs.sort_by(&:length)
|
175
|
+
get_folder_prefix(sorted)
|
176
|
+
end
|
177
|
+
|
178
|
+
def prefix_candidate_for(file)
|
179
|
+
if file.end_with?(File::SEPARATOR)
|
180
|
+
file
|
181
|
+
else
|
182
|
+
file + File::SEPARATOR
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def get_folder_prefix(fs)
|
187
|
+
return get_files_prefix(fs) if fs.length == 1
|
188
|
+
first, *rest = fs
|
189
|
+
pre = prefix_candidate_for(first)
|
190
|
+
|
191
|
+
if rest.all? { |i| i.start_with? pre }
|
192
|
+
return get_folder_prefix(rest)
|
193
|
+
end
|
194
|
+
get_files_prefix(fs)
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_files_prefix(fs)
|
198
|
+
return '' if fs.empty?
|
199
|
+
|
200
|
+
file = fs[0]
|
201
|
+
bn = File.basename(file)
|
202
|
+
# no more prefixes
|
203
|
+
return '' if bn == file
|
204
|
+
|
205
|
+
i = file.rindex(bn)
|
206
|
+
pre = file[0..i-1]
|
207
|
+
|
208
|
+
rest = fs.find_all { |f| !f.start_with?(pre) }
|
209
|
+
return pre if rest.empty?
|
210
|
+
|
211
|
+
new_pre = get_prefix(rest)
|
212
|
+
return new_pre if pre.start_with? new_pre
|
213
|
+
# edge case: completely different prefixes; retry prefix detection
|
214
|
+
a = File.dirname(pre + 'a')
|
215
|
+
b = File.dirname(new_pre + 'b')
|
216
|
+
get_prefix([a, b])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
@@ -1,106 +1,85 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
# author: Dominik Richter
|
3
3
|
# author: Christoph Hartmann
|
4
|
-
|
5
4
|
require 'utils/plugin_registry'
|
5
|
+
require 'inspec/file_provider'
|
6
|
+
require 'digest'
|
6
7
|
|
7
8
|
module Inspec
|
8
9
|
module Plugins
|
10
|
+
#
|
11
|
+
# An Inspec::Plugins::Fetcher is responsible for fetching a remote
|
12
|
+
# source to a local directory or file provided by the user.
|
13
|
+
#
|
14
|
+
# In general, there are two kinds of fetchers. (1) Fetchers that
|
15
|
+
# implement this entire API (see the Git or Url fetchers for
|
16
|
+
# examples), and (2) fetchers that only implement self.resolve and
|
17
|
+
# then call the resolve_next method with a modified target hash.
|
18
|
+
# Fetchers in (2) do not need to implement the functions in this
|
19
|
+
# class because the caller will never actually get an instance of
|
20
|
+
# those fetchers.
|
21
|
+
#
|
9
22
|
class Fetcher < PluginRegistry::Plugin
|
10
23
|
def self.plugin_registry
|
11
24
|
Inspec::Fetcher
|
12
25
|
end
|
13
26
|
|
14
|
-
# Provide a list of files that are available to this fetcher.
|
15
27
|
#
|
16
|
-
#
|
17
|
-
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
# Read a file using this fetcher. The name must correspond to a file
|
22
|
-
# available to this fetcher. Use #files to retrieve the list of
|
23
|
-
# files.
|
28
|
+
# The path to the archive on disk. This can be passed to a
|
29
|
+
# FileProvider to get access to the files in the fetched
|
30
|
+
# profile.
|
24
31
|
#
|
25
|
-
|
26
|
-
|
27
|
-
def read(_file)
|
28
|
-
fail "Fetcher #{self} does not implement `read(...)`. This is required."
|
29
|
-
end
|
30
|
-
|
31
|
-
def relative_target
|
32
|
-
RelFetcher.new(self)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
BLACKLIST_FILES = [
|
37
|
-
'/pax_global_header',
|
38
|
-
'pax_global_header',
|
39
|
-
].freeze
|
40
|
-
|
41
|
-
class RelFetcher < Fetcher
|
42
|
-
attr_reader :files
|
43
|
-
attr_reader :prefix
|
44
|
-
|
45
|
-
def initialize(fetcher)
|
46
|
-
@parent = fetcher
|
47
|
-
@prefix = get_prefix(fetcher.files)
|
48
|
-
@files = fetcher.files.find_all { |x| x.start_with? prefix }
|
49
|
-
.map { |x| x[prefix.length..-1] }
|
32
|
+
def archive_path
|
33
|
+
fail "Fetcher #{self} does not implement `archive_path()`. This is required."
|
50
34
|
end
|
51
35
|
|
52
|
-
|
53
|
-
|
54
|
-
|
36
|
+
#
|
37
|
+
# Fetches the remote source to a local source, using the
|
38
|
+
# provided path as a partial filename. That is, if you pass
|
39
|
+
# /foo/bar/baz, the fetcher can create:
|
40
|
+
#
|
41
|
+
# /foo/bar/baz/: A profile directory, or
|
42
|
+
# /foo/bar/baz.tar.gz: A profile tarball, or
|
43
|
+
# /foo/bar/baz.zip
|
44
|
+
#
|
45
|
+
def fetch(_path)
|
46
|
+
fail "Fetcher #{self} does not implement `fetch()`. This is required."
|
55
47
|
end
|
56
48
|
|
57
|
-
|
58
|
-
|
49
|
+
#
|
50
|
+
# The full specification of the remote source, with any
|
51
|
+
# ambigious references provided by the user resolved to an exact
|
52
|
+
# reference where possible. For example, in the Git provide, a
|
53
|
+
# tag will be resolved to an exact revision.
|
54
|
+
#
|
55
|
+
def resolved_source
|
56
|
+
fail "Fetcher #{self} does not implement `resolved_source()`. This is required for terminal fetchers."
|
59
57
|
end
|
60
58
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
59
|
+
#
|
60
|
+
# relative_target is provided to keep compatibility with 3rd
|
61
|
+
# party plugins.
|
62
|
+
#
|
63
|
+
# Deprecated: This function may be removed in future versions of
|
64
|
+
# Inspec, don't depend on it in new plugins.
|
65
|
+
#
|
66
|
+
# @returns [Inspec::RelativeFileProvider]
|
67
|
+
#
|
68
|
+
def relative_target
|
69
|
+
file_provider = Inspec::FileProvider.for_path(archive_path)
|
70
|
+
file_provider.relative_provider
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
73
|
+
#
|
74
|
+
# A string based on the components of the resolved source,
|
75
|
+
# suitable for constructing per-source file names.
|
76
|
+
#
|
77
|
+
def cache_key
|
78
|
+
key = ''
|
79
|
+
resolved_source.each do |k, v|
|
80
|
+
key << "#{k}:#{v}"
|
79
81
|
end
|
80
|
-
|
81
|
-
fs
|
82
|
-
end
|
83
|
-
|
84
|
-
def get_files_prefix(fs)
|
85
|
-
return '' if fs.empty?
|
86
|
-
|
87
|
-
file = fs[0]
|
88
|
-
bn = File.basename(file)
|
89
|
-
# no more prefixes
|
90
|
-
return '' if bn == file
|
91
|
-
|
92
|
-
i = file.rindex(bn)
|
93
|
-
pre = file[0..i-1]
|
94
|
-
|
95
|
-
rest = fs.find_all { |f| !f.start_with?(pre) }
|
96
|
-
return pre if rest.empty?
|
97
|
-
|
98
|
-
new_pre = get_prefix(rest)
|
99
|
-
return new_pre if pre.start_with? new_pre
|
100
|
-
# edge case: completely different prefixes; retry prefix detection
|
101
|
-
a = File.dirname(pre + 'a')
|
102
|
-
b = File.dirname(new_pre + 'b')
|
103
|
-
get_prefix([a, b])
|
82
|
+
Digest::SHA256.hexdigest key
|
104
83
|
end
|
105
84
|
end
|
106
85
|
end
|
data/lib/inspec/profile.rb
CHANGED
@@ -6,11 +6,14 @@
|
|
6
6
|
require 'forwardable'
|
7
7
|
require 'inspec/polyfill'
|
8
8
|
require 'inspec/fetcher'
|
9
|
+
require 'inspec/file_provider'
|
9
10
|
require 'inspec/source_reader'
|
10
11
|
require 'inspec/metadata'
|
11
12
|
require 'inspec/backend'
|
12
13
|
require 'inspec/rule'
|
14
|
+
require 'inspec/log'
|
13
15
|
require 'inspec/profile_context'
|
16
|
+
require 'inspec/dependencies/vendor_index'
|
14
17
|
require 'inspec/dependencies/lockfile'
|
15
18
|
require 'inspec/dependencies/dependency_set'
|
16
19
|
|
@@ -18,24 +21,34 @@ module Inspec
|
|
18
21
|
class Profile # rubocop:disable Metrics/ClassLength
|
19
22
|
extend Forwardable
|
20
23
|
|
21
|
-
def self.resolve_target(target)
|
22
|
-
|
24
|
+
def self.resolve_target(target, cache = nil)
|
25
|
+
cache ||= VendorIndex.new
|
23
26
|
fetcher = Inspec::Fetcher.resolve(target)
|
24
27
|
if fetcher.nil?
|
25
28
|
fail("Could not fetch inspec profile in #{target.inspect}.")
|
26
29
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
+
|
31
|
+
if cache.exists?(fetcher.cache_key)
|
32
|
+
Inspec::Log.debug "Using cached dependency for #{target}"
|
33
|
+
cache.prefered_entry_for(fetcher.cache_key)
|
34
|
+
else
|
35
|
+
fetcher.fetch(cache.base_path_for(fetcher.cache_key))
|
36
|
+
fetcher.archive_path
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.for_path(path, opts)
|
41
|
+
file_provider = FileProvider.for_path(path)
|
42
|
+
reader = Inspec::SourceReader.resolve(file_provider.relative_provider)
|
30
43
|
if reader.nil?
|
31
|
-
fail("Don't understand inspec profile in #{
|
44
|
+
fail("Don't understand inspec profile in #{path}, it " \
|
32
45
|
"doesn't look like a supported profile structure.")
|
33
46
|
end
|
34
|
-
reader
|
47
|
+
new(reader, opts)
|
35
48
|
end
|
36
49
|
|
37
50
|
def self.for_target(target, opts = {})
|
38
|
-
|
51
|
+
for_path(resolve_target(target, opts[:cache]), opts.merge(target: target))
|
39
52
|
end
|
40
53
|
|
41
54
|
attr_reader :source_reader
|
@@ -46,18 +59,18 @@ module Inspec
|
|
46
59
|
|
47
60
|
# rubocop:disable Metrics/AbcSize
|
48
61
|
def initialize(source_reader, options = {})
|
49
|
-
@
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@source_reader = source_reader
|
53
|
-
if options[:dependencies]
|
54
|
-
@locked_dependencies = options[:dependencies]
|
55
|
-
end
|
62
|
+
@target = options.delete(:target)
|
63
|
+
@logger = options[:logger] || Logger.new(nil)
|
64
|
+
@locked_dependencies = options[:dependencies]
|
56
65
|
@controls = options[:controls] || []
|
57
|
-
@profile_id =
|
58
|
-
@backend =
|
66
|
+
@profile_id = options[:id]
|
67
|
+
@backend = options[:backend] || Inspec::Backend.create(options)
|
68
|
+
@source_reader = source_reader
|
69
|
+
@tests_collected = false
|
59
70
|
Metadata.finalize(@source_reader.metadata, @profile_id)
|
60
|
-
@runner_context =
|
71
|
+
@runner_context = options[:profile_context] || Inspec::ProfileContext.for_profile(self,
|
72
|
+
@backend,
|
73
|
+
options[:attributes])
|
61
74
|
end
|
62
75
|
|
63
76
|
def name
|
data/lib/inspec/resource.rb
CHANGED
@@ -106,7 +106,8 @@ require 'resources/service'
|
|
106
106
|
require 'resources/shadow'
|
107
107
|
require 'resources/ssl'
|
108
108
|
require 'resources/ssh_conf'
|
109
|
-
require 'resources/
|
109
|
+
require 'resources/sys_info'
|
110
|
+
require 'resources/users'
|
110
111
|
require 'resources/vbscript'
|
111
112
|
require 'resources/windows_feature'
|
112
113
|
require 'resources/xinetd'
|
data/lib/inspec/source_reader.rb
CHANGED
@@ -11,9 +11,6 @@ module Inspec
|
|
11
11
|
class SourceReaderRegistry < PluginRegistry
|
12
12
|
def resolve(target)
|
13
13
|
return nil if target.nil?
|
14
|
-
unless target.is_a? Inspec::Plugins::Fetcher
|
15
|
-
fail "SourceReader cannot resolve targets that aren't Fetchers: #{target.class}"
|
16
|
-
end
|
17
14
|
super(target)
|
18
15
|
end
|
19
16
|
end
|
data/lib/inspec/version.rb
CHANGED
@@ -13,72 +13,101 @@
|
|
13
13
|
# All local GPO parameters can be examined via Registry, but not all security
|
14
14
|
# parameters. Therefore we need a combination of Registry and secedit output
|
15
15
|
|
16
|
+
require 'hashie'
|
17
|
+
|
16
18
|
module Inspec::Resources
|
17
19
|
class SecurityPolicy < Inspec.resource(1)
|
18
20
|
name 'security_policy'
|
19
21
|
desc 'Use the security_policy InSpec audit resource to test security policies on the Microsoft Windows platform.'
|
20
22
|
example "
|
21
23
|
describe security_policy do
|
22
|
-
its('SeNetworkLogonRight') { should
|
24
|
+
its('SeNetworkLogonRight') { should include 'S-1-5-11' }
|
23
25
|
end
|
24
26
|
"
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
27
|
+
|
28
|
+
def content
|
29
|
+
read_content
|
30
|
+
end
|
31
|
+
|
32
|
+
def params(*opts)
|
33
|
+
opts.inject(read_params) do |res, nxt|
|
34
|
+
res.respond_to?(:key) ? res[nxt] : nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def method_missing(name)
|
39
|
+
params = read_params
|
40
|
+
return nil if params.nil?
|
41
|
+
|
42
|
+
# deep search for hash key
|
43
|
+
params.extend Hashie::Extensions::DeepFind
|
44
|
+
res = params.deep_find(name.to_s)
|
45
|
+
res
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
'Security Policy'
|
29
50
|
end
|
30
51
|
|
31
|
-
|
32
|
-
|
52
|
+
private
|
53
|
+
|
54
|
+
def read_content
|
55
|
+
return @content if defined?(@content)
|
56
|
+
|
33
57
|
# export the security policy
|
34
58
|
cmd = inspec.command('secedit /export /cfg win_secpol.cfg')
|
35
59
|
return nil if cmd.exit_status.to_i != 0
|
36
60
|
|
37
61
|
# store file content
|
38
62
|
cmd = inspec.command('Get-Content win_secpol.cfg')
|
39
|
-
|
40
|
-
|
41
|
-
@policy = cmd.stdout
|
42
|
-
@loaded = true
|
43
|
-
|
44
|
-
# returns self
|
45
|
-
self
|
63
|
+
return skip_resource "Can't read security policy" if cmd.exit_status.to_i != 0
|
64
|
+
@content = cmd.stdout
|
46
65
|
|
66
|
+
if @content.empty? && file.size > 0
|
67
|
+
return skip_resource "Can't read security policy"
|
68
|
+
end
|
69
|
+
@content
|
47
70
|
ensure
|
48
71
|
# delete temp file
|
49
72
|
inspec.command('Remove-Item win_secpol.cfg').exit_status.to_i
|
50
73
|
end
|
51
74
|
|
52
|
-
def
|
53
|
-
|
54
|
-
|
55
|
-
load
|
56
|
-
end
|
57
|
-
|
58
|
-
# find line with key
|
59
|
-
key = Regexp.escape(method.to_s)
|
60
|
-
target = ''
|
61
|
-
@policy.each_line {|s|
|
62
|
-
target = s.strip if s =~ /^\s*#{key}\s*=\s*(.*)\b/
|
63
|
-
}
|
75
|
+
def read_params
|
76
|
+
return @params if defined?(@params)
|
77
|
+
return @params = {} if read_content.nil?
|
64
78
|
|
65
|
-
|
66
|
-
|
79
|
+
conf = SimpleConfig.new(
|
80
|
+
@content,
|
81
|
+
assignment_re: /^\s*(.*)=\s*(\S*)\s*$/,
|
82
|
+
)
|
83
|
+
@params = convert_hash(conf.params)
|
84
|
+
end
|
67
85
|
|
68
|
-
|
69
|
-
|
70
|
-
|
86
|
+
# extracts the values, this methods detects:
|
87
|
+
# numbers and SIDs and optimizes them for further usage
|
88
|
+
def extract_value(val)
|
89
|
+
if val =~ /^\d+$/
|
90
|
+
val.to_i
|
91
|
+
# special handling for SID array
|
92
|
+
elsif val =~ /^\*\S/
|
93
|
+
val.split(',').map { |v|
|
94
|
+
v.sub('*S', 'S')
|
95
|
+
}
|
96
|
+
# special handling for string values with "
|
97
|
+
elsif !(m = /^\"(.*)\"$/.match(val)).nil?
|
98
|
+
m[1]
|
71
99
|
else
|
72
|
-
|
73
|
-
# requested value is not available
|
74
|
-
val = nil
|
100
|
+
val
|
75
101
|
end
|
76
|
-
|
77
|
-
val
|
78
102
|
end
|
79
103
|
|
80
|
-
def
|
81
|
-
|
104
|
+
def convert_hash(hash)
|
105
|
+
new_hash = {}
|
106
|
+
hash.each do |k, v|
|
107
|
+
v.is_a?(Hash) ? value = convert_hash(v) : value = extract_value(v)
|
108
|
+
new_hash[k.strip] = value
|
109
|
+
end
|
110
|
+
new_hash
|
82
111
|
end
|
83
112
|
end
|
84
113
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Inspec::Resources
|
3
|
+
# this resource returns additional system informatio
|
4
|
+
class System < Inspec.resource(1)
|
5
|
+
name 'sys_info'
|
6
|
+
|
7
|
+
desc 'Use the user InSpec system resource to test for operating system properties.'
|
8
|
+
example "
|
9
|
+
describe sysinfo do
|
10
|
+
its('hostname') { should eq 'example.com' }
|
11
|
+
end
|
12
|
+
"
|
13
|
+
|
14
|
+
# returns the hostname of the local system
|
15
|
+
def hostname
|
16
|
+
os = inspec.os
|
17
|
+
if os.linux?
|
18
|
+
inspec.command('hostname').stdout.chomp
|
19
|
+
elsif os.windows?
|
20
|
+
inspec.powershell('$env:computername').stdout.chomp
|
21
|
+
else
|
22
|
+
skip_resource 'The `sys_info.hostname` resource is not supported on your OS yet.'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|