inspec-core 4.56.58 → 5.7.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|