lib/inspec/config.rb in inspec-4.3.2 vs lib/inspec/config.rb in inspec-4.6.3

- old
+ new

@@ -1,10 +1,14 @@ # Represents InSpec configuration. Merges defaults, config file options, # and CLI arguments. -require 'pp' -require 'stringio' +require "pp" +require "stringio" +require "forwardable" +require "thor" +require "base64" +require "inspec/base_cli" module Inspec class Config # These are options that apply to any transport GENERIC_CREDENTIALS = %w{ @@ -25,11 +29,11 @@ def_delegators :@final_options, :each, :delete, :[], :[]=, :key? attr_reader :final_options # This makes it easy to make a config with a mock backend. def self.mock(opts = {}) - Inspec::Config.new({ backend: :mock }.merge(opts), StringIO.new('{}')) + Inspec::Config.new({ backend: :mock }.merge(opts), StringIO.new("{}")) end # Use this to get a cached version of the config. This prevents you from # being required to pass it around everywhere. def self.cached @@ -56,19 +60,25 @@ def diagnose return unless self[:diagnose] puts "InSpec version: #{Inspec::VERSION}" puts "Train version: #{Train::VERSION}" - puts 'Command line configuration:' + puts "Command line configuration:" pp @cli_opts - puts 'JSON configuration file:' + puts "JSON configuration file:" pp @cfg_file_contents - puts 'Merged configuration:' + puts "Merged configuration:" pp @merged_options puts end + # return all telemetry options from config + # @return [Hash] + def telemetry_options + final_options.select { |key, _| key.include?("telemetry") } + end + #-----------------------------------------------------------------------# # Train Credential Handling #-----------------------------------------------------------------------# # Returns a Hash with Symbol keys as follows: @@ -119,11 +129,11 @@ transport_options.include? option_name # e.g., 'host' end credentials.merge!(unprefixed_transport_options) # If there are any prefixed options, merge them in, stripping the prefix. - transport_prefix = transport_name.downcase.tr('-', '_') + '_' + transport_prefix = transport_name.downcase.tr("-", "_") + "_" transport_options.each do |bare_option_name| prefixed_option_name = transport_prefix + bare_option_name.to_s if final_options.key?(prefixed_option_name) credentials[bare_option_name.to_s] = final_options[prefixed_option_name] end @@ -138,11 +148,11 @@ def _utc_determine_backend(credentials) return if credentials.key?(:backend) # Default to local unless @final_options.key?(:target) - credentials[:backend] = 'local' + credentials[:backend] = "local" return end # Look into target %r{^(?<transport_name>[a-z_\-0-9]+)://.*$} =~ final_options[:target] @@ -155,11 +165,11 @@ def _utc_merge_credset(credentials, transport_name) # Look for Config File credentials/transport_name/credset credset_name = _utc_find_credset_name(credentials, transport_name) if credset_name - credset = @cfg_file_contents.dig('credentials', transport_name, credset_name) + credset = @cfg_file_contents.dig("credentials", transport_name, credset_name) if credset credentials.merge!(credset) else # OK, we had a target that looked like transport://something # But we don't know what that something is - there was no @@ -183,11 +193,11 @@ # Reading Config Files #-----------------------------------------------------------------------# # Regardless of our situation, end up with a readable IO object def resolve_cfg_io(cli_opts, cfg_io) - raise(ArgumentError, 'Inspec::Config must use an IO to read from') if cfg_io && !cfg_io.respond_to?(:read) + raise(ArgumentError, "Inspec::Config must use an IO to read from") if cfg_io && !cfg_io.respond_to?(:read) cfg_io ||= check_for_piped_config(cli_opts) return cfg_io if cfg_io path = determine_cfg_path(cli_opts) @@ -198,23 +208,23 @@ def check_for_piped_config(cli_opts) cli_opt = cli_opts[:config] || cli_opts[:json_config] Inspec.deprecate(:cli_option_json_config) if cli_opts.key?(:json_config) return nil unless cli_opt - return nil unless cli_opt == '-' + return nil unless cli_opt == "-" # This warning is here so that if a user invokes inspec with --config=-, # they will have an explanation for why it appears to hang. - Inspec::Log.warn 'Reading JSON config from standard input' if STDIN.tty? + Inspec::Log.warn "Reading JSON config from standard input" if STDIN.tty? STDIN end def determine_cfg_path(cli_opts) path = cli_opts[:config] || cli_opts[:json_config] Inspec.deprecate(:cli_option_json_config) if cli_opts.key?(:json_config) if path.nil? - default_path = File.join(Inspec.config_dir, 'config.json') + default_path = File.join(Inspec.config_dir, "config.json") path = default_path if File.exist?(default_path) elsif !File.exist?(path) raise ArgumentError, "Could not read configuration file at #{path}" end path @@ -237,11 +247,11 @@ end @cfg_file_contents end def file_version - @cfg_file_contents['version'] || :legacy + @cfg_file_contents["version"] || :legacy end def legacy_file? file_version == :legacy end @@ -249,30 +259,30 @@ def config_file_cli_options if legacy_file? # Assume everything in the file is a CLI option @cfg_file_contents else - @cfg_file_contents['cli_options'] || {} + @cfg_file_contents["cli_options"] || {} end end def config_file_reporter_options # This is assumed to be top-level in both legacy and 1.1. # Technically, you could sneak it in the 1.1 cli opts area. - @cfg_file_contents.key?('reporter') ? { 'reporter' => @cfg_file_contents['reporter'] } : {} + @cfg_file_contents.key?("reporter") ? { "reporter" => @cfg_file_contents["reporter"] } : {} end #-----------------------------------------------------------------------# # Validation #-----------------------------------------------------------------------# def validate_config_file_contents! - version = @cfg_file_contents['version'] + version = @cfg_file_contents["version"] # Assume legacy format, which is unconstrained return unless version - unless version == '1.1' + unless version == "1.1" raise Inspec::ConfigError::Invalid, "Unsupported config file version '#{version}' - currently supported versions: 1.1" end valid_fields = %w{version cli_options credentials compliance reporter}.sort @cfg_file_contents.keys.each do |seen_field| @@ -284,39 +294,39 @@ def validate_reporters!(reporters) return if reporters.nil? # TODO: move this into a reporter plugin type system valid_types = [ - 'automate', - 'cli', - 'documentation', - 'html', - 'json', - 'json-automate', - 'json-min', - 'json-rspec', - 'junit', - 'progress', - 'yaml', + "automate", + "cli", + "documentation", + "html", + "json", + "json-automate", + "json-min", + "json-rspec", + "junit", + "progress", + "yaml", ] reporters.each do |reporter_name, reporter_config| raise NotImplementedError, "'#{reporter_name}' is not a valid reporter type." unless valid_types.include?(reporter_name) - next unless reporter_name == 'automate' + next unless reporter_name == "automate" %w{token url}.each do |option| raise Inspec::ReporterError, "You must specify a automate #{option} via the config file." if reporter_config[option].nil? end end # check to make sure we are only reporting one type to stdout stdout_reporters = 0 reporters.each_value do |reporter_config| - stdout_reporters += 1 if reporter_config['stdout'] == true + stdout_reporters += 1 if reporter_config["stdout"] == true end - raise ArgumentError, 'The option --reporter can only have a single report outputting to stdout.' if stdout_reporters > 1 + raise ArgumentError, "The option --reporter can only have a single report outputting to stdout." if stdout_reporters > 1 end #-----------------------------------------------------------------------# # Merging Options #-----------------------------------------------------------------------# @@ -356,81 +366,81 @@ Inspec::BaseCLI.inspec_cli_command = @command_name # TODO: move to a more relevant location end def finalize_parse_reporters(options) # rubocop:disable Metrics/AbcSize # default to cli report for ad-hoc runners - options['reporter'] = ['cli'] if options['reporter'].nil? + options["reporter"] = ["cli"] if options["reporter"].nil? # parse out cli to proper report format - if options['reporter'].is_a?(Array) + if options["reporter"].is_a?(Array) reports = {} - options['reporter'].each do |report| - reporter_name, destination = report.split(':', 2) - if destination.nil? || destination.strip == '-' - reports[reporter_name] = { 'stdout' => true } + options["reporter"].each do |report| + reporter_name, destination = report.split(":", 2) + if destination.nil? || destination.strip == "-" + reports[reporter_name] = { "stdout" => true } else reports[reporter_name] = { - 'file' => destination, - 'stdout' => false, + "file" => destination, + "stdout" => false, } - reports[reporter_name]['target_id'] = options['target_id'] if options['target_id'] + reports[reporter_name]["target_id"] = options["target_id"] if options["target_id"] end end - options['reporter'] = reports + options["reporter"] = reports end # add in stdout if not specified - if options['reporter'].is_a?(Hash) - options['reporter'].each do |reporter_name, config| - options['reporter'][reporter_name] = {} if config.nil? - options['reporter'][reporter_name]['stdout'] = true if options['reporter'][reporter_name].empty? - options['reporter'][reporter_name]['target_id'] = options['target_id'] if options['target_id'] + if options["reporter"].is_a?(Hash) + options["reporter"].each do |reporter_name, config| + options["reporter"][reporter_name] = {} if config.nil? + options["reporter"][reporter_name]["stdout"] = true if options["reporter"][reporter_name].empty? + options["reporter"][reporter_name]["target_id"] = options["target_id"] if options["target_id"] end end - validate_reporters!(options['reporter']) + validate_reporters!(options["reporter"]) options end def finalize_handle_sudo(options) # Due to limitations in Thor it is not possible to set an argument to be # both optional and its value to be mandatory. E.g. the user supplying # the --password argument is optional and not always required, but # whenever it is used, it requires a value. Handle options that were # defined in such a way and require a value here: %w{password sudo-password}.each do |option_name| - snake_case_option_name = option_name.tr('-', '_').to_s + snake_case_option_name = option_name.tr("-", "_").to_s next unless options[snake_case_option_name] == -1 # Thor sets -1 for missing value - see #1918 raise ArgumentError, "Please provide a value for --#{option_name}. For example: --#{option_name}=hello." end # Infer `--sudo` if using `--sudo-password` without `--sudo` - if options['sudo_password'] && !options['sudo'] - options['sudo'] = true - Inspec::Log.warn '`--sudo-password` used without `--sudo`. Adding `--sudo`.' + if options["sudo_password"] && !options["sudo"] + options["sudo"] = true + Inspec::Log.warn "`--sudo-password` used without `--sudo`. Adding `--sudo`." end end def finalize_compliance_login(options) # check for compliance settings # This is always a hash, comes from config file, not CLI opts - if options.key?('compliance') - require 'plugins/inspec-compliance/lib/inspec-compliance/api' - InspecPlugins::Compliance::API.login(options['compliance']) + if options.key?("compliance") + require "plugins/inspec-compliance/lib/inspec-compliance/api" + InspecPlugins::Compliance::API.login(options["compliance"]) end end class Defaults DEFAULTS = { exec: { - 'reporter' => ['cli'], - 'show_progress' => false, - 'color' => true, - 'create_lockfile' => true, - 'backend_cache' => true, + "reporter" => ["cli"], + "show_progress" => false, + "color" => true, + "create_lockfile" => true, + "backend_cache" => true, }, shell: { - 'reporter' => ['cli'], + "reporter" => ["cli"], }, }.freeze def self.for_command(command_name) DEFAULTS[command_name] || {}