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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -12
  3. data/etc/deprecations.json +1 -1
  4. data/lib/inspec/base_cli.rb +7 -1
  5. data/lib/inspec/cached_fetcher.rb +2 -2
  6. data/lib/inspec/cli.rb +4 -1
  7. data/lib/inspec/control_eval_context.rb +64 -17
  8. data/lib/inspec/dsl.rb +18 -3
  9. data/lib/inspec/fetcher/url.rb +45 -3
  10. data/lib/inspec/fetcher.rb +3 -3
  11. data/lib/inspec/plugin/v1/registry.rb +6 -2
  12. data/lib/inspec/profile.rb +33 -4
  13. data/lib/inspec/resources/apache_conf.rb +8 -6
  14. data/lib/inspec/resources/chrony_conf.rb +55 -0
  15. data/lib/inspec/resources/csv.rb +26 -3
  16. data/lib/inspec/resources/ibmdb2_conf.rb +57 -0
  17. data/lib/inspec/resources/ibmdb2_session.rb +69 -0
  18. data/lib/inspec/resources/mongodb_session.rb +88 -0
  19. data/lib/inspec/resources/mssql_session.rb +1 -5
  20. data/lib/inspec/resources/mssql_sys_conf.rb +48 -0
  21. data/lib/inspec/resources/opa.rb +26 -0
  22. data/lib/inspec/resources/opa_api.rb +39 -0
  23. data/lib/inspec/resources/opa_cli.rb +43 -0
  24. data/lib/inspec/resources/oracle.rb +66 -0
  25. data/lib/inspec/resources/oracledb_conf.rb +40 -0
  26. data/lib/inspec/resources/oracledb_listener_conf.rb +123 -0
  27. data/lib/inspec/resources/oracledb_session.rb +16 -6
  28. data/lib/inspec/resources/postgres.rb +45 -12
  29. data/lib/inspec/resources/postgres_conf.rb +2 -0
  30. data/lib/inspec/resources/postgres_hba_conf.rb +2 -1
  31. data/lib/inspec/resources/postgres_ident_conf.rb +2 -1
  32. data/lib/inspec/resources/postgres_session.rb +20 -11
  33. data/lib/inspec/resources/registry_key.rb +1 -1
  34. data/lib/inspec/resources/security_identifier.rb +8 -14
  35. data/lib/inspec/resources/security_policy.rb +4 -3
  36. data/lib/inspec/resources/service.rb +7 -1
  37. data/lib/inspec/resources/sybase_conf.rb +37 -0
  38. data/lib/inspec/resources/sybase_session.rb +111 -0
  39. data/lib/inspec/resources/wmi.rb +1 -1
  40. data/lib/inspec/resources.rb +9 -0
  41. data/lib/inspec/rule.rb +1 -1
  42. data/lib/inspec/run_data/profile.rb +0 -2
  43. data/lib/inspec/runner.rb +2 -0
  44. data/lib/inspec/utils/filter.rb +1 -1
  45. data/lib/inspec/version.rb +1 -1
  46. data/lib/plugins/inspec-init/templates/profiles/aws/inspec.yml +1 -1
  47. data/lib/plugins/inspec-init/templates/profiles/azure/inspec.yml +1 -1
  48. data/lib/plugins/inspec-init/templates/profiles/gcp/inspec.yml +1 -1
  49. data/lib/plugins/inspec-plugin-manager-cli/lib/inspec-plugin-manager-cli/cli_command.rb +16 -15
  50. metadata +15 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da7b208efcb020501f9a283e5e512d3862cd4c3e7c19f2012e143af2721fd893
4
- data.tar.gz: f534843ff5445086f3d40802fff3fe19c8ed787660dde2ce17816e8dc1225cac
3
+ metadata.gz: 1cc013914b6503c547eeb5fbba13ce8398cf039236b82a0422339a8b7d178649
4
+ data.tar.gz: bb763eb39cb82fa264417d15147769d9d4b3fe1dff34ac40351712a35c6dbcb3
5
5
  SHA512:
6
- metadata.gz: efd72de313d408802e8484dba99e6291bcd1a571fe028642736fccfcb3d02b78bb700d88a0441d6b2525285891707a738b8315c8dc315edc50e288eb000940e3
7
- data.tar.gz: 12d51bdaea741376370ea5e64abf645d9734a2edf4328118b8931c11d6032446830f0f7f495eed987e7fcc27518472e21f523d93bd1c1578468b97d9028d11c3
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
- gem "html-proofer", platforms: :ruby # do not attempt to run proofer on windows
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"
@@ -4,7 +4,7 @@
4
4
  "groups": {
5
5
  "attrs_value_replaces_default": {
6
6
  "action": "warn",
7
- "prefix": "The 'default' option for attributes is being replaced by 'value' - please use it instead."
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",
@@ -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 control_exist_in_controls_list?(id) || controls_list_empty?
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) || controls_list_empty?
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
- # Returns true if configuration hash is empty or configuration hash does not have the list of controls that needs to be included
191
- def controls_list_empty?
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
- filter_included_controls(context, dep_entry.profile, &block) unless opts[:include_all]
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
- unless include_ctx.rules[id] || include_ctx.rules[fid]
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
@@ -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
- "https://github.com/#{m[:user]}/#{m[:repo]}/archive/master.tar.gz"
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
- "https://bitbucket.org/#{m[:user]}/#{m[:repo]}/get/master.tar.gz"
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
 
@@ -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.resolve(target)
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
@@ -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
- fetcher = resolve_target(target, opts[:vendor_cache])
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(include_list = @controls)
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
- (include_files + include_files_optional).each do |f|
105
- id = Pathname.new(f).absolute? ? f : File.join(conf_dir, f)
106
- files = find_files(id, depth: 1, type: "file")
107
- files += find_files(id, depth: 1, type: "link")
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
- includes.push(files) if files
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
@@ -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: true, converters: %i{all blank_to_nil})
45
+ csv = CSV.new(content, headers: @headers, converters: %i{all blank_to_nil})
32
46
 
33
47
  # convert to hash
34
- csv.to_a.map(&:to_hash)
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
- @params.map { |x| x[key.first.to_s] }.compact
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