inspec 0.34.1 → 1.0.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 +323 -6
- data/Gemfile +10 -0
- data/README.md +1 -1
- data/Rakefile +4 -55
- data/docs/.gitignore +2 -0
- data/docs/README.md +40 -0
- data/docs/cli.md +342 -0
- data/docs/dsl_inspec.md +245 -0
- data/docs/dsl_resource.md +93 -0
- data/docs/inspec_and_friends.md +105 -0
- data/docs/matchers.md +136 -0
- data/docs/plugin_kitchen_inspec.html.md +55 -0
- data/docs/profiles.md +285 -0
- data/docs/resources/apache_conf.md.erb +75 -0
- data/docs/resources/apt.md.erb +84 -0
- data/docs/resources/audit_policy.md.erb +61 -0
- data/docs/resources/auditd_conf.md.erb +79 -0
- data/docs/resources/auditd_rules.md.erb +132 -0
- data/docs/resources/bash.md.erb +84 -0
- data/docs/resources/bond.md.erb +97 -0
- data/docs/resources/bridge.md.erb +67 -0
- data/docs/resources/bsd_service.md.erb +76 -0
- data/docs/resources/command.md.erb +151 -0
- data/docs/resources/csv.md.erb +62 -0
- data/docs/resources/directory.md.erb +43 -0
- data/docs/resources/etc_group.md.erb +116 -0
- data/docs/resources/etc_passwd.md.erb +155 -0
- data/docs/resources/etc_shadow.md.erb +149 -0
- data/docs/resources/file.md.erb +460 -0
- data/docs/resources/gem.md.erb +73 -0
- data/docs/resources/group.md.erb +74 -0
- data/docs/resources/grub_conf.md.erb +115 -0
- data/docs/resources/host.md.erb +85 -0
- data/docs/resources/iis_site.md.erb +142 -0
- data/docs/resources/inetd_conf.md.erb +99 -0
- data/docs/resources/ini.md.erb +69 -0
- data/docs/resources/interface.md.erb +66 -0
- data/docs/resources/iptables.md.erb +70 -0
- data/docs/resources/json.md.erb +76 -0
- data/docs/resources/kernel_module.md.erb +60 -0
- data/docs/resources/kernel_parameter.md.erb +72 -0
- data/docs/resources/launchd_service.md.erb +76 -0
- data/docs/resources/limits_conf.md.erb +80 -0
- data/docs/resources/login_def.md.erb +77 -0
- data/docs/resources/mount.md.erb +83 -0
- data/docs/resources/mysql_conf.md.erb +102 -0
- data/docs/resources/mysql_session.md.erb +63 -0
- data/docs/resources/npm.md.erb +75 -0
- data/docs/resources/ntp_conf.md.erb +76 -0
- data/docs/resources/oneget.md.erb +67 -0
- data/docs/resources/os.md.erb +154 -0
- data/docs/resources/os_env.md.erb +98 -0
- data/docs/resources/package.md.erb +115 -0
- data/docs/resources/parse_config.md.erb +122 -0
- data/docs/resources/parse_config_file.md.erb +143 -0
- data/docs/resources/pip.md.erb +74 -0
- data/docs/resources/port.md.erb +150 -0
- data/docs/resources/postgres_conf.md.erb +90 -0
- data/docs/resources/postgres_session.md.erb +75 -0
- data/docs/resources/powershell.md.erb +116 -0
- data/docs/resources/process.md.erb +73 -0
- data/docs/resources/registry_key.md.erb +149 -0
- data/docs/resources/runit_service.md.erb +76 -0
- data/docs/resources/security_policy.md.erb +61 -0
- data/docs/resources/service.md.erb +135 -0
- data/docs/resources/ssh_config.md.erb +94 -0
- data/docs/resources/sshd_config.md.erb +97 -0
- data/docs/resources/ssl.md.erb +133 -0
- data/docs/resources/sys_info.md.erb +55 -0
- data/docs/resources/systemd_service.md.erb +76 -0
- data/docs/resources/sysv_service.md.erb +76 -0
- data/docs/resources/upstart_service.md.erb +76 -0
- data/docs/resources/user.md.erb +154 -0
- data/docs/resources/users.md.erb +140 -0
- data/docs/resources/vbscript.md.erb +69 -0
- data/docs/resources/windows_feature.md.erb +61 -0
- data/docs/resources/wmi.md.erb +95 -0
- data/docs/resources/xinetd_conf.md.erb +170 -0
- data/docs/resources/yaml.md.erb +69 -0
- data/docs/resources/yum.md.erb +103 -0
- data/docs/ruby_usage.md +154 -0
- data/docs/shared/matcher_be.md.erb +1 -0
- data/docs/shared/matcher_cmp.md.erb +45 -0
- data/docs/shared/matcher_eq.md.erb +3 -0
- data/docs/shared/matcher_include.md.erb +1 -0
- data/docs/shared/matcher_match.md.erb +1 -0
- data/docs/shell.md +150 -0
- data/examples/meta-profile/inspec.yml +2 -4
- data/inspec.gemspec +1 -1
- data/lib/bundles/inspec-compliance/api.rb +28 -18
- data/lib/bundles/inspec-compliance/cli.rb +19 -27
- data/lib/bundles/inspec-compliance/target.rb +15 -2
- data/lib/fetchers/git.rb +4 -0
- data/lib/fetchers/local.rb +16 -1
- data/lib/fetchers/mock.rb +4 -0
- data/lib/fetchers/url.rb +46 -20
- data/lib/inspec/base_cli.rb +14 -0
- data/lib/inspec/cached_fetcher.rb +67 -0
- data/lib/inspec/cli.rb +22 -8
- data/lib/inspec/control_eval_context.rb +12 -2
- data/lib/inspec/dependencies/{vendor_index.rb → cache.rb} +5 -4
- data/lib/inspec/dependencies/dependency_set.rb +8 -14
- data/lib/inspec/dependencies/lockfile.rb +5 -13
- data/lib/inspec/dependencies/requirement.rb +31 -41
- data/lib/inspec/dependencies/resolver.rb +3 -3
- data/lib/inspec/dsl.rb +16 -4
- data/lib/inspec/errors.rb +1 -0
- data/lib/inspec/fetcher.rb +25 -1
- data/lib/inspec/objects/each_loop.rb +5 -2
- data/lib/inspec/objects/test.rb +8 -2
- data/lib/inspec/plugins/fetcher.rb +13 -12
- data/lib/inspec/plugins/resource.rb +3 -0
- data/lib/inspec/profile.rb +62 -26
- data/lib/inspec/profile_context.rb +44 -8
- data/lib/inspec/resource.rb +18 -3
- data/lib/inspec/rspec_json_formatter.rb +91 -40
- data/lib/inspec/rule.rb +45 -2
- data/lib/inspec/runner.rb +51 -39
- data/lib/inspec/shell.rb +5 -16
- data/lib/inspec/version.rb +1 -1
- data/lib/resources/apache_conf.rb +1 -1
- data/lib/resources/gem.rb +1 -0
- data/lib/resources/groups.rb +190 -0
- data/lib/resources/oneget.rb +1 -0
- data/lib/resources/os.rb +1 -1
- data/lib/resources/package.rb +3 -1
- data/lib/resources/parse_config.rb +18 -1
- data/lib/resources/pip.rb +1 -1
- data/lib/resources/security_policy.rb +54 -0
- data/lib/resources/ssl.rb +9 -11
- data/lib/resources/sys_info.rb +1 -1
- data/lib/resources/users.rb +81 -64
- metadata +91 -17
- data/docs/cli.rst +0 -442
- data/docs/dsl_inspec.rst +0 -259
- data/docs/dsl_resource.rst +0 -90
- data/docs/inspec_and_friends.rst +0 -85
- data/docs/matchers.rst +0 -137
- data/docs/profiles.rst +0 -169
- data/docs/readme.rst +0 -105
- data/docs/resources.rst +0 -4836
- data/docs/ruby_usage.rst +0 -145
- data/docs/shell.rst +0 -130
- data/docs/template.rst +0 -51
- data/lib/resources/group.rb +0 -137
data/lib/fetchers/url.rb
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# author: Christoph Hartmann
|
|
4
4
|
|
|
5
5
|
require 'uri'
|
|
6
|
+
require 'digest'
|
|
6
7
|
require 'tempfile'
|
|
7
8
|
require 'open-uri'
|
|
8
9
|
|
|
@@ -81,43 +82,68 @@ module Fetchers
|
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
def fetch(path)
|
|
84
|
-
|
|
85
|
-
@archive_path = download_archive(path)
|
|
85
|
+
@archive_path ||= download_archive(path)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def resolved_source
|
|
89
|
-
{ url: @target }
|
|
89
|
+
@resolved_source ||= { url: @target, sha256: sha256 }
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
def cache_key
|
|
93
|
+
@archive_shasum ||= sha256
|
|
94
|
+
end
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
http_opts = {}
|
|
98
|
-
http_opts['ssl_verify_mode'.to_sym] = OpenSSL::SSL::VERIFY_NONE if @insecure
|
|
99
|
-
http_opts['Authorization'] = "Bearer #{@token}" if @token
|
|
96
|
+
def to_s
|
|
97
|
+
@target
|
|
98
|
+
end
|
|
100
99
|
|
|
101
|
-
|
|
100
|
+
private
|
|
102
101
|
|
|
102
|
+
def sha256
|
|
103
|
+
file = @archive_path || temp_archive_path
|
|
104
|
+
Digest::SHA256.hexdigest File.read(file)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def file_type_from_remote(remote)
|
|
103
108
|
content_type = remote.meta['content-type']
|
|
104
|
-
file_type = MIME_TYPES[content_type]
|
|
105
|
-
throw(RuntimeError, 'Failed to resolve URL target, its '\
|
|
106
|
-
"metadata did not match ZIP or TAR: #{content_type}")
|
|
109
|
+
file_type = MIME_TYPES[content_type]
|
|
107
110
|
|
|
108
|
-
# fall back to tar
|
|
109
111
|
if file_type.nil?
|
|
110
|
-
|
|
112
|
+
Inspec::Log.warn("Unrecognized content type: #{content_type}. Assuming tar.gz")
|
|
113
|
+
file_type = '.tar.gz'
|
|
111
114
|
end
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
|
|
116
|
+
file_type
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def temp_archive_path
|
|
120
|
+
@temp_archive_path ||= download_archive_to_temp
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Downloads archive to temporary file with side effect :( of setting @archive_type
|
|
124
|
+
def download_archive_to_temp
|
|
125
|
+
return @temp_archive_path if ! @temp_archive_path.nil?
|
|
126
|
+
Inspec::Log.debug("Fetching URL: #{@target}")
|
|
127
|
+
http_opts = {}
|
|
128
|
+
http_opts['ssl_verify_mode'.to_sym] = OpenSSL::SSL::VERIFY_NONE if @insecure
|
|
129
|
+
http_opts['Authorization'] = "Bearer #{@token}" if @token
|
|
130
|
+
remote = open(@target, http_opts)
|
|
131
|
+
@archive_type = file_type_from_remote(remote) # side effect :(
|
|
132
|
+
archive = Tempfile.new(['inspec-dl-', @archive_type])
|
|
115
133
|
archive.binmode
|
|
116
134
|
archive.write(remote.read)
|
|
117
135
|
archive.rewind
|
|
118
136
|
archive.close
|
|
119
|
-
|
|
137
|
+
Inspec::Log.debug("Archive stored at temporary location: #{archive.path}")
|
|
138
|
+
@temp_archive_path = archive.path
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def download_archive(path)
|
|
142
|
+
download_archive_to_temp
|
|
143
|
+
final_path = "#{path}#{@archive_type}"
|
|
144
|
+
FileUtils.mv(temp_archive_path, final_path)
|
|
120
145
|
Inspec::Log.debug("Fetched archive moved to: #{final_path}")
|
|
146
|
+
@temp_archive_path = nil
|
|
121
147
|
final_path
|
|
122
148
|
end
|
|
123
149
|
end
|
data/lib/inspec/base_cli.rb
CHANGED
|
@@ -58,6 +58,10 @@ module Inspec
|
|
|
58
58
|
desc: 'Use colors in output.'
|
|
59
59
|
option :attrs, type: :array,
|
|
60
60
|
desc: 'Load attributes file (experimental)'
|
|
61
|
+
option :cache, type: :string,
|
|
62
|
+
desc: 'Use the given path for caching dependencies. (default: ~/.inspec/cache)'
|
|
63
|
+
option :create_lockfile, type: :boolean, default: true,
|
|
64
|
+
desc: 'Write out a lockfile based on this execution (unless one already exists)'
|
|
61
65
|
end
|
|
62
66
|
|
|
63
67
|
private
|
|
@@ -128,6 +132,16 @@ module Inspec
|
|
|
128
132
|
Logger.const_get(l.upcase)
|
|
129
133
|
end
|
|
130
134
|
|
|
135
|
+
def pretty_handle_exception(exception)
|
|
136
|
+
case exception
|
|
137
|
+
when Inspec::Error
|
|
138
|
+
$stderr.puts exception.message
|
|
139
|
+
exit(1)
|
|
140
|
+
else
|
|
141
|
+
raise exception # rubocop:disable Style/SignalException
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
131
145
|
def configure_logger(o)
|
|
132
146
|
#
|
|
133
147
|
# TODO(ssd): This is a big gross, but this configures the
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require 'inspec/fetcher'
|
|
3
|
+
require 'forwardable'
|
|
4
|
+
|
|
5
|
+
module Inspec
|
|
6
|
+
class CachedFetcher
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
attr_reader :cache, :target, :fetcher
|
|
10
|
+
def initialize(target, cache)
|
|
11
|
+
@target = target
|
|
12
|
+
@fetcher = Inspec::Fetcher.resolve(target)
|
|
13
|
+
|
|
14
|
+
if @fetcher.nil?
|
|
15
|
+
fail("Could not fetch inspec profile in #{target.inspect}.")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
@cache = cache
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def resolved_source
|
|
22
|
+
fetch
|
|
23
|
+
@fetcher.resolved_source
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def cache_key
|
|
27
|
+
k = if target.is_a?(Hash)
|
|
28
|
+
target[:sha256] || target[:ref]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
if k.nil?
|
|
32
|
+
fetcher.cache_key
|
|
33
|
+
else
|
|
34
|
+
k
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def fetch
|
|
39
|
+
if cache.exists?(cache_key)
|
|
40
|
+
Inspec::Log.debug "Using cached dependency for #{target}"
|
|
41
|
+
[cache.prefered_entry_for(cache_key), false]
|
|
42
|
+
else
|
|
43
|
+
Inspec::Log.debug "Dependency does not exist in the cache #{target}"
|
|
44
|
+
fetcher.fetch(cache.base_path_for(fetcher.cache_key))
|
|
45
|
+
assert_cache_sanity!
|
|
46
|
+
[fetcher.archive_path, fetcher.writable?]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def assert_cache_sanity!
|
|
51
|
+
if target.respond_to?(:key?) && target.key?(:sha256)
|
|
52
|
+
if fetcher.resolved_source[:sha256] != target[:sha256]
|
|
53
|
+
fail <<EOF
|
|
54
|
+
The remote source #{fetcher} no longer has the requested content:
|
|
55
|
+
|
|
56
|
+
Request Content Hash: #{target[:sha256]}
|
|
57
|
+
Actual Content Hash: #{fetcher.resolved_source[:sha256]}
|
|
58
|
+
|
|
59
|
+
For URL, supermarket, compliance, and other sources that do not
|
|
60
|
+
provide versioned artifacts, this likely means that the remote source
|
|
61
|
+
has changed since your lockfile was generated.
|
|
62
|
+
EOF
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/inspec/cli.rb
CHANGED
|
@@ -48,6 +48,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
48
48
|
fdst = File.expand_path(dst)
|
|
49
49
|
File.write(fdst, JSON.dump(profile.info))
|
|
50
50
|
end
|
|
51
|
+
rescue StandardError => e
|
|
52
|
+
pretty_handle_exception(e)
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
desc 'check PATH', 'verify all tests at the specified PATH'
|
|
@@ -97,14 +99,18 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
97
99
|
end
|
|
98
100
|
end
|
|
99
101
|
exit 1 unless result[:summary][:valid]
|
|
102
|
+
rescue StandardError => e
|
|
103
|
+
pretty_handle_exception(e)
|
|
100
104
|
end
|
|
101
105
|
|
|
102
106
|
desc 'vendor', 'Download all dependencies and generate a lockfile'
|
|
103
107
|
def vendor(path = nil)
|
|
104
108
|
configure_logger(opts)
|
|
105
|
-
profile = Inspec::Profile.for_target('./', opts)
|
|
106
|
-
lockfile = profile.generate_lockfile
|
|
109
|
+
profile = Inspec::Profile.for_target('./', opts.merge(cache: Inspec::Cache.new(path)))
|
|
110
|
+
lockfile = profile.generate_lockfile
|
|
107
111
|
File.write('inspec.lock', lockfile.to_yaml)
|
|
112
|
+
rescue StandardError => e
|
|
113
|
+
pretty_handle_exception(e)
|
|
108
114
|
end
|
|
109
115
|
|
|
110
116
|
desc 'archive PATH', 'archive a profile to tar.gz (default) or zip'
|
|
@@ -136,6 +142,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
136
142
|
|
|
137
143
|
# generate archive
|
|
138
144
|
exit 1 unless profile.archive(opts)
|
|
145
|
+
rescue StandardError => e
|
|
146
|
+
pretty_handle_exception(e)
|
|
139
147
|
end
|
|
140
148
|
|
|
141
149
|
desc 'exec PATHS', 'run all test files at the specified PATH.'
|
|
@@ -147,6 +155,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
147
155
|
|
|
148
156
|
# run tests
|
|
149
157
|
run_tests(targets, o)
|
|
158
|
+
rescue StandardError => e
|
|
159
|
+
pretty_handle_exception(e)
|
|
150
160
|
end
|
|
151
161
|
|
|
152
162
|
desc 'detect', 'detect the target OS'
|
|
@@ -165,6 +175,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
165
175
|
mark_text(res[item.to_sym]))
|
|
166
176
|
}
|
|
167
177
|
end
|
|
178
|
+
rescue StandardError => e
|
|
179
|
+
pretty_handle_exception(e)
|
|
168
180
|
end
|
|
169
181
|
|
|
170
182
|
desc 'shell', 'open an interactive debugging shell'
|
|
@@ -196,12 +208,16 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
196
208
|
exit 0
|
|
197
209
|
rescue RuntimeError, Train::UserError => e
|
|
198
210
|
$stderr.puts e.message
|
|
211
|
+
rescue StandardError => e
|
|
212
|
+
pretty_handle_exception(e)
|
|
199
213
|
end
|
|
200
214
|
|
|
201
215
|
desc 'env', 'Output shell-appropriate completion configuration'
|
|
202
216
|
def env(shell = nil)
|
|
203
217
|
p = Inspec::EnvPrinter.new(self.class, shell)
|
|
204
218
|
p.print_and_exit!
|
|
219
|
+
rescue StandardError => e
|
|
220
|
+
pretty_handle_exception(e)
|
|
205
221
|
end
|
|
206
222
|
|
|
207
223
|
desc 'version', 'prints the version of this tool'
|
|
@@ -213,13 +229,11 @@ class Inspec::InspecCLI < Inspec::BaseCLI # rubocop:disable Metrics/ClassLength
|
|
|
213
229
|
|
|
214
230
|
def run_command(opts)
|
|
215
231
|
runner = Inspec::Runner.new(opts)
|
|
216
|
-
|
|
217
|
-
|
|
232
|
+
res = runner.eval_with_virtual_profile(opts[:command])
|
|
233
|
+
runner.load
|
|
218
234
|
|
|
219
|
-
return :ruby_eval, res if
|
|
220
|
-
|
|
221
|
-
runner.register_rules(ctx)
|
|
222
|
-
return :rspec_run, runner.run # rubocop:disable Style/RedundantReturn
|
|
235
|
+
return :ruby_eval, res if runner.all_rules.empty?
|
|
236
|
+
return :rspec_run, runner.run_tests # rubocop:disable Style/RedundantReturn
|
|
223
237
|
end
|
|
224
238
|
end
|
|
225
239
|
|
|
@@ -23,7 +23,7 @@ module Inspec
|
|
|
23
23
|
require 'rspec/core/dsl'
|
|
24
24
|
Class.new(Inspec::Rule) do
|
|
25
25
|
include RSpec::Core::DSL
|
|
26
|
-
|
|
26
|
+
with_resource_dsl resources_dsl
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
|
|
@@ -92,6 +92,14 @@ module Inspec
|
|
|
92
92
|
res
|
|
93
93
|
end
|
|
94
94
|
|
|
95
|
+
define_method :add_resource do |name, new_res|
|
|
96
|
+
resources_dsl.module_exec do
|
|
97
|
+
define_method name.to_sym do |*args|
|
|
98
|
+
new_res.new(@backend, name.to_s, *args)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
95
103
|
define_method :add_resources do |context|
|
|
96
104
|
self.class.class_eval do
|
|
97
105
|
include context.to_resources_dsl
|
|
@@ -107,7 +115,9 @@ module Inspec
|
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
define_method :register_control do |control, &block|
|
|
110
|
-
|
|
118
|
+
if @skip_profile || !profile_context_owner.profile_supports_os?
|
|
119
|
+
::Inspec::Rule.set_skip_rule(control, true)
|
|
120
|
+
end
|
|
111
121
|
|
|
112
122
|
profile_context_owner.register_rule(control, &block) unless control.nil?
|
|
113
123
|
end
|
|
@@ -4,7 +4,7 @@ require 'fileutils'
|
|
|
4
4
|
|
|
5
5
|
module Inspec
|
|
6
6
|
#
|
|
7
|
-
#
|
|
7
|
+
# Inspec::Cache manages an on-disk cache of inspec profiles. The
|
|
8
8
|
# cache can contain:
|
|
9
9
|
#
|
|
10
10
|
# - .tar.gz profile archives
|
|
@@ -16,7 +16,7 @@ module Inspec
|
|
|
16
16
|
# sources.
|
|
17
17
|
#
|
|
18
18
|
#
|
|
19
|
-
class
|
|
19
|
+
class Cache
|
|
20
20
|
attr_reader :path
|
|
21
21
|
def initialize(path = nil)
|
|
22
22
|
@path = path || File.join(Dir.home, '.inspec', 'cache')
|
|
@@ -43,19 +43,20 @@ module Inspec
|
|
|
43
43
|
|
|
44
44
|
#
|
|
45
45
|
# For a given name and source_url, return true if the
|
|
46
|
-
# profile exists in the
|
|
46
|
+
# profile exists in the Cache.
|
|
47
47
|
#
|
|
48
48
|
# @param [String] name
|
|
49
49
|
# @param [String] source_url
|
|
50
50
|
# @return [Boolean]
|
|
51
51
|
#
|
|
52
52
|
def exists?(key)
|
|
53
|
+
return false if key.nil? || key.empty?
|
|
53
54
|
path = base_path_for(key)
|
|
54
55
|
File.directory?(path) || File.exist?("#{path}.tar.gz") || File.exist?("#{path}.zip")
|
|
55
56
|
end
|
|
56
57
|
|
|
57
58
|
#
|
|
58
|
-
# Return the path to given profile in the
|
|
59
|
+
# Return the path to given profile in the cache.
|
|
59
60
|
#
|
|
60
61
|
# The `source_url` parameter should be a URI-like string that
|
|
61
62
|
# fully specifies the source of the exact version we want to pull
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
require 'inspec/dependencies/vendor_index'
|
|
3
2
|
require 'inspec/dependencies/requirement'
|
|
4
3
|
require 'inspec/dependencies/resolver'
|
|
5
4
|
|
|
@@ -7,9 +6,6 @@ module Inspec
|
|
|
7
6
|
#
|
|
8
7
|
# A DependencySet manages a list of dependencies for a profile.
|
|
9
8
|
#
|
|
10
|
-
# Currently this class is a thin wrapper interface to coordinate the
|
|
11
|
-
# VendorIndex and the Resolver.
|
|
12
|
-
#
|
|
13
9
|
class DependencySet
|
|
14
10
|
#
|
|
15
11
|
# Return a dependency set given a lockfile.
|
|
@@ -18,22 +14,21 @@ module Inspec
|
|
|
18
14
|
# @param cwd [String] Current working directory for relative path includes
|
|
19
15
|
# @param vendor_path [String] Path to the vendor directory
|
|
20
16
|
#
|
|
21
|
-
def self.from_lockfile(lockfile, cwd,
|
|
22
|
-
vendor_index = VendorIndex.new(vendor_path)
|
|
17
|
+
def self.from_lockfile(lockfile, cwd, cache, backend)
|
|
23
18
|
dep_tree = lockfile.deps.map do |dep|
|
|
24
|
-
Inspec::Requirement.from_lock_entry(dep, cwd,
|
|
19
|
+
Inspec::Requirement.from_lock_entry(dep, cwd, cache, backend)
|
|
25
20
|
end
|
|
26
21
|
|
|
27
22
|
dep_list = flatten_dep_tree(dep_tree)
|
|
28
|
-
new(cwd,
|
|
23
|
+
new(cwd, cache, dep_list, backend)
|
|
29
24
|
end
|
|
30
25
|
|
|
31
|
-
def self.from_array(dependencies, cwd,
|
|
26
|
+
def self.from_array(dependencies, cwd, cache, backend)
|
|
32
27
|
dep_list = {}
|
|
33
28
|
dependencies.each do |d|
|
|
34
29
|
dep_list[d.name] = d
|
|
35
30
|
end
|
|
36
|
-
new(cwd,
|
|
31
|
+
new(cwd, cache, dep_list, backend)
|
|
37
32
|
end
|
|
38
33
|
|
|
39
34
|
# This is experimental code to test the working of the
|
|
@@ -58,9 +53,9 @@ module Inspec
|
|
|
58
53
|
# @param cwd [String] current working directory for relative path includes
|
|
59
54
|
# @param vendor_path [String] path which contains vendored dependencies
|
|
60
55
|
# @return [dependencies] this
|
|
61
|
-
def initialize(cwd,
|
|
56
|
+
def initialize(cwd, cache, dep_list, backend)
|
|
62
57
|
@cwd = cwd
|
|
63
|
-
@
|
|
58
|
+
@cache = cache
|
|
64
59
|
@dep_list = dep_list
|
|
65
60
|
@backend = backend
|
|
66
61
|
end
|
|
@@ -91,8 +86,7 @@ module Inspec
|
|
|
91
86
|
#
|
|
92
87
|
def vendor(dependencies)
|
|
93
88
|
return nil if dependencies.nil? || dependencies.empty?
|
|
94
|
-
@
|
|
95
|
-
@dep_list = Resolver.resolve(dependencies, @vendor_index, @cwd, @backend)
|
|
89
|
+
@dep_list = Resolver.resolve(dependencies, @cache, @cwd, @backend)
|
|
96
90
|
end
|
|
97
91
|
end
|
|
98
92
|
end
|
|
@@ -4,8 +4,8 @@ require 'yaml'
|
|
|
4
4
|
module Inspec
|
|
5
5
|
class Lockfile
|
|
6
6
|
# When we finalize this feature, we should set these to 1
|
|
7
|
-
MINIMUM_SUPPORTED_VERSION =
|
|
8
|
-
CURRENT_LOCKFILE_VERSION =
|
|
7
|
+
MINIMUM_SUPPORTED_VERSION = 1
|
|
8
|
+
CURRENT_LOCKFILE_VERSION = 1
|
|
9
9
|
|
|
10
10
|
def self.from_dependency_set(dep_set)
|
|
11
11
|
lockfile_content = {
|
|
@@ -32,14 +32,6 @@ lower than the minimum supported version #{MINIMUM_SUPPORTED_VERSION}.
|
|
|
32
32
|
Please create a new lockfile for this project by running:
|
|
33
33
|
|
|
34
34
|
inspec vendor
|
|
35
|
-
EOF
|
|
36
|
-
elsif version == 0
|
|
37
|
-
# Remove this case once this feature stablizes
|
|
38
|
-
$stderr.puts <<EOF
|
|
39
|
-
WARNING: This is a version 0 lockfile. Thank you for trying the
|
|
40
|
-
experimental dependency management feature. Please be aware you may
|
|
41
|
-
need to regenerate this lockfile in future versions as the feature is
|
|
42
|
-
currently in development.
|
|
43
35
|
EOF
|
|
44
36
|
elsif version > CURRENT_LOCKFILE_VERSION
|
|
45
37
|
fail <<EOF
|
|
@@ -78,8 +70,8 @@ EOF
|
|
|
78
70
|
# different entry points of the API.
|
|
79
71
|
def parse_content_hash(lockfile_content_hash)
|
|
80
72
|
case version
|
|
81
|
-
when
|
|
82
|
-
|
|
73
|
+
when 1
|
|
74
|
+
parse_content_hash_1(lockfile_content_hash)
|
|
83
75
|
else
|
|
84
76
|
# If we've gotten here, there is likely a mistake in the
|
|
85
77
|
# lockfile version validation in the constructor.
|
|
@@ -87,7 +79,7 @@ EOF
|
|
|
87
79
|
end
|
|
88
80
|
end
|
|
89
81
|
|
|
90
|
-
def
|
|
82
|
+
def parse_content_hash_1(lockfile_content_hash)
|
|
91
83
|
@deps = if lockfile_content_hash['depends']
|
|
92
84
|
lockfile_content_hash['depends'].map { |i| symbolize_keys(i) }
|
|
93
85
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
|
-
require 'inspec/
|
|
2
|
+
require 'inspec/cached_fetcher'
|
|
3
3
|
require 'inspec/dependencies/dependency_set'
|
|
4
4
|
require 'digest'
|
|
5
5
|
|
|
@@ -9,53 +9,54 @@ module Inspec
|
|
|
9
9
|
# appropriate we delegate to Inspec::Profile directly.
|
|
10
10
|
#
|
|
11
11
|
class Requirement
|
|
12
|
-
|
|
13
|
-
attr_writer :dependencies
|
|
14
|
-
|
|
15
|
-
def self.from_metadata(dep, vendor_index, opts)
|
|
12
|
+
def self.from_metadata(dep, cache, opts)
|
|
16
13
|
fail 'Cannot load empty dependency.' if dep.nil? || dep.empty?
|
|
17
|
-
name
|
|
18
|
-
version = dep[:version]
|
|
19
|
-
new(name, version, vendor_index, opts[:cwd], opts.merge(dep))
|
|
14
|
+
new(dep[:name], dep[:version], cache, opts[:cwd], opts.merge(dep))
|
|
20
15
|
end
|
|
21
16
|
|
|
22
|
-
def self.from_lock_entry(entry, cwd,
|
|
17
|
+
def self.from_lock_entry(entry, cwd, cache, backend)
|
|
23
18
|
req = new(entry[:name],
|
|
24
19
|
entry[:version_constraints],
|
|
25
|
-
|
|
20
|
+
cache,
|
|
26
21
|
cwd,
|
|
27
22
|
entry[:resolved_source].merge(backend: backend))
|
|
28
23
|
|
|
29
24
|
locked_deps = []
|
|
30
|
-
Array(entry[
|
|
31
|
-
locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd,
|
|
25
|
+
Array(entry[:dependencies]).each do |dep_entry|
|
|
26
|
+
locked_deps << Inspec::Requirement.from_lock_entry(dep_entry, cwd, cache, backend)
|
|
32
27
|
end
|
|
33
28
|
req.lock_deps(locked_deps)
|
|
34
29
|
req
|
|
35
30
|
end
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
attr_reader :cwd, :opts, :required_version
|
|
33
|
+
def initialize(name, version_constraints, cache, cwd, opts)
|
|
38
34
|
@name = name
|
|
39
|
-
@
|
|
40
|
-
@
|
|
41
|
-
@vendor_index = vendor_index
|
|
35
|
+
@required_version = Gem::Requirement.new(Array(version_constraints))
|
|
36
|
+
@cache = cache
|
|
42
37
|
@backend = opts[:backend]
|
|
43
38
|
@opts = opts
|
|
44
39
|
@cwd = cwd
|
|
45
40
|
end
|
|
46
41
|
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
#
|
|
43
|
+
# A dependency can be renamed in inspec.yml/inspec.lock. Prefer
|
|
44
|
+
# the name the user gave this dependency over the profile name.
|
|
45
|
+
#
|
|
46
|
+
def name
|
|
47
|
+
@name || profile.name
|
|
49
48
|
end
|
|
50
49
|
|
|
51
50
|
def source_version
|
|
52
|
-
profile.
|
|
51
|
+
profile.version
|
|
53
52
|
end
|
|
54
53
|
|
|
55
54
|
def source_satisfies_spec?
|
|
56
|
-
name
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
gem_dep.match?(profile.name, profile.version)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def gem_dep
|
|
59
|
+
@gem_dep ||= Gem::Dependency.new(profile.name, required_version, :runtime)
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
def resolved_source
|
|
@@ -66,14 +67,13 @@ module Inspec
|
|
|
66
67
|
h = {
|
|
67
68
|
'name' => name,
|
|
68
69
|
'resolved_source' => resolved_source,
|
|
69
|
-
'version_constraints' =>
|
|
70
|
+
'version_constraints' => required_version.to_s,
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
if !dependencies.empty?
|
|
73
74
|
h['dependencies'] = dependencies.map(&:to_hash)
|
|
74
75
|
end
|
|
75
76
|
|
|
76
|
-
h['content_hash'] = content_hash if content_hash
|
|
77
77
|
h
|
|
78
78
|
end
|
|
79
79
|
|
|
@@ -81,39 +81,29 @@ module Inspec
|
|
|
81
81
|
@dependencies = dep_array
|
|
82
82
|
end
|
|
83
83
|
|
|
84
|
-
def content_hash
|
|
85
|
-
@content_hash ||= begin
|
|
86
|
-
archive_path = @vendor_index.archive_entry_for(fetcher.cache_key) || fetcher.archive_path
|
|
87
|
-
if archive_path && File.file?(archive_path)
|
|
88
|
-
Digest::SHA256.hexdigest File.read(archive_path)
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
84
|
def fetcher
|
|
94
|
-
@fetcher ||= Inspec::
|
|
95
|
-
fail "No fetcher for #{name} (options: #{opts})" if @fetcher.nil?
|
|
96
|
-
@fetcher
|
|
85
|
+
@fetcher ||= Inspec::CachedFetcher.new(opts, @cache)
|
|
97
86
|
end
|
|
98
87
|
|
|
99
88
|
def dependencies
|
|
100
89
|
@dependencies ||= profile.metadata.dependencies.map do |r|
|
|
101
|
-
Inspec::Requirement.from_metadata(r, @
|
|
90
|
+
Inspec::Requirement.from_metadata(r, @cache, cwd: @cwd, backend: @backend)
|
|
102
91
|
end
|
|
103
92
|
end
|
|
104
93
|
|
|
105
94
|
def to_s
|
|
106
|
-
|
|
95
|
+
name
|
|
107
96
|
end
|
|
108
97
|
|
|
109
98
|
def profile
|
|
99
|
+
return @profile if ! @profile.nil?
|
|
100
|
+
|
|
110
101
|
opts = @opts.dup
|
|
111
|
-
opts[:cache] = @vendor_index
|
|
112
102
|
opts[:backend] = @backend
|
|
113
103
|
if !@dependencies.nil?
|
|
114
|
-
opts[:dependencies] = Inspec::DependencySet.from_array(@dependencies, @cwd, @
|
|
104
|
+
opts[:dependencies] = Inspec::DependencySet.from_array(@dependencies, @cwd, @cache, @backend)
|
|
115
105
|
end
|
|
116
|
-
@profile
|
|
106
|
+
@profile = Inspec::Profile.for_fetcher(fetcher, opts)
|
|
117
107
|
end
|
|
118
108
|
end
|
|
119
109
|
end
|
|
@@ -23,9 +23,9 @@ module Inspec
|
|
|
23
23
|
# implementation of the fetcher being used.
|
|
24
24
|
#
|
|
25
25
|
class Resolver
|
|
26
|
-
def self.resolve(dependencies,
|
|
26
|
+
def self.resolve(dependencies, cache, working_dir, backend)
|
|
27
27
|
reqs = dependencies.map do |dep|
|
|
28
|
-
req = Inspec::Requirement.from_metadata(dep,
|
|
28
|
+
req = Inspec::Requirement.from_metadata(dep, cache, cwd: working_dir, backend: backend)
|
|
29
29
|
req || fail("Cannot initialize dependency: #{req}")
|
|
30
30
|
end
|
|
31
31
|
new.resolve(reqs)
|
|
@@ -50,7 +50,7 @@ module Inspec
|
|
|
50
50
|
def resolve(deps, top_level = true, seen_items = {}, path_string = '') # rubocop:disable Metrics/AbcSize
|
|
51
51
|
graph = {}
|
|
52
52
|
if top_level
|
|
53
|
-
Inspec::Log.debug("Starting traversal of dependencies #{deps.map(&:
|
|
53
|
+
Inspec::Log.debug("Starting traversal of dependencies #{deps.map(&:to_s)}")
|
|
54
54
|
else
|
|
55
55
|
Inspec::Log.debug("Traversing dependency tree of transitive dependency #{deps.map(&:name)}")
|
|
56
56
|
end
|
data/lib/inspec/dsl.rb
CHANGED
|
@@ -18,6 +18,15 @@ module Inspec::DSL
|
|
|
18
18
|
alias require_rules require_controls
|
|
19
19
|
alias include_rules include_controls
|
|
20
20
|
|
|
21
|
+
def require_resource(options = {})
|
|
22
|
+
fail 'You must specify a specific resource name when calling require_resource()' if options[:resource].nil?
|
|
23
|
+
|
|
24
|
+
from_profile = options[:profile] || profile_name
|
|
25
|
+
target_name = options[:as] || options[:resource]
|
|
26
|
+
res = resource_class(from_profile, options[:resource])
|
|
27
|
+
add_resource(target_name, res)
|
|
28
|
+
end
|
|
29
|
+
|
|
21
30
|
def self.load_spec_files_for_profile(bind_context, opts, &block)
|
|
22
31
|
dependencies = opts[:dependencies]
|
|
23
32
|
profile_id = opts[:profile_id]
|
|
@@ -44,12 +53,15 @@ EOF
|
|
|
44
53
|
end
|
|
45
54
|
|
|
46
55
|
def self.filter_included_controls(context, profile, &block)
|
|
47
|
-
|
|
56
|
+
mock = Inspec::Backend.create({ backend: 'mock' })
|
|
57
|
+
include_ctx = Inspec::ProfileContext.for_profile(profile, mock, {})
|
|
48
58
|
include_ctx.load(block) if block_given?
|
|
49
59
|
# remove all rules that were not registered
|
|
50
|
-
context.
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
context.all_rules.each do |r|
|
|
61
|
+
id = Inspec::Rule.rule_id(r)
|
|
62
|
+
fid = Inspec::Rule.profile_id(r) + '/' + id
|
|
63
|
+
unless include_ctx.rules[id] || include_ctx.rules[fid]
|
|
64
|
+
context.remove_rule(fid)
|
|
53
65
|
end
|
|
54
66
|
end
|
|
55
67
|
end
|