inspec-core 4.56.58 → 5.7.9
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/Gemfile +24 -9
- data/etc/deprecations.json +12 -11
- data/inspec-core.gemspec +3 -5
- data/lib/inspec/base_cli.rb +14 -2
- data/lib/inspec/cli.rb +16 -7
- data/lib/inspec/dependencies/dependency_set.rb +2 -6
- data/lib/inspec/dependency_installer.rb +74 -0
- data/lib/inspec/dependency_loader.rb +97 -0
- data/lib/inspec/dsl.rb +16 -23
- data/lib/inspec/env_printer.rb +1 -1
- data/lib/inspec/errors.rb +7 -0
- data/lib/inspec/fetcher/git.rb +28 -43
- data/lib/inspec/fetcher/url.rb +1 -1
- data/lib/inspec/formatters/base.rb +22 -0
- data/lib/inspec/metadata.rb +36 -0
- data/lib/inspec/plugin/v2/plugin_types/streaming_reporter.rb +44 -1
- data/lib/inspec/profile.rb +81 -29
- data/lib/inspec/reporters/automate.rb +1 -1
- data/lib/inspec/reporters/cli.rb +1 -1
- data/lib/inspec/reporters/json.rb +31 -11
- data/lib/inspec/resource.rb +6 -0
- data/lib/inspec/resources/cassandradb_session.rb +3 -4
- data/lib/inspec/resources/cron.rb +49 -0
- data/lib/inspec/resources/file.rb +1 -1
- data/lib/inspec/resources/ibmdb2_session.rb +3 -4
- data/lib/inspec/resources/ipfilter.rb +59 -0
- data/lib/inspec/resources/ipnat.rb +58 -0
- data/lib/inspec/resources/mongodb_session.rb +1 -7
- data/lib/inspec/resources/oracledb_session.rb +7 -20
- data/lib/inspec/resources/postgres_session.rb +5 -7
- data/lib/inspec/resources/processes.rb +4 -6
- data/lib/inspec/resources/service.rb +2 -4
- data/lib/inspec/resources.rb +3 -16
- data/lib/inspec/rule.rb +1 -1
- data/lib/inspec/runner.rb +18 -1
- data/lib/inspec/runner_rspec.rb +15 -0
- data/lib/inspec/schema/exec_json.rb +59 -58
- data/lib/inspec/schema/exec_json_min.rb +16 -16
- data/lib/inspec/schema/primitives.rb +68 -51
- data/lib/inspec/schema/profile_json.rb +27 -27
- data/lib/inspec/schema.rb +1 -0
- data/lib/inspec/secrets/yaml.rb +1 -7
- data/lib/inspec/ui.rb +1 -0
- data/lib/inspec/utils/deprecated_cloud_resources_list.rb +54 -0
- data/lib/inspec/version.rb +1 -1
- data/lib/inspec.rb +3 -0
- data/lib/matchers/matchers.rb +1 -7
- data/lib/plugins/inspec-init/lib/inspec-init/cli.rb +1 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_plugin.rb +9 -0
- data/lib/plugins/inspec-init/lib/inspec-init/cli_resource.rb +126 -0
- data/lib/plugins/inspec-init/lib/inspec-init/renderer.rb +9 -8
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/plugin.erb +16 -0
- data/lib/plugins/inspec-init/templates/plugins/inspec-plugin-template/lib/inspec-plugin-template/streaming_reporter.erb +31 -0
- data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/resources/basic/docs/resource-doc.erb +77 -0
- data/lib/plugins/inspec-init/templates/resources/basic/libraries/inspec-resource-template.erb +94 -0
- data/lib/plugins/inspec-init/templates/resources/plural/docs/resource-doc.erb +62 -0
- data/lib/plugins/inspec-init/templates/resources/plural/libraries/inspec-resource-template.erb +73 -0
- data/lib/plugins/inspec-reporter-html2/templates/body.html.erb +6 -4
- data/lib/plugins/inspec-reporter-html2/templates/control.html.erb +4 -1
- data/lib/plugins/inspec-reporter-html2/templates/profile.html.erb +2 -1
- data/lib/plugins/inspec-reporter-html2/templates/result.html.erb +1 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/README.md +5 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/plugin.rb +13 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/streaming_reporter.rb +112 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar/version.rb +8 -0
- data/lib/plugins/inspec-streaming-reporter-progress-bar/lib/inspec-streaming-reporter-progress-bar.rb +15 -0
- metadata +24 -7
data/lib/inspec/fetcher/git.rb
CHANGED
@@ -41,7 +41,6 @@ module Inspec::Fetcher
|
|
41
41
|
@ref = opts[:ref]
|
42
42
|
@remote_url = expand_local_path(remote_url)
|
43
43
|
@repo_directory = nil
|
44
|
-
@resolved_ref = nil
|
45
44
|
@relative_path = opts[:relative_path] if opts[:relative_path] && !opts[:relative_path].empty?
|
46
45
|
end
|
47
46
|
|
@@ -71,7 +70,7 @@ module Inspec::Fetcher
|
|
71
70
|
if @relative_path
|
72
71
|
perform_relative_path_fetch(destination_path, working_dir)
|
73
72
|
else
|
74
|
-
Inspec::Log.debug("Checkout of #{resolved_ref
|
73
|
+
Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
|
75
74
|
"Moving checkout to #{destination_path}")
|
76
75
|
FileUtils.cp_r(working_dir + "/.", destination_path)
|
77
76
|
end
|
@@ -81,14 +80,14 @@ module Inspec::Fetcher
|
|
81
80
|
end
|
82
81
|
|
83
82
|
def perform_relative_path_fetch(destination_path, working_dir)
|
84
|
-
Inspec::Log.debug("Checkout of #{resolved_ref
|
83
|
+
Inspec::Log.debug("Checkout of #{resolved_ref} successful. " \
|
85
84
|
"Moving #{@relative_path} to #{destination_path}")
|
86
85
|
unless File.exist?("#{working_dir}/#{@relative_path}")
|
87
86
|
# Cleanup the destination path - otherwise we'll have an empty dir
|
88
87
|
# in the cache, which is enough to confuse the cache reader
|
89
88
|
# This is a courtesy, assuming we're writing to the cache; if we're
|
90
89
|
# vendoring to something more complex, don't bother.
|
91
|
-
FileUtils.
|
90
|
+
FileUtils.rmdir(destination_path) if Dir.empty?(destination_path)
|
92
91
|
|
93
92
|
raise Inspec::FetcherFailure, "Cannot find relative path '#{@relative_path}' " \
|
94
93
|
"within profile in git repo specified by '#{@remote_url}'"
|
@@ -97,16 +96,9 @@ module Inspec::Fetcher
|
|
97
96
|
end
|
98
97
|
|
99
98
|
def cache_key
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
OpenSSL::Digest.hexdigest("SHA256", @remote_url + @relative_path)
|
104
|
-
elsif resolved_ref.nil?
|
105
|
-
OpenSSL::Digest.hexdigest("SHA256", @remote_url)
|
106
|
-
else
|
107
|
-
resolved_ref
|
108
|
-
end
|
109
|
-
cache_key
|
99
|
+
return resolved_ref unless @relative_path
|
100
|
+
|
101
|
+
OpenSSL::Digest.hexdigest("SHA256", resolved_ref + @relative_path)
|
110
102
|
end
|
111
103
|
|
112
104
|
def archive_path
|
@@ -114,11 +106,7 @@ module Inspec::Fetcher
|
|
114
106
|
end
|
115
107
|
|
116
108
|
def resolved_source
|
117
|
-
|
118
|
-
source = { git: @remote_url }
|
119
|
-
else
|
120
|
-
source = { git: @remote_url, ref: resolved_ref }
|
121
|
-
end
|
109
|
+
source = { git: @remote_url, ref: resolved_ref }
|
122
110
|
source[:relative_path] = @relative_path if @relative_path
|
123
111
|
source
|
124
112
|
end
|
@@ -137,27 +125,33 @@ module Inspec::Fetcher
|
|
137
125
|
elsif @tag
|
138
126
|
resolve_ref(@tag)
|
139
127
|
else
|
140
|
-
resolve_ref
|
128
|
+
resolve_ref(default_ref)
|
141
129
|
end
|
142
130
|
end
|
143
131
|
|
144
|
-
def
|
145
|
-
command_string =
|
146
|
-
# Running git ls-remote command helps to raise error if git URL is invalid and avoids cache_key creation
|
147
|
-
"git ls-remote \"#{@remote_url}\""
|
148
|
-
else
|
149
|
-
"git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
|
150
|
-
end
|
132
|
+
def default_ref
|
133
|
+
command_string = "git remote show #{@remote_url}"
|
151
134
|
cmd = shellout(command_string)
|
152
|
-
|
153
|
-
|
154
|
-
if ref_name.nil?
|
155
|
-
ref = nil
|
135
|
+
unless cmd.exitstatus == 0
|
136
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{@remote_url} - error running '#{command_string}': #{cmd.stderr}")
|
156
137
|
else
|
157
|
-
ref =
|
138
|
+
ref = cmd.stdout.lines.detect { |l| l.include? "HEAD branch:" }&.split(":")&.last&.strip
|
158
139
|
unless ref
|
159
|
-
raise
|
140
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{@remote_url} - error running '#{command_string}': NULL reference")
|
160
141
|
end
|
142
|
+
|
143
|
+
ref
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def resolve_ref(ref_name)
|
148
|
+
command_string = "git ls-remote \"#{@remote_url}\" \"#{ref_name}*\""
|
149
|
+
cmd = shellout(command_string)
|
150
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - error running '#{command_string}': #{cmd.stderr}") unless cmd.exitstatus == 0
|
151
|
+
|
152
|
+
ref = parse_ls_remote(cmd.stdout, ref_name)
|
153
|
+
unless ref
|
154
|
+
raise Inspec::FetcherFailure, "Profile git dependency failed - unable to resolve #{ref_name} to a specific git commit for #{@remote_url}"
|
161
155
|
end
|
162
156
|
|
163
157
|
ref
|
@@ -206,14 +200,7 @@ module Inspec::Fetcher
|
|
206
200
|
|
207
201
|
def checkout(dir = @repo_directory)
|
208
202
|
clone(dir)
|
209
|
-
#
|
210
|
-
# and will always checkout the default HEAD branch, else it will checkout specific branch, tag or git reference.
|
211
|
-
if resolved_ref.nil?
|
212
|
-
git_cmd("checkout", dir)
|
213
|
-
else
|
214
|
-
git_cmd("checkout #{resolved_ref}", dir)
|
215
|
-
end
|
216
|
-
|
203
|
+
git_cmd("checkout #{resolved_ref}", dir)
|
217
204
|
@repo_directory
|
218
205
|
end
|
219
206
|
|
@@ -221,8 +208,6 @@ module Inspec::Fetcher
|
|
221
208
|
cmd = shellout("git #{cmd}", cwd: dir)
|
222
209
|
cmd.error!
|
223
210
|
cmd.status
|
224
|
-
rescue Mixlib::ShellOut::ShellCommandFailed => e
|
225
|
-
raise Inspec::FetcherFailure, "Error while running git command. #{e.message} "
|
226
211
|
rescue Errno::ENOENT
|
227
212
|
raise Inspec::FetcherFailure, "Profile git dependency failed for #{@remote_url} - to use git sources, you must have git installed."
|
228
213
|
end
|
data/lib/inspec/fetcher/url.rb
CHANGED
@@ -262,7 +262,7 @@ module Inspec::Fetcher
|
|
262
262
|
|
263
263
|
open(target, opts)
|
264
264
|
|
265
|
-
rescue SocketError,
|
265
|
+
rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e
|
266
266
|
raise Inspec::FetcherFailure, "Profile URL dependency #{target} could not be fetched: #{e.message}"
|
267
267
|
end
|
268
268
|
|
@@ -15,6 +15,8 @@ module Inspec::Formatters
|
|
15
15
|
@profiles = []
|
16
16
|
@profiles_info = nil
|
17
17
|
@backend = nil
|
18
|
+
@all_controls_count = nil
|
19
|
+
@control_checks_count_map = {}
|
18
20
|
end
|
19
21
|
|
20
22
|
# RSpec Override: #dump_summary
|
@@ -81,6 +83,26 @@ module Inspec::Formatters
|
|
81
83
|
@profiles.push(profile)
|
82
84
|
end
|
83
85
|
|
86
|
+
# These control count related methods are called via runner rspec library of inspec
|
87
|
+
# And these are used within streaming plugins to determine end of control
|
88
|
+
######### Start of control count related methods
|
89
|
+
def set_controls_count(controls_count)
|
90
|
+
@all_controls_count = controls_count
|
91
|
+
end
|
92
|
+
|
93
|
+
def set_control_checks_count_map(mapping)
|
94
|
+
@control_checks_count_map = mapping
|
95
|
+
end
|
96
|
+
|
97
|
+
def get_controls_count
|
98
|
+
@all_controls_count
|
99
|
+
end
|
100
|
+
|
101
|
+
def get_control_checks_count_map
|
102
|
+
@control_checks_count_map
|
103
|
+
end
|
104
|
+
######### end of control count related methods
|
105
|
+
|
84
106
|
# Return all the collected output to the caller
|
85
107
|
def results
|
86
108
|
run_data
|
data/lib/inspec/metadata.rb
CHANGED
@@ -41,6 +41,7 @@ module Inspec
|
|
41
41
|
description
|
42
42
|
version
|
43
43
|
inspec_version
|
44
|
+
entitlement_id
|
44
45
|
}.each do |name|
|
45
46
|
define_method name.to_sym do |arg|
|
46
47
|
params[name.to_sym] = arg
|
@@ -51,6 +52,10 @@ module Inspec
|
|
51
52
|
params[:depends] || []
|
52
53
|
end
|
53
54
|
|
55
|
+
def gem_dependencies
|
56
|
+
params[:gem_dependencies] || []
|
57
|
+
end
|
58
|
+
|
54
59
|
def supports(sth, version = nil)
|
55
60
|
# Ignore supports with metadata.rb. This file is legacy and the way it
|
56
61
|
# it handles `supports` deprecated. A deprecation warning will be printed
|
@@ -94,6 +99,10 @@ module Inspec
|
|
94
99
|
errors.push("Version needs to be in SemVer format")
|
95
100
|
end
|
96
101
|
|
102
|
+
if params[:entitlement_id] && params[:entitlement_id].strip.empty?
|
103
|
+
errors.push("Entitlement ID should not be blank.")
|
104
|
+
end
|
105
|
+
|
97
106
|
unless supports_runtime?
|
98
107
|
warnings.push("The current inspec version #{Inspec::VERSION} cannot satisfy profile inspec_version constraint #{params[:inspec_version]}")
|
99
108
|
end
|
@@ -109,6 +118,33 @@ module Inspec
|
|
109
118
|
warnings.push("License '#{params[:license]}' needs to be in SPDX format or marked as 'Proprietary'. See https://spdx.org/licenses/.")
|
110
119
|
end
|
111
120
|
|
121
|
+
# If gem_dependencies is set, it must be an array of hashes with keys name and optional version
|
122
|
+
unless params[:gem_dependencies].nil?
|
123
|
+
list = params[:gem_dependencies]
|
124
|
+
if list.is_a?(Array) && list.all? { |e| e.is_a? Hash }
|
125
|
+
list.each do |entry|
|
126
|
+
errors.push("gem_dependencies entries must all have a 'name' field") unless entry.key?(:name)
|
127
|
+
if entry[:version]
|
128
|
+
orig = entry[:version]
|
129
|
+
begin
|
130
|
+
# Split on commas as we may have a complex dep
|
131
|
+
orig.split(",").map { |c| Gem::Requirement.parse(c) }
|
132
|
+
rescue Gem::Requirement::BadRequirementError
|
133
|
+
errors.push "Unparseable gem dependency '#{orig}' for #{entry[:name]}"
|
134
|
+
rescue Inspec::GemDependencyInstallError => e
|
135
|
+
errors.push e.message
|
136
|
+
end
|
137
|
+
end
|
138
|
+
extra = (entry.keys - %i{name version})
|
139
|
+
unless extra.empty?
|
140
|
+
warnings.push "Unknown gem_dependencies key(s) #{extra.join(",")} seen for entry '#{entry[:name]}'"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
else
|
144
|
+
errors.push("gem_dependencies must be a List of Hashes")
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
112
148
|
[errors, warnings]
|
113
149
|
end
|
114
150
|
|
@@ -1,10 +1,53 @@
|
|
1
1
|
module Inspec::Plugin::V2::PluginType
|
2
|
-
class StreamingReporter < Inspec::Plugin::V2::PluginBase
|
2
|
+
class StreamingReporter < Inspec::Plugin::V2::PluginBase
|
3
3
|
register_plugin_type(:streaming_reporter)
|
4
4
|
|
5
5
|
#====================================================================#
|
6
6
|
# StreamingReporter plugin type API
|
7
7
|
#====================================================================#
|
8
8
|
# Implementation classes must implement these methods.
|
9
|
+
|
10
|
+
def initialize_streaming_reporter
|
11
|
+
@running_controls_list = []
|
12
|
+
@control_checks_count_map = {}
|
13
|
+
@controls_count = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
# method to identify when the control started running
|
19
|
+
# this will be useful in executing operations on control's level start
|
20
|
+
def control_started?(control_id)
|
21
|
+
if @running_controls_list.include? control_id
|
22
|
+
false
|
23
|
+
else
|
24
|
+
@running_controls_list.push(control_id)
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# method to identify when the control ended running
|
30
|
+
# this will be useful in executing operations on control's level end
|
31
|
+
def control_ended?(control_id)
|
32
|
+
set_control_checks_count_map_value
|
33
|
+
unless @control_checks_count_map[control_id].nil?
|
34
|
+
@control_checks_count_map[control_id] -= 1
|
35
|
+
@control_checks_count_map[control_id] == 0
|
36
|
+
else
|
37
|
+
false
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# method to identify total no. of controls
|
42
|
+
def controls_count
|
43
|
+
@controls_count ||= RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_controls_count
|
44
|
+
end
|
45
|
+
|
46
|
+
# this method is used in the logic of determining end of control
|
47
|
+
def set_control_checks_count_map_value
|
48
|
+
if @control_checks_count_map.empty?
|
49
|
+
@control_checks_count_map = RSpec.configuration.formatters.grep(Inspec::Formatters::Base).first.get_control_checks_count_map
|
50
|
+
end
|
51
|
+
end
|
9
52
|
end
|
10
53
|
end
|
data/lib/inspec/profile.rb
CHANGED
@@ -13,6 +13,8 @@ require "inspec/dependencies/cache"
|
|
13
13
|
require "inspec/dependencies/lockfile"
|
14
14
|
require "inspec/dependencies/dependency_set"
|
15
15
|
require "inspec/utils/json_profile_summary"
|
16
|
+
require "inspec/dependency_loader"
|
17
|
+
require "inspec/dependency_installer"
|
16
18
|
|
17
19
|
module Inspec
|
18
20
|
class Profile
|
@@ -103,7 +105,6 @@ module Inspec
|
|
103
105
|
@check_mode = options[:check_mode] || false
|
104
106
|
@parent_profile = options[:parent_profile]
|
105
107
|
@legacy_profile_path = options[:profiles_path] || false
|
106
|
-
@check_cookstyle = options[:with_cookstyle]
|
107
108
|
Metadata.finalize(@source_reader.metadata, @profile_id, options)
|
108
109
|
|
109
110
|
# if a backend has already been created, clone it so each profile has its own unique backend object
|
@@ -379,6 +380,66 @@ module Inspec
|
|
379
380
|
@runner_context
|
380
381
|
end
|
381
382
|
|
383
|
+
def collect_gem_dependencies(profile_context)
|
384
|
+
gem_dependencies = []
|
385
|
+
all_profiles = []
|
386
|
+
profile_context.dependencies.list.values.each do |requirement|
|
387
|
+
all_profiles << requirement.profile
|
388
|
+
end
|
389
|
+
all_profiles << self
|
390
|
+
all_profiles.each do |profile|
|
391
|
+
gem_dependencies << profile.metadata.gem_dependencies unless profile.metadata.gem_dependencies.empty?
|
392
|
+
end
|
393
|
+
gem_dependencies.flatten.uniq
|
394
|
+
end
|
395
|
+
|
396
|
+
# Loads the required gems specified in the Profile's metadata file from default inspec gems path i.e. ~/.inspec/gems
|
397
|
+
# else installs and loads them.
|
398
|
+
def load_gem_dependencies
|
399
|
+
gem_dependencies = collect_gem_dependencies(load_libraries)
|
400
|
+
gem_dependencies.each do |gem_data|
|
401
|
+
dependency_loader = DependencyLoader.new
|
402
|
+
if dependency_loader.gem_version_installed?(gem_data[:name], gem_data[:version]) ||
|
403
|
+
dependency_loader.gem_installed?(gem_data[:name])
|
404
|
+
load_gem_dependency(gem_data)
|
405
|
+
else
|
406
|
+
if Inspec::Config.cached[:auto_install_gems]
|
407
|
+
install_gem_dependency(gem_data)
|
408
|
+
load_gem_dependency(gem_data)
|
409
|
+
else
|
410
|
+
ui = Inspec::UI.new
|
411
|
+
gem_dependencies.each { |gem_dependency| ui.list_item("#{gem_dependency[:name]} #{gem_dependency[:version]}") }
|
412
|
+
choice = ui.prompt.select("Would you like to install profile gem dependencies listed above?", %w{Yes No})
|
413
|
+
if choice == "Yes"
|
414
|
+
Inspec::Config.cached[:auto_install_gems] = true
|
415
|
+
load_gem_dependencies
|
416
|
+
else
|
417
|
+
ui.error "Unable to resolve above listed profile gem dependencies."
|
418
|
+
Inspec::UI.new.exit(:gem_dependency_load_error)
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Requires gem_data as argument.
|
426
|
+
# gem_dta example: { name: "gem_name", version: "0.0.1"}
|
427
|
+
def load_gem_dependency(gem_data)
|
428
|
+
dependency_loader = DependencyLoader.new(nil, [gem_data])
|
429
|
+
dependency_loader.load
|
430
|
+
rescue Inspec::GemDependencyLoadError => e
|
431
|
+
raise e.message
|
432
|
+
end
|
433
|
+
|
434
|
+
# Requires gem_data as argument.
|
435
|
+
# gem_dta example: { name: "gem_name", version: "0.0.1"}
|
436
|
+
def install_gem_dependency(gem_data)
|
437
|
+
gem_dependency = DependencyInstaller.new(nil, [gem_data])
|
438
|
+
gem_dependency.install
|
439
|
+
rescue Inspec::GemDependencyInstallError => e
|
440
|
+
raise e.message
|
441
|
+
end
|
442
|
+
|
382
443
|
def to_s
|
383
444
|
"Inspec::Profile<#{name}>"
|
384
445
|
end
|
@@ -594,13 +655,12 @@ module Inspec
|
|
594
655
|
end
|
595
656
|
|
596
657
|
# Running cookstyle to check for code offenses
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
offense.call(data[0], data[1], data[2], nil, msg)
|
602
|
-
end
|
658
|
+
cookstyle_linting_check.each do |lint_output|
|
659
|
+
data = lint_output.split(":")
|
660
|
+
msg = "#{data[-2]}:#{data[-1]}"
|
661
|
+
offense.call(data[0], data[1], data[2], nil, msg)
|
603
662
|
end
|
663
|
+
|
604
664
|
# profile is valid if we could not find any error & offenses
|
605
665
|
result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
|
606
666
|
|
@@ -617,6 +677,7 @@ module Inspec
|
|
617
677
|
end
|
618
678
|
|
619
679
|
# generates a archive of a folder profile
|
680
|
+
# assumes that the profile was checked before
|
620
681
|
def archive(opts)
|
621
682
|
# check if file exists otherwise overwrite the archive
|
622
683
|
dst = archive_name(opts)
|
@@ -633,34 +694,31 @@ module Inspec
|
|
633
694
|
# TODO ignore all .files, but add the files to debug output
|
634
695
|
|
635
696
|
# Generate temporary inspec.json for archive
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
)
|
642
|
-
end
|
697
|
+
Inspec::Utils::JsonProfileSummary.produce_json(
|
698
|
+
info: info,
|
699
|
+
write_path: "#{root_path}inspec.json",
|
700
|
+
suppress_output: true
|
701
|
+
)
|
643
702
|
|
644
703
|
# display all files that will be part of the archive
|
645
704
|
@logger.debug "Add the following files to archive:"
|
646
705
|
files.each { |f| @logger.debug " " + f }
|
647
|
-
@logger.debug " inspec.json"
|
706
|
+
@logger.debug " inspec.json"
|
648
707
|
|
649
|
-
archive_files = opts[:export] ? files.push("inspec.json") : files
|
650
708
|
if opts[:zip]
|
651
709
|
# generate zip archive
|
652
710
|
require "inspec/archive/zip"
|
653
711
|
zag = Inspec::Archive::ZipArchiveGenerator.new
|
654
|
-
zag.archive(root_path,
|
712
|
+
zag.archive(root_path, files.push("inspec.json"), dst)
|
655
713
|
else
|
656
714
|
# generate tar archive
|
657
715
|
require "inspec/archive/tar"
|
658
716
|
tag = Inspec::Archive::TarArchiveGenerator.new
|
659
|
-
tag.archive(root_path,
|
717
|
+
tag.archive(root_path, files.push("inspec.json"), dst)
|
660
718
|
end
|
661
719
|
|
662
720
|
# Cleanup
|
663
|
-
FileUtils.rm_f("#{root_path}inspec.json")
|
721
|
+
FileUtils.rm_f("#{root_path}inspec.json")
|
664
722
|
|
665
723
|
@logger.info "Finished archive generation."
|
666
724
|
true
|
@@ -758,12 +816,10 @@ module Inspec
|
|
758
816
|
return Pathname.new(name)
|
759
817
|
end
|
760
818
|
|
761
|
-
|
762
|
-
metadata = @source_reader.metadata.params
|
763
|
-
name = metadata[:name] ||
|
819
|
+
name = params[:name] ||
|
764
820
|
raise("Cannot create an archive without a profile name! Please "\
|
765
821
|
"specify the name in metadata or use --output to create the archive.")
|
766
|
-
version =
|
822
|
+
version = params[:version] ||
|
767
823
|
raise("Cannot create an archive without a profile version! Please "\
|
768
824
|
"specify the version in metadata or use --output to create the archive.")
|
769
825
|
ext = opts[:zip] ? "zip" : "tar.gz"
|
@@ -780,6 +836,7 @@ module Inspec
|
|
780
836
|
end
|
781
837
|
|
782
838
|
def load_checks_params(params)
|
839
|
+
load_gem_dependencies
|
783
840
|
load_libraries
|
784
841
|
tests = collect_tests
|
785
842
|
params[:controls] = controls = {}
|
@@ -791,12 +848,7 @@ module Inspec
|
|
791
848
|
f = load_rule_filepath(prefix, rule)
|
792
849
|
load_rule(rule, f, controls, groups)
|
793
850
|
end
|
794
|
-
|
795
|
-
# identifying inputs using profile name
|
796
|
-
params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(params[:name])
|
797
|
-
else
|
798
|
-
params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
|
799
|
-
end
|
851
|
+
params[:inputs] = Inspec::InputRegistry.list_inputs_for_profile(@profile_id)
|
800
852
|
params
|
801
853
|
end
|
802
854
|
|
@@ -21,7 +21,7 @@ module Inspec::Reporters
|
|
21
21
|
final_report[:type] = "inspec_report"
|
22
22
|
|
23
23
|
final_report[:end_time] = Time.now.utc.strftime("%FT%TZ")
|
24
|
-
final_report[:node_uuid] =
|
24
|
+
final_report[:node_uuid] = @config["node_uuid"] || report[:platform][:target_id]
|
25
25
|
raise Inspec::ReporterError, "Cannot find a UUID for your node. Please specify one via json-config." if final_report[:node_uuid].nil?
|
26
26
|
|
27
27
|
final_report[:report_uuid] = @config["report_uuid"] || uuid_from_string(final_report[:end_time] + final_report[:node_uuid])
|
data/lib/inspec/reporters/cli.rb
CHANGED
@@ -76,7 +76,7 @@ module Inspec::Reporters
|
|
76
76
|
}
|
77
77
|
header["Failure Message"] = profile[:status_message] if profile[:status] == "failed"
|
78
78
|
header["Target"] = run_data[:platform][:target] unless run_data[:platform][:target].nil?
|
79
|
-
header["Target ID"] =
|
79
|
+
header["Target ID"] = run_data[:platform][:target_id] || ""
|
80
80
|
|
81
81
|
pad = header.keys.max_by(&:length).length + 1
|
82
82
|
header.each do |title, value|
|
@@ -29,28 +29,48 @@ module Inspec::Reporters
|
|
29
29
|
{
|
30
30
|
name: run_data[:platform][:name],
|
31
31
|
release: run_data[:platform][:release],
|
32
|
-
target_id: run_data[:platform][:target_id] ||
|
32
|
+
target_id: run_data[:platform][:target_id] || "",
|
33
33
|
}.reject { |_k, v| v.nil? }
|
34
34
|
end
|
35
35
|
|
36
36
|
def profile_results(control)
|
37
37
|
(control[:results] || []).map { |r|
|
38
38
|
{
|
39
|
-
status:
|
40
|
-
code_desc:
|
41
|
-
run_time:
|
42
|
-
start_time:
|
43
|
-
resource:
|
44
|
-
skip_message:
|
45
|
-
message:
|
46
|
-
exception:
|
47
|
-
backtrace:
|
48
|
-
resource_class:
|
39
|
+
status: r[:status],
|
40
|
+
code_desc: r[:code_desc],
|
41
|
+
run_time: r[:run_time],
|
42
|
+
start_time: r[:start_time],
|
43
|
+
resource: r[:resource],
|
44
|
+
skip_message: r[:skip_message],
|
45
|
+
message: r[:message],
|
46
|
+
exception: r[:exception],
|
47
|
+
backtrace: r[:backtrace],
|
48
|
+
resource_class: r[:resource_class],
|
49
49
|
resource_params: r[:resource_params].to_s,
|
50
|
+
resource_id: extract_resource_id(r),
|
50
51
|
}.reject { |_k, v| v.nil? }
|
51
52
|
}
|
52
53
|
end
|
53
54
|
|
55
|
+
def extract_resource_id(r)
|
56
|
+
# According to the RunData API, this is supposed to be an anonymous
|
57
|
+
# class that represents a resource, with embedded instance methods....
|
58
|
+
resource_obj = r[:resource_title]
|
59
|
+
return resource_obj.resource_id if resource_obj.respond_to?(:resource_id)
|
60
|
+
|
61
|
+
# But sometimes, it isn't, and has been collapsed into the to_s stringification of the resource.
|
62
|
+
if resource_obj.is_a?(String)
|
63
|
+
orig_str = resource_obj
|
64
|
+
# Try to trim off the resource class - eg "File /some/path" => "/some/path"
|
65
|
+
trimmed_str = orig_str.sub(/^#{r[:resource_class]}/i, "").strip
|
66
|
+
trimmed_str.empty? ? orig_str : trimmed_str
|
67
|
+
else
|
68
|
+
# Boo, InSpec is crazy, and we don't know what it possibly could be.
|
69
|
+
# Failsafe for resource_id is empty string.
|
70
|
+
""
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
54
74
|
def profiles
|
55
75
|
run_data[:profiles].map do |p|
|
56
76
|
res = {
|
data/lib/inspec/resource.rb
CHANGED
@@ -34,6 +34,12 @@ module Inspec
|
|
34
34
|
Inspec::Resource.support_registry[key].push(criteria)
|
35
35
|
end
|
36
36
|
|
37
|
+
def resource_id(value = nil)
|
38
|
+
@resource_id = value if value
|
39
|
+
@resource_id = "" if @resource_id.nil?
|
40
|
+
@resource_id
|
41
|
+
end
|
42
|
+
|
37
43
|
# TODO: this is pretty terrible and is only here to work around
|
38
44
|
# the idea that we've trained resource authors to make initialize
|
39
45
|
# methods w/o calling super.
|
@@ -1,11 +1,10 @@
|
|
1
1
|
module Inspec::Resources
|
2
2
|
class Lines
|
3
|
-
attr_reader :output
|
3
|
+
attr_reader :output
|
4
4
|
|
5
|
-
def initialize(raw, desc
|
5
|
+
def initialize(raw, desc)
|
6
6
|
@output = raw
|
7
7
|
@desc = desc
|
8
|
-
@exit_status = exit_status
|
9
8
|
end
|
10
9
|
|
11
10
|
def to_s
|
@@ -41,7 +40,7 @@ module Inspec::Resources
|
|
41
40
|
if cmd.exit_status != 0 || out =~ /Unable to connect to any servers/ || out.downcase =~ /^error:.*/
|
42
41
|
raise Inspec::Exceptions::ResourceFailed, "Cassandra query with errors: #{out}"
|
43
42
|
else
|
44
|
-
Lines.new(cmd.stdout.strip, "Cassandra query: #{q}"
|
43
|
+
Lines.new(cmd.stdout.strip, "Cassandra query: #{q}")
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "inspec/resources/crontab"
|
2
|
+
|
3
|
+
module Inspec::Resources
|
4
|
+
class Cron < Crontab
|
5
|
+
name "cron"
|
6
|
+
supports platform: "unix"
|
7
|
+
desc "Use the cron InSpec audit resource to test entires in the crontab file for a given user. This also can be used as alias to crontab resource."
|
8
|
+
example <<~EXAMPLE
|
9
|
+
describe cron do
|
10
|
+
it { should have_entry '* * * * * /usr/local/bin/foo' }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe cron(user: "username") do
|
14
|
+
its(:table) { should match /you can use regexp/ }
|
15
|
+
end
|
16
|
+
EXAMPLE
|
17
|
+
|
18
|
+
def initialize(opts = nil)
|
19
|
+
super
|
20
|
+
@params = read_cron_contents
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_cron_contents
|
24
|
+
result = inspec.command(crontab_cmd)
|
25
|
+
if result.exit_status == 0
|
26
|
+
result.stdout.lines.map { |l| parse_comment_line(l, comment_char: "#", standalone_comments: false)[0].strip }
|
27
|
+
else
|
28
|
+
error = result.stdout + "\n" + result.stderr
|
29
|
+
raise Inspec::Exceptions::ResourceFailed, "Error while executing #{crontab_cmd} command: #{error}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def table
|
34
|
+
@params.reject(&:empty?).join("\n")
|
35
|
+
end
|
36
|
+
|
37
|
+
def has_entry?(rule)
|
38
|
+
@params.include?(rule)
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_s
|
42
|
+
if is_user_crontab?
|
43
|
+
"cron for user #{@user}"
|
44
|
+
else
|
45
|
+
"cron for current user"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -65,7 +65,7 @@ module Inspec::Resources
|
|
65
65
|
def user_permissions
|
66
66
|
return {} unless exist?
|
67
67
|
|
68
|
-
return
|
68
|
+
return skip_reource"`user_permissions` is not supported on your OS yet." unless inspec.os.windows?
|
69
69
|
|
70
70
|
@perms_provider.user_permissions(file)
|
71
71
|
end
|