inspec-core 4.41.20 → 4.52.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +4 -0
- data/etc/deprecations.json +1 -1
- data/lib/bundles/inspec-supermarket/README.md +21 -2
- data/lib/bundles/inspec-supermarket/cli.rb +20 -3
- data/lib/bundles/inspec-supermarket/target.rb +3 -2
- data/lib/inspec/base_cli.rb +12 -0
- data/lib/inspec/cli.rb +21 -4
- data/lib/inspec/control_eval_context.rb +40 -39
- data/lib/inspec/dsl.rb +18 -3
- data/lib/inspec/globals.rb +5 -0
- data/lib/inspec/plugin/v1/registry.rb +1 -1
- data/lib/inspec/profile.rb +115 -2
- data/lib/inspec/resources/auditd.rb +5 -4
- data/lib/inspec/resources/cassandra.rb +64 -0
- data/lib/inspec/resources/cassandradb_conf.rb +47 -0
- data/lib/inspec/resources/cassandradb_session.rb +68 -0
- data/lib/inspec/resources/chrony_conf.rb +55 -0
- data/lib/inspec/resources/csv.rb +26 -3
- data/lib/inspec/resources/groups.rb +22 -3
- data/lib/inspec/resources/http.rb +135 -54
- data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
- data/lib/inspec/resources/ibmdb2_session.rb +69 -0
- data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
- data/lib/inspec/resources/opa.rb +4 -1
- data/lib/inspec/resources/oracle.rb +66 -0
- data/lib/inspec/resources/oracledb_conf.rb +40 -0
- data/lib/inspec/resources/oracledb_listener_conf.rb +123 -0
- data/lib/inspec/resources/oracledb_session.rb +25 -6
- data/lib/inspec/resources/packages.rb +21 -0
- data/lib/inspec/resources/postgres_session.rb +15 -4
- data/lib/inspec/resources/service.rb +59 -10
- data/lib/inspec/resources/ssl.rb +7 -0
- data/lib/inspec/resources/sybase_conf.rb +37 -0
- data/lib/inspec/resources/sybase_session.rb +111 -0
- data/lib/inspec/resources/users.rb +16 -2
- data/lib/inspec/resources/windows_firewall.rb +1 -1
- data/lib/inspec/resources.rb +9 -0
- data/lib/inspec/run_data/profile.rb +0 -2
- data/lib/inspec/version.rb +1 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5fc9c31a9983866ff40fb11326a3c2bac04342e1d473b845e81639bbca88a8c
|
4
|
+
data.tar.gz: 6fd55b54b5c1510572fa963d2d4833a8f3132ae5cbfb1871a4662f8b3da720d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d9f356b30301430dbeeeb599b696aacfc4ec1caaadafd61e6af40462db60fa400cd2bf4add6a682fe379c65ed4adb0f926a858e6b072066f06296821725f7f92
|
7
|
+
data.tar.gz: 25381065952aa3d460cedd9c56a5b389625b2ed11841f2085047e81444c3aa1d788338468c640f5c27403b4074a5649b825a3289e0b63cdc96d70e8450cc664c
|
data/Gemfile
CHANGED
data/etc/deprecations.json
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
"groups": {
|
5
5
|
"attrs_value_replaces_default": {
|
6
6
|
"action": "warn",
|
7
|
-
"prefix": "The 'default' option for
|
7
|
+
"prefix": "The 'default' option for inputs is being replaced by 'value' - please use it instead."
|
8
8
|
},
|
9
9
|
"attrs_dsl": {
|
10
10
|
"action": "ignore",
|
@@ -8,8 +8,27 @@ To use the CLI, this InSpec add-on adds the following commands:
|
|
8
8
|
|
9
9
|
Compliance profiles from Supermarket can be executed in two ways:
|
10
10
|
|
11
|
-
- via supermarket exec:
|
12
|
-
|
11
|
+
- via supermarket exec:
|
12
|
+
|
13
|
+
**Public Supermarket**
|
14
|
+
|
15
|
+
`inspec supermarket exec nathenharvey/tmp-compliance-profile`
|
16
|
+
|
17
|
+
**Private Supermarket**
|
18
|
+
|
19
|
+
`inspec supermarket exec nathenharvey/tmp-compliance-profile --supermarket_url="PRIVATE_SUPERMARKET_URL"`
|
20
|
+
|
21
|
+
|
22
|
+
- via supermarket scheme:
|
23
|
+
|
24
|
+
**Public Supermarket**
|
25
|
+
|
26
|
+
`inspec exec supermarket://nathenharvey/tmp-compliance-profile`
|
27
|
+
|
28
|
+
**Private Supermarket**
|
29
|
+
|
30
|
+
`inspec exec supermarket://nathenharvey/tmp-compliance-profile --supermarket_url="PRIVATE_SUPERMARKET_URL"`
|
31
|
+
|
13
32
|
|
14
33
|
## Usage
|
15
34
|
|
@@ -15,10 +15,18 @@ module Supermarket
|
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "profiles", "list all available profiles in Chef Supermarket"
|
18
|
+
supermarket_options
|
18
19
|
def profiles
|
19
|
-
|
20
|
-
|
20
|
+
o = config
|
21
|
+
diagnose(o)
|
22
|
+
configure_logger(o)
|
21
23
|
|
24
|
+
# display profiles in format user/profile
|
25
|
+
supermarket_profiles = if o["supermarket_url"]
|
26
|
+
Supermarket::API.profiles(o["supermarket_url"])
|
27
|
+
else
|
28
|
+
Supermarket::API.profiles
|
29
|
+
end
|
22
30
|
headline("Available profiles:")
|
23
31
|
supermarket_profiles.each do |p|
|
24
32
|
li("#{p["tool_name"]} #{mark_text(p["tool_owner"] + "/" + p["slug"])}")
|
@@ -45,9 +53,18 @@ module Supermarket
|
|
45
53
|
end
|
46
54
|
|
47
55
|
desc "info PROFILE", "display Supermarket profile details"
|
56
|
+
supermarket_options
|
48
57
|
def info(profile)
|
58
|
+
o = config
|
59
|
+
diagnose(o)
|
60
|
+
configure_logger(o)
|
61
|
+
|
49
62
|
# check that the profile is available
|
50
|
-
supermarket_profiles =
|
63
|
+
supermarket_profiles = if o["supermarket_url"]
|
64
|
+
Supermarket::API.profiles(o["supermarket_url"])
|
65
|
+
else
|
66
|
+
Supermarket::API.profiles
|
67
|
+
end
|
51
68
|
found = supermarket_profiles.select do |p|
|
52
69
|
profile == "#{p["tool_owner"]}/#{p["slug"]}"
|
53
70
|
end
|
@@ -9,10 +9,11 @@ module Supermarket
|
|
9
9
|
priority 500
|
10
10
|
|
11
11
|
def self.resolve(target, opts = {})
|
12
|
+
supermarket_url = opts["supermarket_url"] || Supermarket::API::SUPERMARKET_URL
|
12
13
|
supermarket_uri, supermarket_server = if target.is_a?(String) && URI(target).scheme == "supermarket"
|
13
|
-
[target,
|
14
|
+
[target, supermarket_url]
|
14
15
|
elsif target.respond_to?(:key?) && target.key?(:supermarket)
|
15
|
-
supermarket_server = target[:supermarket_url] ||
|
16
|
+
supermarket_server = target[:supermarket_url] || supermarket_url
|
16
17
|
["supermarket://#{target[:supermarket]}", supermarket_server]
|
17
18
|
end
|
18
19
|
return nil unless supermarket_uri
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -126,6 +126,8 @@ module Inspec
|
|
126
126
|
desc: "Specify a shell type for winrm (eg. 'elevated' or 'powershell')"
|
127
127
|
option :docker_url, type: :string,
|
128
128
|
desc: "Provides path to Docker API endpoint (Docker)"
|
129
|
+
option :ssh_config_file, type: :array,
|
130
|
+
desc: "A list of paths to the ssh config file, e.g ~/.ssh/config or /etc/ssh/ssh_config"
|
129
131
|
end
|
130
132
|
|
131
133
|
def self.profile_options
|
@@ -135,9 +137,15 @@ module Inspec
|
|
135
137
|
desc: "Use the given path for caching dependencies. (default: ~/.inspec/cache)"
|
136
138
|
end
|
137
139
|
|
140
|
+
def self.supermarket_options
|
141
|
+
option :supermarket_url, type: :string,
|
142
|
+
desc: "Specify the URL of a private Chef Supermarket."
|
143
|
+
end
|
144
|
+
|
138
145
|
def self.exec_options
|
139
146
|
target_options
|
140
147
|
profile_options
|
148
|
+
supermarket_options
|
141
149
|
option :controls, type: :array,
|
142
150
|
desc: "A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests."
|
143
151
|
option :tags, type: :array,
|
@@ -174,6 +182,10 @@ module Inspec
|
|
174
182
|
desc: "After normal execution order, results are sorted by control ID, or by file (default), or randomly. None uses legacy unsorted mode."
|
175
183
|
option :filter_empty_profiles, type: :boolean, default: false,
|
176
184
|
desc: "Filter empty profiles (profiles without controls) from the report."
|
185
|
+
option :filter_waived_controls, type: :boolean,
|
186
|
+
desc: "Do not execute waived controls in InSpec at all. Must use with --waiver-file. Ignores `run` setting of waiver file."
|
187
|
+
option :retain_waiver_data, type: :boolean,
|
188
|
+
desc: "EXPERIMENTAL: Only works in conjunction with --filter-waived-controls, retains waiver data about controls that were skipped"
|
177
189
|
option :command_timeout, type: :numeric,
|
178
190
|
desc: "Maximum seconds to allow commands to run during execution.",
|
179
191
|
long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
|
data/lib/inspec/cli.rb
CHANGED
@@ -93,7 +93,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
93
93
|
end
|
94
94
|
|
95
95
|
desc "check PATH", "verify all tests at the specified PATH"
|
96
|
-
option :format, type: :string
|
96
|
+
option :format, type: :string,
|
97
|
+
desc: "The output format to use doc (default), json. If valid format is not provided then it will use the default."
|
97
98
|
profile_options
|
98
99
|
def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
99
100
|
o = config
|
@@ -121,8 +122,13 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
121
122
|
end
|
122
123
|
puts
|
123
124
|
|
124
|
-
|
125
|
-
|
125
|
+
enable_offenses = !Inspec.locally_windows? # See 5723
|
126
|
+
if result[:errors].empty? && result[:warnings].empty? && result[:offenses].empty?
|
127
|
+
if enable_offenses
|
128
|
+
ui.plain_line("No errors, warnings, or offenses")
|
129
|
+
else
|
130
|
+
ui.plain_line("No errors or warnings")
|
131
|
+
end
|
126
132
|
else
|
127
133
|
item_msg = lambda { |item|
|
128
134
|
pos = [item[:file], item[:line], item[:column]].compact.join(":")
|
@@ -134,11 +140,22 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
134
140
|
|
135
141
|
puts
|
136
142
|
|
143
|
+
if enable_offenses && !result[:offenses].empty?
|
144
|
+
puts "Offenses:\n"
|
145
|
+
result[:offenses].each { |item| ui.cyan(" #{Inspec::UI::GLYPHS[:script_x]} #{item_msg.call(item)}\n\n") }
|
146
|
+
end
|
147
|
+
|
148
|
+
offenses = ui.cyan("#{result[:offenses].length} offenses", print: false)
|
137
149
|
errors = ui.red("#{result[:errors].length} errors", print: false)
|
138
150
|
warnings = ui.yellow("#{result[:warnings].length} warnings", print: false)
|
139
|
-
|
151
|
+
if enable_offenses
|
152
|
+
ui.plain_line("Summary: #{errors}, #{warnings}, #{offenses}")
|
153
|
+
else
|
154
|
+
ui.plain_line("Summary: #{errors}, #{warnings}")
|
155
|
+
end
|
140
156
|
end
|
141
157
|
end
|
158
|
+
|
142
159
|
ui.exit Inspec::UI::EXIT_USAGE_ERROR unless result[:summary][:valid]
|
143
160
|
rescue StandardError => e
|
144
161
|
pretty_handle_exception(e)
|
@@ -18,6 +18,7 @@ module Inspec
|
|
18
18
|
attr_accessor :skip_file
|
19
19
|
attr_accessor :profile_context
|
20
20
|
attr_accessor :resources_dsl
|
21
|
+
attr_accessor :conf
|
21
22
|
|
22
23
|
def initialize(profile_context, resources_dsl, backend, conf, dependencies, require_loader, skip_only_if_eval)
|
23
24
|
@profile_context = profile_context
|
@@ -189,29 +190,24 @@ module Inspec
|
|
189
190
|
@skip_file = true
|
190
191
|
end
|
191
192
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
if
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
193
|
+
# Check if the given control exist in the --tags option
|
194
|
+
def tag_exist_in_control_tags?(tag_ids)
|
195
|
+
tag_option_matches_with_list = false
|
196
|
+
if !tag_ids.empty? && !tag_ids.nil? && profile_tag_config_exist?
|
197
|
+
tag_option_matches_with_list = !(tag_ids & @conf["profile"].include_tags_list).empty?
|
198
|
+
unless tag_option_matches_with_list
|
199
|
+
@conf["profile"].include_tags_list.any? do |inclusion|
|
200
|
+
# Try to see if the inclusion is a regex, and if it matches
|
201
|
+
if inclusion.is_a?(Regexp)
|
202
|
+
tag_ids.each do |id|
|
203
|
+
tag_option_matches_with_list = (inclusion =~ id)
|
204
|
+
break if tag_option_matches_with_list
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
200
209
|
end
|
201
|
-
|
202
|
-
|
203
|
-
# Returns true if configuration hash is not empty and it contains the list of controls is not empty
|
204
|
-
def profile_config_exist?
|
205
|
-
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_controls_list.empty?
|
206
|
-
end
|
207
|
-
|
208
|
-
def profile_tag_config_exist?
|
209
|
-
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_tags_list.empty?
|
210
|
-
end
|
211
|
-
|
212
|
-
# Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included
|
213
|
-
def controls_list_empty?
|
214
|
-
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
|
210
|
+
tag_option_matches_with_list
|
215
211
|
end
|
216
212
|
|
217
213
|
def tags_list_empty?
|
@@ -230,24 +226,29 @@ module Inspec
|
|
230
226
|
id_exist_in_list
|
231
227
|
end
|
232
228
|
|
233
|
-
#
|
234
|
-
def
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
229
|
+
# Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included
|
230
|
+
def controls_list_empty?
|
231
|
+
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def block_location(block, alternate_caller)
|
237
|
+
if block.nil?
|
238
|
+
alternate_caller[/^(.+:\d+):in .+$/, 1] || "unknown"
|
239
|
+
else
|
240
|
+
path, line = block.source_location
|
241
|
+
"#{File.basename(path)}:#{line}"
|
249
242
|
end
|
250
|
-
|
243
|
+
end
|
244
|
+
|
245
|
+
# Returns true if configuration hash is not empty and it contains the list of controls is not empty
|
246
|
+
def profile_config_exist?
|
247
|
+
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_controls_list.empty?
|
248
|
+
end
|
249
|
+
|
250
|
+
def profile_tag_config_exist?
|
251
|
+
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_tags_list.empty?
|
251
252
|
end
|
252
253
|
end
|
253
254
|
end
|
data/lib/inspec/dsl.rb
CHANGED
@@ -93,23 +93,38 @@ module Inspec::DSL
|
|
93
93
|
context = dep_entry.profile.runner_context
|
94
94
|
# if we don't want all the rules, then just make 1 pass to get all rule_IDs
|
95
95
|
# that we want to keep from the original
|
96
|
-
|
96
|
+
if !opts[:include_all] || !(opts[:conf]["profile"].include_tags_list.empty?) || !opts[:conf]["profile"].include_controls_list.empty?
|
97
|
+
filter_included_controls(context, dep_entry.profile, opts, &block)
|
98
|
+
end
|
97
99
|
# interpret the block and skip/modify as required
|
98
100
|
context.load(block) if block_given?
|
99
101
|
bind_context.add_subcontext(context)
|
100
102
|
end
|
101
103
|
|
102
|
-
def self.filter_included_controls(context, profile, &block)
|
104
|
+
def self.filter_included_controls(context, profile, opts, &block)
|
103
105
|
mock = Inspec::Backend.create(Inspec::Config.mock)
|
104
106
|
include_ctx = Inspec::ProfileContext.for_profile(profile, mock)
|
105
107
|
include_ctx.load(block) if block_given?
|
108
|
+
include_ctx.control_eval_context.conf = opts[:conf]
|
109
|
+
control_eval_ctx = include_ctx.control_eval_context
|
106
110
|
# remove all rules that were not registered
|
107
111
|
context.all_rules.each do |r|
|
108
112
|
id = Inspec::Rule.rule_id(r)
|
109
113
|
fid = Inspec::Rule.profile_id(r) + "/" + id
|
110
|
-
|
114
|
+
if !opts[:include_all] && !(include_ctx.rules[id] || include_ctx.rules[fid])
|
111
115
|
context.remove_rule(fid)
|
112
116
|
end
|
117
|
+
|
118
|
+
unless control_eval_ctx.controls_list_empty?
|
119
|
+
# filter the dependent profile controls which are not in the --controls options list
|
120
|
+
context.remove_rule(fid) unless control_eval_ctx.control_exist_in_controls_list?(id)
|
121
|
+
end
|
122
|
+
|
123
|
+
unless control_eval_ctx.tags_list_empty?
|
124
|
+
# filter included controls using --tags
|
125
|
+
tag_ids = control_eval_ctx.control_tags(r)
|
126
|
+
context.remove_rule(fid) unless control_eval_ctx.tag_exist_in_control_tags?(tag_ids)
|
127
|
+
end
|
113
128
|
end
|
114
129
|
end
|
115
130
|
end
|
data/lib/inspec/globals.rb
CHANGED
@@ -11,7 +11,7 @@ class PluginRegistry
|
|
11
11
|
# @return [Plugin] plugin instance if it can be resolved, nil otherwise
|
12
12
|
def resolve(target, opts = {})
|
13
13
|
modules.each do |m|
|
14
|
-
res = if Inspec::Fetcher::Url
|
14
|
+
res = if ["Inspec::Fetcher::Url", "Supermarket::Fetcher"].include? m.to_s
|
15
15
|
m.resolve(target, opts)
|
16
16
|
else
|
17
17
|
m.resolve(target)
|
data/lib/inspec/profile.rb
CHANGED
@@ -217,6 +217,9 @@ module Inspec
|
|
217
217
|
|
218
218
|
locked_dependencies.each(&:collect_tests)
|
219
219
|
|
220
|
+
tests = filter_waived_controls
|
221
|
+
|
222
|
+
# Collect tests
|
220
223
|
tests.each do |path, content|
|
221
224
|
next if content.nil? || content.empty?
|
222
225
|
|
@@ -233,6 +236,65 @@ module Inspec
|
|
233
236
|
@runner_context.all_rules
|
234
237
|
end
|
235
238
|
|
239
|
+
# Wipe out waived controls
|
240
|
+
def filter_waived_controls
|
241
|
+
cfg = Inspec::Config.cached
|
242
|
+
return tests unless cfg["filter_waived_controls"]
|
243
|
+
|
244
|
+
## Find the waivers file
|
245
|
+
# - TODO: cli_opts and instance_variable_get could be exposed
|
246
|
+
waiver_paths = cfg.instance_variable_get(:@cli_opts)["waiver_file"]
|
247
|
+
if waiver_paths.blank?
|
248
|
+
Inspec::Log.error "Must use --waiver-file with --filter-waived-controls"
|
249
|
+
Inspec::UI.new.exit(:usage_error)
|
250
|
+
end
|
251
|
+
|
252
|
+
# # Pull together waiver
|
253
|
+
waived_control_ids = []
|
254
|
+
waiver_paths.each do |waiver_path|
|
255
|
+
waiver_content = YAML.load_file(waiver_path)
|
256
|
+
unless waiver_content
|
257
|
+
# Note that we will have already issued a detailed warning
|
258
|
+
Inspec::Log.error "YAML parsing error in #{waiver_path}"
|
259
|
+
Inspec::UI.new.exit(:usage_error)
|
260
|
+
end
|
261
|
+
waived_control_ids << waiver_content.keys
|
262
|
+
end
|
263
|
+
waived_control_id_regex = "(#{waived_control_ids.join("|")})"
|
264
|
+
|
265
|
+
## Purge tests (this could be doone in next block for performance)
|
266
|
+
## TODO: implement earlier with pure AST and pure autocorrect AST
|
267
|
+
filtered_tests = {}
|
268
|
+
if cfg["retain_waiver_data"]
|
269
|
+
# VERY EXPERIMENTAL, but an empty describe block at the top level
|
270
|
+
# of the control blocks evaluation of ruby code until later-term
|
271
|
+
# waivers (behind the scenes this tells RSpec to hold on and use its internals to lazy load the code). This allows current waiver-data (e.g. skips) to still
|
272
|
+
# be processed and rendered
|
273
|
+
tests.each do |control_filename, source_code|
|
274
|
+
cleared_tests = source_code.scan(/control\s+['"].+?['"].+?(?=(?:control\s+['"].+?['"])|\z)/m).collect do |element|
|
275
|
+
next if element.blank?
|
276
|
+
|
277
|
+
if element&.match?(waived_control_id_regex)
|
278
|
+
splitlines = element.split("\n")
|
279
|
+
splitlines[0] + "\ndescribe '---' do\n" + splitlines[1..-1].join("\n") + "\nend\n"
|
280
|
+
else
|
281
|
+
element
|
282
|
+
end
|
283
|
+
end.join("")
|
284
|
+
filtered_tests[control_filename] = cleared_tests
|
285
|
+
end
|
286
|
+
else
|
287
|
+
tests.each do |control_filename, source_code|
|
288
|
+
cleared_tests = source_code.scan(/control\s+['"].+?['"].+?(?=(?:control\s+['"].+?['"])|\z)/m).select do |control_code|
|
289
|
+
!control_code.match?(waived_control_id_regex)
|
290
|
+
end.join("")
|
291
|
+
|
292
|
+
filtered_tests[control_filename] = cleared_tests
|
293
|
+
end
|
294
|
+
end
|
295
|
+
filtered_tests
|
296
|
+
end
|
297
|
+
|
236
298
|
# This creates the list of controls provided in the --controls options which need to be include
|
237
299
|
# for evaluation.
|
238
300
|
def include_controls_list
|
@@ -386,6 +448,45 @@ module Inspec
|
|
386
448
|
res
|
387
449
|
end
|
388
450
|
|
451
|
+
def cookstyle_linting_check
|
452
|
+
msgs = []
|
453
|
+
return msgs if Inspec.locally_windows? # See #5723
|
454
|
+
|
455
|
+
output = cookstyle_rake_output.split("Offenses:").last
|
456
|
+
msgs = output.split("\n").select { |x| x =~ /[A-Z]:/ } unless output.nil?
|
457
|
+
msgs
|
458
|
+
end
|
459
|
+
|
460
|
+
# Cookstyle linting rake run output
|
461
|
+
def cookstyle_rake_output
|
462
|
+
require "cookstyle"
|
463
|
+
require "rubocop/rake_task"
|
464
|
+
begin
|
465
|
+
RuboCop::RakeTask.new(:cookstyle_lint) do |spec|
|
466
|
+
spec.options += [
|
467
|
+
"--display-cop-names",
|
468
|
+
"--parallel",
|
469
|
+
"--only=InSpec/Deprecations",
|
470
|
+
]
|
471
|
+
spec.patterns += Dir.glob("#{@target}/**/*.rb").reject { |f| File.directory?(f) }
|
472
|
+
spec.fail_on_error = false
|
473
|
+
end
|
474
|
+
rescue LoadError
|
475
|
+
puts "Rubocop is not available. Install the rubocop gem to run the lint tests."
|
476
|
+
end
|
477
|
+
begin
|
478
|
+
stdout = StringIO.new
|
479
|
+
$stdout = stdout
|
480
|
+
Rake::Task["cookstyle_lint"].invoke
|
481
|
+
$stdout = STDOUT
|
482
|
+
Rake.application["cookstyle_lint"].reenable
|
483
|
+
stdout.string
|
484
|
+
rescue => e
|
485
|
+
puts "Cookstyle lint checks could not be performed. Error while running cookstyle - #{e}"
|
486
|
+
""
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
389
490
|
# Check if the profile is internally well-structured. The logger will be
|
390
491
|
# used to print information on errors and warnings which are found.
|
391
492
|
#
|
@@ -402,6 +503,7 @@ module Inspec
|
|
402
503
|
},
|
403
504
|
errors: [],
|
404
505
|
warnings: [],
|
506
|
+
offenses: [],
|
405
507
|
}
|
406
508
|
|
407
509
|
entry = lambda { |file, line, column, control, msg|
|
@@ -424,6 +526,10 @@ module Inspec
|
|
424
526
|
result[:errors].push(entry.call(file, line, column, control, msg))
|
425
527
|
}
|
426
528
|
|
529
|
+
offense = lambda { |file, line, column, control, msg|
|
530
|
+
result[:offenses].push(entry.call(file, line, column, control, msg))
|
531
|
+
}
|
532
|
+
|
427
533
|
@logger.info "Checking profile in #{@target}"
|
428
534
|
meta_path = @source_reader.target.abs_path(@source_reader.metadata.ref)
|
429
535
|
|
@@ -486,8 +592,15 @@ module Inspec
|
|
486
592
|
warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? || control[:checks].empty?
|
487
593
|
end
|
488
594
|
|
489
|
-
#
|
490
|
-
|
595
|
+
# Running cookstyle to check for code offenses
|
596
|
+
cookstyle_linting_check.each do |lint_output|
|
597
|
+
data = lint_output.split(":")
|
598
|
+
msg = "#{data[-2]}:#{data[-1]}"
|
599
|
+
offense.call(data[0], data[1], data[2], nil, msg)
|
600
|
+
end
|
601
|
+
|
602
|
+
# profile is valid if we could not find any error & offenses
|
603
|
+
result[:summary][:valid] = result[:errors].empty? && result[:offenses].empty?
|
491
604
|
|
492
605
|
@logger.info "Control definitions OK." if result[:warnings].empty?
|
493
606
|
result
|
@@ -28,12 +28,13 @@ module Inspec::Resources
|
|
28
28
|
EXAMPLE
|
29
29
|
|
30
30
|
def initialize
|
31
|
-
|
31
|
+
@auditctl_cmd_str = inspec.os.name.eql?("alpine") ? "/usr/sbin/auditctl" : "/sbin/auditctl"
|
32
|
+
unless inspec.command(@auditctl_cmd_str).exist?
|
32
33
|
raise Inspec::Exceptions::ResourceFailed,
|
33
|
-
"Command
|
34
|
+
"Command `#{@auditctl_cmd_str}` does not exist"
|
34
35
|
end
|
35
36
|
|
36
|
-
auditctl_cmd = "
|
37
|
+
auditctl_cmd = "#{@auditctl_cmd_str} -l"
|
37
38
|
result = inspec.command(auditctl_cmd)
|
38
39
|
|
39
40
|
if result.exit_status != 0
|
@@ -68,7 +69,7 @@ module Inspec::Resources
|
|
68
69
|
filter.install_filter_methods_on_resource(self, :params)
|
69
70
|
|
70
71
|
def status(name = nil)
|
71
|
-
@status_content ||= inspec.command("
|
72
|
+
@status_content ||= inspec.command("#{@auditctl_cmd_str} -s").stdout.chomp
|
72
73
|
|
73
74
|
# See: https://github.com/inspec/inspec/issues/3113
|
74
75
|
if @status_content =~ /^AUDIT_STATUS/
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Cassandra < Inspec.resource(1)
|
3
|
+
name "cassandra"
|
4
|
+
supports platform: "unix"
|
5
|
+
supports platform: "windows"
|
6
|
+
|
7
|
+
desc "The 'cassandra' resource is a helper for the 'cql_conf'"
|
8
|
+
|
9
|
+
attr_reader :conf_path
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
case inspec.os[:family]
|
13
|
+
when "debian", "redhat", "linux", "suse"
|
14
|
+
determine_conf_dir_and_path_in_linux
|
15
|
+
when "windows"
|
16
|
+
determine_conf_dir_and_path_in_windows
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_s
|
21
|
+
"CassandraDB"
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def determine_conf_dir_and_path_in_linux
|
27
|
+
cassandra_home = inspec.os_env("CASSANDRA_HOME").content
|
28
|
+
|
29
|
+
if cassandra_home.nil? || cassandra_home.empty?
|
30
|
+
warn "$CASSANDRA_HOME environment variable not set in the system"
|
31
|
+
nil
|
32
|
+
else
|
33
|
+
conf_path = "#{cassandra_home}/cassandra.yaml"
|
34
|
+
if !inspec.file(conf_path).exist?
|
35
|
+
warn "Cassandra conf file not found in #{cassandra_home} directory."
|
36
|
+
nil
|
37
|
+
else
|
38
|
+
@conf_path = conf_path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue => e
|
42
|
+
fail_resource "Errors reading cassandra conf file: #{e}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def determine_conf_dir_and_path_in_windows
|
46
|
+
cassandra_home = inspec.os_env("CASSANDRA_HOME").content
|
47
|
+
|
48
|
+
if cassandra_home.nil? || cassandra_home.empty?
|
49
|
+
warn "CASSANDRA_HOME environment variable not set in the system"
|
50
|
+
nil
|
51
|
+
else
|
52
|
+
conf_path = "#{cassandra_home}\\conf\\cassandra.yaml"
|
53
|
+
if !inspec.file(conf_path).exist?
|
54
|
+
warn "Cassandra conf file not found in #{cassandra_home}\\conf directory."
|
55
|
+
nil
|
56
|
+
else
|
57
|
+
@conf_path = conf_path
|
58
|
+
end
|
59
|
+
end
|
60
|
+
rescue => e
|
61
|
+
fail_resource "Errors reading cassandra conf file: #{e}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require "inspec/resources/json"
|
2
|
+
require "inspec/resources/cassandra"
|
3
|
+
|
4
|
+
module Inspec::Resources
|
5
|
+
class CassandradbConf < JsonConfig
|
6
|
+
name "cassandradb_conf"
|
7
|
+
supports platform: "unix"
|
8
|
+
supports platform: "windows"
|
9
|
+
desc "Use the cql_conf InSpec audit resource to test the contents of the configuration file for Cassandra DB"
|
10
|
+
example <<~EXAMPLE
|
11
|
+
describe cassandradb_conf do
|
12
|
+
its('listen_address') { should eq '0.0.0.0' }
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
def initialize(conf_path = nil)
|
17
|
+
cassandra = nil
|
18
|
+
if conf_path.nil?
|
19
|
+
cassandra = inspec.cassandra
|
20
|
+
@conf_path = cassandra.conf_path
|
21
|
+
else
|
22
|
+
@conf_path = conf_path
|
23
|
+
end
|
24
|
+
|
25
|
+
if cassandra && cassandra.resource_failed?
|
26
|
+
raise cassandra.resource_exception_message
|
27
|
+
elsif @conf_path.nil?
|
28
|
+
return skip_resource "Cassandra db conf path is not set"
|
29
|
+
end
|
30
|
+
|
31
|
+
super(@conf_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse(content)
|
37
|
+
YAML.load(content)
|
38
|
+
rescue => e
|
39
|
+
raise Inspec::Exceptions::ResourceFailed, "Unable to parse `cassandra.yaml` file: #{e.message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def resource_base_name
|
43
|
+
"Cassandra Configuration"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|