inspec-core 4.38.3 → 4.46.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +5 -12
- data/etc/deprecations.json +1 -1
- data/lib/inspec/base_cli.rb +7 -1
- data/lib/inspec/cached_fetcher.rb +2 -2
- data/lib/inspec/cli.rb +4 -1
- data/lib/inspec/control_eval_context.rb +64 -17
- data/lib/inspec/dsl.rb +18 -3
- data/lib/inspec/fetcher/url.rb +45 -3
- data/lib/inspec/fetcher.rb +3 -3
- data/lib/inspec/plugin/v1/registry.rb +6 -2
- data/lib/inspec/profile.rb +33 -4
- data/lib/inspec/resources/apache_conf.rb +8 -6
- data/lib/inspec/resources/chrony_conf.rb +55 -0
- data/lib/inspec/resources/csv.rb +26 -3
- data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
- data/lib/inspec/resources/ibmdb2_session.rb +69 -0
- data/lib/inspec/resources/mongodb_session.rb +88 -0
- data/lib/inspec/resources/mssql_session.rb +1 -5
- data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
- data/lib/inspec/resources/opa.rb +26 -0
- data/lib/inspec/resources/opa_api.rb +39 -0
- data/lib/inspec/resources/opa_cli.rb +43 -0
- 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 +16 -6
- data/lib/inspec/resources/postgres.rb +45 -12
- data/lib/inspec/resources/postgres_conf.rb +2 -0
- data/lib/inspec/resources/postgres_hba_conf.rb +2 -1
- data/lib/inspec/resources/postgres_ident_conf.rb +2 -1
- data/lib/inspec/resources/postgres_session.rb +20 -11
- data/lib/inspec/resources/registry_key.rb +1 -1
- data/lib/inspec/resources/security_identifier.rb +8 -14
- data/lib/inspec/resources/security_policy.rb +4 -3
- data/lib/inspec/resources/service.rb +7 -1
- data/lib/inspec/resources/sybase_conf.rb +37 -0
- data/lib/inspec/resources/sybase_session.rb +111 -0
- data/lib/inspec/resources/wmi.rb +1 -1
- data/lib/inspec/resources.rb +9 -0
- data/lib/inspec/rule.rb +1 -1
- data/lib/inspec/run_data/profile.rb +0 -2
- data/lib/inspec/runner.rb +2 -0
- data/lib/inspec/utils/filter.rb +1 -1
- data/lib/inspec/version.rb +1 -1
- data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +1 -1
- data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +1 -1
- data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +16 -15
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1cc013914b6503c547eeb5fbba13ce8398cf039236b82a0422339a8b7d178649
|
4
|
+
data.tar.gz: bb763eb39cb82fa264417d15147769d9d4b3fe1dff34ac40351712a35c6dbcb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 492c4bbde3afe3c8be2d7640bb5e6a04f973a762aa2b541231dc61a3862d2d4d3248a49ff9b149ca66fa07eba83e1cd61260185a61fb073ba6a3cc9b542ac04f
|
7
|
+
data.tar.gz: 89b55b1c8d8da24e1266bf6e19f688d284b88b18ec36c97f3f088c6f0422ac6ae6b0ddcab867bcb174ae789fc8e5bb334ed40cfd49adb26c7c3eb401c3164123
|
data/Gemfile
CHANGED
@@ -20,28 +20,21 @@ end
|
|
20
20
|
# but our runtime dep is still 3.9+
|
21
21
|
gem "rspec", ">= 3.10"
|
22
22
|
|
23
|
-
def probably_x86?
|
24
|
-
# We don't currently build on ARM windows, so assume x86 there
|
25
|
-
return true if RUBY_PLATFORM =~ /windows|mswin|msys|mingw|cygwin/
|
26
|
-
|
27
|
-
# Otherwise rely on uname -m
|
28
|
-
`uname -m`.match?(/^(x86_64|i\d86)/)
|
29
|
-
end
|
30
|
-
|
31
23
|
group :omnibus do
|
32
24
|
gem "rb-readline"
|
33
25
|
gem "appbundler"
|
34
26
|
gem "ed25519" # ed25519 ssh key support done here as its a native gem we can't put in the gemspec
|
35
27
|
gem "bcrypt_pbkdf" # ed25519 ssh key support done here as its a native gem we can't put in the gemspec
|
36
|
-
if probably_x86?
|
37
|
-
gem "x25519" # ed25519 KEX module, not supported on ARM
|
38
|
-
end
|
39
28
|
end
|
40
29
|
|
41
30
|
group :test do
|
42
31
|
gem "chefstyle", "~> 2.0.3"
|
43
32
|
gem "concurrent-ruby", "~> 1.0"
|
44
|
-
|
33
|
+
if Gem.ruby_version.to_s.start_with?("2.5")
|
34
|
+
gem "html-proofer", "= 3.19.1" , platforms: :ruby # do not attempt to run proofer on windows
|
35
|
+
else
|
36
|
+
gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
|
37
|
+
end
|
45
38
|
gem "json_schemer", ">= 0.2.1", "< 0.2.19"
|
46
39
|
gem "m"
|
47
40
|
gem "minitest-sprint", "~> 1.0"
|
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",
|
data/lib/inspec/base_cli.rb
CHANGED
@@ -43,11 +43,15 @@ module Inspec
|
|
43
43
|
begin
|
44
44
|
if (allowed_commands & ARGV.map(&:downcase)).empty? && # Did they use a non-exempt command?
|
45
45
|
!ARGV.empty? # Did they supply at least one command?
|
46
|
-
LicenseAcceptance::Acceptor.check_and_persist(
|
46
|
+
license_acceptor_output = LicenseAcceptance::Acceptor.check_and_persist(
|
47
47
|
Inspec::Dist::EXEC_NAME,
|
48
48
|
Inspec::VERSION,
|
49
49
|
logger: Inspec::Log
|
50
50
|
)
|
51
|
+
if license_acceptor_output && ARGV.count == 1 && (ARGV.first.include? "--chef-license")
|
52
|
+
Inspec::UI.new.exit
|
53
|
+
end
|
54
|
+
license_acceptor_output
|
51
55
|
end
|
52
56
|
rescue LicenseAcceptance::LicenseNotAcceptedError
|
53
57
|
Inspec::Log.error "#{Inspec::Dist::PRODUCT_NAME} cannot execute without accepting the license"
|
@@ -136,6 +140,8 @@ module Inspec
|
|
136
140
|
profile_options
|
137
141
|
option :controls, type: :array,
|
138
142
|
desc: "A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests."
|
143
|
+
option :tags, type: :array,
|
144
|
+
desc: "A list of tags names that are part of controls to filter and run controls, or a list of /regexes/ to match against tags names of controls. Ignore all other tests."
|
139
145
|
option :reporter, type: :array,
|
140
146
|
banner: "one two:/output/file/path",
|
141
147
|
desc: "Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit, yaml"
|
@@ -6,9 +6,9 @@ module Inspec
|
|
6
6
|
extend Forwardable
|
7
7
|
|
8
8
|
attr_reader :cache, :target, :fetcher
|
9
|
-
def initialize(target, cache)
|
9
|
+
def initialize(target, cache, opts = {})
|
10
10
|
@target = target
|
11
|
-
@fetcher = Inspec::Fetcher::Registry.resolve(target)
|
11
|
+
@fetcher = Inspec::Fetcher::Registry.resolve(target, opts)
|
12
12
|
|
13
13
|
if @fetcher.nil?
|
14
14
|
raise("Could not fetch inspec profile in #{target.inspect}.")
|
data/lib/inspec/cli.rb
CHANGED
@@ -65,6 +65,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
65
65
|
desc: "Save the created profile to a path"
|
66
66
|
option :controls, type: :array,
|
67
67
|
desc: "A list of controls to include. Ignore all other tests."
|
68
|
+
option :tags, type: :array,
|
69
|
+
desc: "A list of tags to filter controls and include only those. Ignore all other tests."
|
68
70
|
profile_options
|
69
71
|
def json(target)
|
70
72
|
require "json" unless defined?(JSON)
|
@@ -91,7 +93,8 @@ class Inspec::InspecCLI < Inspec::BaseCLI
|
|
91
93
|
end
|
92
94
|
|
93
95
|
desc "check PATH", "verify all tests at the specified PATH"
|
94
|
-
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."
|
95
98
|
profile_options
|
96
99
|
def check(path) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
|
97
100
|
o = config
|
@@ -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
|
@@ -53,12 +54,30 @@ module Inspec
|
|
53
54
|
|
54
55
|
def control(id, opts = {}, &block)
|
55
56
|
opts[:skip_only_if_eval] = @skip_only_if_eval
|
56
|
-
if
|
57
|
+
if (controls_list_empty? && tags_list_empty?) || control_exist_in_controls_list?(id)
|
57
58
|
register_control(Inspec::Rule.new(id, profile_id, resources_dsl, opts, &block))
|
59
|
+
elsif !tags_list_empty?
|
60
|
+
# Inside elsif rule is initialised before registering it because it enables fetching of control tags
|
61
|
+
# This condition is only true when --tags option is used
|
62
|
+
inspec_rule = Inspec::Rule.new(id, profile_id, resources_dsl, opts, &block)
|
63
|
+
tag_ids = control_tags(inspec_rule)
|
64
|
+
register_control(inspec_rule) if tag_exist_in_control_tags?(tag_ids)
|
58
65
|
end
|
59
66
|
end
|
67
|
+
|
60
68
|
alias rule control
|
61
69
|
|
70
|
+
def control_tags(inspec_rule)
|
71
|
+
all_tags = []
|
72
|
+
inspec_rule.tag.each do |key, value|
|
73
|
+
all_tags.push(key)
|
74
|
+
all_tags.push(value) unless value.nil?
|
75
|
+
end
|
76
|
+
all_tags.flatten.compact.uniq.map(&:to_s)
|
77
|
+
rescue
|
78
|
+
[]
|
79
|
+
end
|
80
|
+
|
62
81
|
# Describe allows users to write rspec-like bare describe
|
63
82
|
# blocks without declaring an inclosing control. Here, we
|
64
83
|
# generate a control for them automatically and then execute
|
@@ -74,7 +93,7 @@ module Inspec
|
|
74
93
|
res = describe(*args, &block)
|
75
94
|
end
|
76
95
|
|
77
|
-
if control_exist_in_controls_list?(id)
|
96
|
+
if controls_list_empty? || control_exist_in_controls_list?(id)
|
78
97
|
register_control(rule, &block)
|
79
98
|
end
|
80
99
|
|
@@ -171,6 +190,47 @@ module Inspec
|
|
171
190
|
@skip_file = true
|
172
191
|
end
|
173
192
|
|
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
|
209
|
+
end
|
210
|
+
tag_option_matches_with_list
|
211
|
+
end
|
212
|
+
|
213
|
+
def tags_list_empty?
|
214
|
+
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_tags_list.empty? || @conf.empty?
|
215
|
+
end
|
216
|
+
|
217
|
+
# Check if the given control exist in the --controls option
|
218
|
+
def control_exist_in_controls_list?(id)
|
219
|
+
id_exist_in_list = false
|
220
|
+
if profile_config_exist?
|
221
|
+
id_exist_in_list = @conf["profile"].include_controls_list.any? do |inclusion|
|
222
|
+
# Try to see if the inclusion is a regex, and if it matches
|
223
|
+
inclusion == id || (inclusion.is_a?(Regexp) && inclusion =~ id)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
id_exist_in_list
|
227
|
+
end
|
228
|
+
|
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
|
+
|
174
234
|
private
|
175
235
|
|
176
236
|
def block_location(block, alternate_caller)
|
@@ -187,21 +247,8 @@ module Inspec
|
|
187
247
|
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_controls_list.empty?
|
188
248
|
end
|
189
249
|
|
190
|
-
|
191
|
-
|
192
|
-
!@conf.empty? && @conf.key?("profile") && @conf["profile"].include_controls_list.empty? || @conf.empty?
|
193
|
-
end
|
194
|
-
|
195
|
-
# Check if the given control exist in the --controls option
|
196
|
-
def control_exist_in_controls_list?(id)
|
197
|
-
id_exist_in_list = false
|
198
|
-
if profile_config_exist?
|
199
|
-
id_exist_in_list = @conf["profile"].include_controls_list.any? do |inclusion|
|
200
|
-
# Try to see if the inclusion is a regex, and if it matches
|
201
|
-
inclusion == id || (inclusion.is_a?(Regexp) && inclusion =~ id)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
id_exist_in_list
|
250
|
+
def profile_tag_config_exist?
|
251
|
+
!@conf.empty? && @conf.key?("profile") && !@conf["profile"].include_tags_list.empty?
|
205
252
|
end
|
206
253
|
end
|
207
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/fetcher/url.rb
CHANGED
@@ -44,11 +44,17 @@ module Inspec::Fetcher
|
|
44
44
|
# - Branch URL
|
45
45
|
# - Commit URL
|
46
46
|
#
|
47
|
-
# master url:
|
47
|
+
# master url(default branch master):
|
48
48
|
# https://github.com/nathenharvey/tmp_compliance_profile/ is transformed to
|
49
49
|
# https://github.com/nathenharvey/tmp_compliance_profile/archive/master.tar.gz
|
50
50
|
# https://bitbucket.org/username/repo is transformed to
|
51
51
|
# https://bitbucket.org/username/repo/get/master.tar.gz
|
52
|
+
|
53
|
+
# main url(default branch main):
|
54
|
+
# https://github.com/nathenharvey/tmp_compliance_profile/ is transformed to
|
55
|
+
# https://github.com/nathenharvey/tmp_compliance_profile/archive/main.tar.gz
|
56
|
+
# https://bitbucket.org/username/repo is transformed to
|
57
|
+
# https://bitbucket.org/username/repo/get/main.tar.gz
|
52
58
|
#
|
53
59
|
# branch:
|
54
60
|
# https://github.com/hardening-io/tests-os-hardening/tree/2.0 is transformed to
|
@@ -68,14 +74,18 @@ module Inspec::Fetcher
|
|
68
74
|
BITBUCKET_URL_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)(\.git)?(/)?$}.freeze
|
69
75
|
BITBUCKET_URL_BRANCH_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)/branch/(?<branch>[\w\.]+)(/)?$}.freeze
|
70
76
|
BITBUCKET_URL_COMMIT_REGEX = %r{^https?://(www\.)?bitbucket\.org/(?<user>[\w-]+)/(?<repo>[\w-]+)/commits/(?<commit>[\w\.]+)(/)?$}.freeze
|
77
|
+
GITHUB_URL = "https://github.com".freeze
|
78
|
+
BITBUCKET_URL = "https://bitbucket.org".freeze
|
71
79
|
|
72
80
|
def self.transform(target)
|
73
81
|
transformed_target = if m = GITHUB_URL_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
|
74
|
-
|
82
|
+
default_branch = default_ref(m, GITHUB_URL)
|
83
|
+
"https://github.com/#{m[:user]}/#{m[:repo]}/archive/#{default_branch}.tar.gz"
|
75
84
|
elsif m = GITHUB_URL_WITH_TREE_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
|
76
85
|
"https://github.com/#{m[:user]}/#{m[:repo]}/archive/#{m[:commit]}.tar.gz"
|
77
86
|
elsif m = BITBUCKET_URL_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
|
78
|
-
|
87
|
+
default_branch = default_ref(m, BITBUCKET_URL)
|
88
|
+
"https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/#{default_branch}.tar.gz"
|
79
89
|
elsif m = BITBUCKET_URL_BRANCH_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
|
80
90
|
"https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/#{m[:branch]}.tar.gz"
|
81
91
|
elsif m = BITBUCKET_URL_COMMIT_REGEX.match(target) # rubocop:disable Lint/AssignmentInCondition
|
@@ -120,6 +130,38 @@ module Inspec::Fetcher
|
|
120
130
|
|
121
131
|
private
|
122
132
|
|
133
|
+
class << self
|
134
|
+
def default_ref(match_data, repo_url)
|
135
|
+
remote_url = "#{repo_url}/#{match_data[:user]}/#{match_data[:repo]}.git"
|
136
|
+
command_string = "git remote show #{remote_url}"
|
137
|
+
cmd = shellout(command_string)
|
138
|
+
unless cmd.exitstatus == 0
|
139
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{remote_url} - error running '#{command_string}': #{cmd.stderr}")
|
140
|
+
else
|
141
|
+
ref = cmd.stdout.lines.detect { |l| l.include? "HEAD branch:" }&.split(":")&.last&.strip
|
142
|
+
unless ref
|
143
|
+
raise(Inspec::FetcherFailure, "Profile git dependency failed with default reference - #{remote_url} - error running '#{command_string}': NULL reference")
|
144
|
+
end
|
145
|
+
|
146
|
+
ref
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def shellout(cmd, opts = {})
|
151
|
+
Inspec::Log.debug("Running external command: #{cmd} (#{opts})")
|
152
|
+
cmd = Mixlib::ShellOut.new(cmd, opts)
|
153
|
+
cmd.run_command
|
154
|
+
Inspec::Log.debug("External command: completed with exit status: #{cmd.exitstatus}")
|
155
|
+
Inspec::Log.debug("External command: STDOUT BEGIN")
|
156
|
+
Inspec::Log.debug(cmd.stdout)
|
157
|
+
Inspec::Log.debug("External command: STDOUT END")
|
158
|
+
Inspec::Log.debug("External command: STDERR BEGIN")
|
159
|
+
Inspec::Log.debug(cmd.stderr)
|
160
|
+
Inspec::Log.debug("External command: STDERR END")
|
161
|
+
cmd
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
123
165
|
def parse_uri(target)
|
124
166
|
return URI.parse(target) if target.is_a?(String)
|
125
167
|
|
data/lib/inspec/fetcher.rb
CHANGED
@@ -2,12 +2,12 @@ require "inspec/plugin/v1"
|
|
2
2
|
|
3
3
|
module Inspec
|
4
4
|
class FetcherRegistry < PluginRegistry
|
5
|
-
def resolve(target)
|
5
|
+
def resolve(target, opts = {})
|
6
6
|
if fetcher_specified?(target)
|
7
|
-
super(target)
|
7
|
+
super(target, opts)
|
8
8
|
else
|
9
9
|
Inspec::Log.debug("Assuming default supermarket source for #{target}")
|
10
|
-
super(with_default_fetcher(target))
|
10
|
+
super(with_default_fetcher(target), opts)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -9,9 +9,13 @@ class PluginRegistry
|
|
9
9
|
#
|
10
10
|
# @param [String] target to resolve
|
11
11
|
# @return [Plugin] plugin instance if it can be resolved, nil otherwise
|
12
|
-
def resolve(target)
|
12
|
+
def resolve(target, opts = {})
|
13
13
|
modules.each do |m|
|
14
|
-
res = m
|
14
|
+
res = if Inspec::Fetcher::Url == m
|
15
|
+
m.resolve(target, opts)
|
16
|
+
else
|
17
|
+
m.resolve(target)
|
18
|
+
end
|
15
19
|
return res unless res.nil?
|
16
20
|
end
|
17
21
|
nil
|
data/lib/inspec/profile.rb
CHANGED
@@ -18,9 +18,9 @@ module Inspec
|
|
18
18
|
class Profile
|
19
19
|
extend Forwardable
|
20
20
|
|
21
|
-
def self.resolve_target(target, cache)
|
21
|
+
def self.resolve_target(target, cache, opts = {})
|
22
22
|
Inspec::Log.debug "Resolve #{target} into cache #{cache.path}"
|
23
|
-
Inspec::CachedFetcher.new(target, cache)
|
23
|
+
Inspec::CachedFetcher.new(target, cache, opts)
|
24
24
|
end
|
25
25
|
|
26
26
|
# Check if the profile contains a vendored cache, move content into global cache
|
@@ -70,7 +70,11 @@ module Inspec
|
|
70
70
|
|
71
71
|
def self.for_target(target, opts = {})
|
72
72
|
opts[:vendor_cache] ||= Cache.new
|
73
|
-
|
73
|
+
config = {}
|
74
|
+
unless opts[:runner_conf].nil?
|
75
|
+
config = opts[:runner_conf].respond_to?(:final_options) ? opts[:runner_conf].final_options : opts[:runner_conf]
|
76
|
+
end
|
77
|
+
fetcher = resolve_target(target, opts[:vendor_cache], config)
|
74
78
|
for_fetcher(fetcher, opts)
|
75
79
|
end
|
76
80
|
|
@@ -87,6 +91,7 @@ module Inspec
|
|
87
91
|
@logger = options[:logger] || Logger.new(nil)
|
88
92
|
@locked_dependencies = options[:dependencies]
|
89
93
|
@controls = options[:controls] || []
|
94
|
+
@tags = options[:tags] || []
|
90
95
|
@writable = options[:writable] || false
|
91
96
|
@profile_id = options[:id]
|
92
97
|
@profile_name = options[:profile_name]
|
@@ -206,7 +211,7 @@ module Inspec
|
|
206
211
|
@params ||= load_params
|
207
212
|
end
|
208
213
|
|
209
|
-
def collect_tests
|
214
|
+
def collect_tests
|
210
215
|
unless @tests_collected || failed?
|
211
216
|
return unless supports_platform?
|
212
217
|
|
@@ -253,6 +258,30 @@ module Inspec
|
|
253
258
|
included_controls
|
254
259
|
end
|
255
260
|
|
261
|
+
# This creates the list of controls to be filtered by tag values provided in the --tags options
|
262
|
+
def include_tags_list
|
263
|
+
return [] if @tags.nil? || @tags.empty?
|
264
|
+
|
265
|
+
included_tags = @tags
|
266
|
+
# Check for anything that might be a regex in the list, and make it official
|
267
|
+
included_tags.each_with_index do |inclusion, index|
|
268
|
+
next if inclusion.is_a?(Regexp)
|
269
|
+
# Insist the user wrap the regex in slashes to demarcate it as a regex
|
270
|
+
next unless inclusion.start_with?("/") && inclusion.end_with?("/")
|
271
|
+
|
272
|
+
inclusion = inclusion[1..-2] # Trim slashes
|
273
|
+
begin
|
274
|
+
re = Regexp.new(inclusion)
|
275
|
+
included_tags[index] = re
|
276
|
+
rescue RegexpError => e
|
277
|
+
warn "Ignoring unparseable regex '/#{inclusion}/' in --control CLI option: #{e.message}"
|
278
|
+
included_tags[index] = nil
|
279
|
+
end
|
280
|
+
end
|
281
|
+
included_tags.compact!
|
282
|
+
included_tags
|
283
|
+
end
|
284
|
+
|
256
285
|
def load_libraries
|
257
286
|
return @runner_context if @libraries_loaded
|
258
287
|
|
@@ -82,7 +82,7 @@ module Inspec::Resources
|
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
|
-
@params.merge!(params)
|
85
|
+
@params.merge!(params) { |key, current_val, new_val| [*current_val].to_a + [*new_val].to_a }
|
86
86
|
|
87
87
|
to_read = to_read.drop(1)
|
88
88
|
to_read += include_files(params).find_all do |fp|
|
@@ -101,12 +101,14 @@ module Inspec::Resources
|
|
101
101
|
include_files_optional = params["IncludeOptional"] || []
|
102
102
|
|
103
103
|
includes = []
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
104
|
+
unless conf_dir.nil?
|
105
|
+
(include_files + include_files_optional).each do |f|
|
106
|
+
id = Pathname.new(f).absolute? ? f : File.join(conf_dir, f)
|
107
|
+
files = find_files(id, depth: 1, type: "file")
|
108
|
+
files += find_files(id, depth: 1, type: "link")
|
108
109
|
|
109
|
-
|
110
|
+
includes.push(files) if files
|
111
|
+
end
|
110
112
|
end
|
111
113
|
|
112
114
|
# [].flatten! == nil
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# chrony_conf
|
2
|
+
|
3
|
+
require "inspec/utils/simpleconfig"
|
4
|
+
require "inspec/utils/file_reader"
|
5
|
+
|
6
|
+
module Inspec::Resources
|
7
|
+
class ChronyConf < Inspec.resource(1)
|
8
|
+
name "chrony_conf"
|
9
|
+
supports platform: "unix"
|
10
|
+
desc "Use the chrony_conf InSpec audit resource to test the synchronization settings defined in the chrony.conf file. This file is typically located at /etc/chrony.conf."
|
11
|
+
example <<~EXAMPLE
|
12
|
+
describe chrony_conf do
|
13
|
+
its('server') { should_not cmp nil }
|
14
|
+
its('restrict') { should include '-4 default kod notrap nomodify nopeer noquery' }
|
15
|
+
its('pool') { should include 'pool.ntp.org iburst' }
|
16
|
+
its('driftfile') { should cmp '/var/lib/ntp/drift' }
|
17
|
+
its('allow') { should cmp nil }
|
18
|
+
its('keyfile') { should cmp '/etc/chrony.keys' }
|
19
|
+
end
|
20
|
+
EXAMPLE
|
21
|
+
|
22
|
+
include FileReader
|
23
|
+
|
24
|
+
def initialize(path = nil)
|
25
|
+
@conf_path = path || "/etc/chrony.conf"
|
26
|
+
@content = read_file_content(@conf_path)
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(name)
|
30
|
+
param = read_params[name.to_s]
|
31
|
+
# extract first value if we have only one value in array
|
32
|
+
return param[0] if param.is_a?(Array) && (param.length == 1)
|
33
|
+
|
34
|
+
param
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_s
|
38
|
+
"chrony.conf"
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def read_params
|
44
|
+
return @params if defined?(@params)
|
45
|
+
|
46
|
+
# parse the file
|
47
|
+
conf = SimpleConfig.new(
|
48
|
+
@content,
|
49
|
+
assignment_regex: /^\s*(\S+)\s+(.*)\s*$/,
|
50
|
+
multiple_values: true
|
51
|
+
)
|
52
|
+
@params = conf.params
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/inspec/resources/csv.rb
CHANGED
@@ -11,14 +11,28 @@ module Inspec::Resources
|
|
11
11
|
describe csv('example.csv') do
|
12
12
|
its('name') { should eq(['John', 'Alice']) }
|
13
13
|
end
|
14
|
+
|
15
|
+
describe csv('example.csv', false).params do
|
16
|
+
its[[0]] { should eq (['name', 'col1', 'col2']) }
|
17
|
+
emd
|
14
18
|
EXAMPLE
|
15
19
|
|
20
|
+
def initialize(path, headers = true)
|
21
|
+
@headers = headers
|
22
|
+
super(path)
|
23
|
+
end
|
24
|
+
|
16
25
|
# override the parse method from JsonConfig
|
17
26
|
# Assuming a header row of name,col1,col2, it will output an array of hashes like so:
|
18
27
|
# [
|
19
28
|
# { 'name' => 'row1', 'col1' => 'value1', 'col2' => 'value2' },
|
20
29
|
# { 'name' => 'row2', 'col1' => 'value3', 'col2' => 'value4' }
|
21
30
|
# ]
|
31
|
+
# When headers is set to false it will return data as array of array
|
32
|
+
# [
|
33
|
+
# ['name', col1', 'col2'],
|
34
|
+
# ['row2', 'value3', 'value4']
|
35
|
+
# ]
|
22
36
|
def parse(content)
|
23
37
|
require "csv" unless defined?(CSV)
|
24
38
|
|
@@ -28,10 +42,14 @@ module Inspec::Resources
|
|
28
42
|
end
|
29
43
|
|
30
44
|
# implicit conversion of values
|
31
|
-
csv = CSV.new(content, headers:
|
45
|
+
csv = CSV.new(content, headers: @headers, converters: %i{all blank_to_nil})
|
32
46
|
|
33
47
|
# convert to hash
|
34
|
-
|
48
|
+
if @headers
|
49
|
+
csv.to_a.map(&:to_hash)
|
50
|
+
else
|
51
|
+
csv.to_a
|
52
|
+
end
|
35
53
|
rescue => e
|
36
54
|
raise Inspec::Exceptions::ResourceFailed, "Unable to parse CSV: #{e.message}"
|
37
55
|
end
|
@@ -42,7 +60,12 @@ module Inspec::Resources
|
|
42
60
|
# #value method from JsonConfig (which uses ObjectTraverser.extract_value)
|
43
61
|
# doesn't make sense here.
|
44
62
|
def value(key)
|
45
|
-
@
|
63
|
+
if @headers
|
64
|
+
@params.map { |x| x[key.first.to_s] }.compact
|
65
|
+
else
|
66
|
+
# when headers is set to false send the array as it is.
|
67
|
+
@params
|
68
|
+
end
|
46
69
|
end
|
47
70
|
|
48
71
|
private
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Inspec::Resources
|
2
|
+
class Ibmdb2Conf < Inspec.resource(1)
|
3
|
+
name "ibmdb2_conf"
|
4
|
+
|
5
|
+
supports platform: "unix"
|
6
|
+
supports platform: "windows"
|
7
|
+
|
8
|
+
desc "Use the ibmdb2_conf InSpec audit resource to test the configuration values of IBM Db2 database."
|
9
|
+
example <<~EXAMPLE
|
10
|
+
describe ibmdb2_conf(db2_executable_file_path: "path_to_db2_binary", db_instance: "db2inst1") do
|
11
|
+
its("output") { should_not be_empty }
|
12
|
+
its("output") { should include("Audit buffer size (4KB) (AUDIT_BUF_SZ) = 0")}
|
13
|
+
end
|
14
|
+
EXAMPLE
|
15
|
+
|
16
|
+
attr_reader :output
|
17
|
+
|
18
|
+
def initialize(opts = {})
|
19
|
+
if inspec.os.platform?("unix")
|
20
|
+
@db2_executable_file_path = opts[:db2_executable_file_path]
|
21
|
+
@db_instance = opts[:db_instance]
|
22
|
+
raise Inspec::Exceptions::ResourceFailed, "Can't connect to IBM DB2 without db2_executable_file_path, db_instance options provided." if @db2_executable_file_path.nil? || @db_instance.nil?
|
23
|
+
end
|
24
|
+
@output = run_command
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
"IBM Db2 Conf"
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def run_command
|
34
|
+
# attach to the db2 instance and get the configuration
|
35
|
+
if inspec.os.platform?("unix")
|
36
|
+
cmd = inspec.command("#{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} get database manager configuration")
|
37
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
38
|
+
|
39
|
+
# check if following specific error is there. Sourcing the db2profile to resolve the error.
|
40
|
+
if cmd.exit_status != 0 && out =~ /SQL10007N Message "-1390" could not be retrieved. Reason code: "3"/
|
41
|
+
cmd = inspec.command(". ~/sqllib/db2profile\; #{@db2_executable_file_path} attach to #{@db_instance}\; #{@db2_executable_file_path} get database manager configuration")
|
42
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
43
|
+
end
|
44
|
+
elsif inspec.os.platform?("windows")
|
45
|
+
# set-item command set the powershell to run the db2 commands.
|
46
|
+
cmd = inspec.command("set-item -path env:DB2CLP -value \"**$$**\"\; db2 get database manager configuration")
|
47
|
+
out = cmd.stdout + "\n" + cmd.stderr
|
48
|
+
end
|
49
|
+
|
50
|
+
if cmd.exit_status != 0 || out =~ /Can't connect to IBM Db2 server/ || out.downcase =~ /^error:.*/
|
51
|
+
raise Inspec::Exceptions::ResourceFailed, "IBM Db2 query with error: #{out}"
|
52
|
+
else
|
53
|
+
cmd.stdout.gsub(/\n|\r/, ",").split(",").reject { |n| n.nil? || n.empty? }.map { |n| n.strip.gsub!(/\s+/, " ") }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|