class Inspec::BaseCLI

The InSpec load order has this file being loaded before `inspec/base_cli` can finish being loaded. So, we must define Inspec::BaseCLI here first to avoid a NameError below.

Constants

ALL_OF_OUR_REPORTERS

Attributes

inspec_cli_command[RW]

Public Class Methods

check_license!() click to toggle source

EULA acceptance

# File lib/inspec/base_cli.rb, line 39
def self.check_license!
  allowed_commands = ["-h", "--help", "help", "-v", "--version", "version"]

  require "license_acceptance/acceptor"
  begin
    if (allowed_commands & ARGV.map(&:downcase)).empty? && # Did they use a non-exempt command?
        !ARGV.empty? # Did they supply at least one command?
      license_acceptor_output = LicenseAcceptance::Acceptor.check_and_persist(
        Inspec::Dist::EXEC_NAME,
        Inspec::VERSION,
        logger: Inspec::Log
      )
      if license_acceptor_output && ARGV.count == 1 && (ARGV.first.include? "--chef-license")
        Inspec::UI.new.exit
      end
      license_acceptor_output
    end
  rescue LicenseAcceptance::LicenseNotAcceptedError
    Inspec::Log.error "#{Inspec::Dist::PRODUCT_NAME} cannot execute without accepting the license"
    Inspec::UI.new.exit(:license_not_accepted)
  end
end
exec_options() click to toggle source
# File lib/inspec/base_cli.rb, line 138
def self.exec_options
  target_options
  profile_options
  option :controls, type: :array,
    desc: "A list of control names to run, or a list of /regexes/ to match against control names. Ignore all other tests."
  option :tags, type: :array,
    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."
  option :reporter, type: :array,
    banner: "one two:/output/file/path",
    desc: "Enable one or more output reporters: cli, documentation, html, progress, json, json-min, json-rspec, junit, yaml"
  option :reporter_message_truncation, type: :string,
    desc: "Number of characters to truncate failure messages and code_desc in report data to (default: no truncation)"
  option :reporter_backtrace_inclusion, type: :boolean,
    desc: "Include a code backtrace in report data (default: true)"
  option :input, type: :array, banner: "name1=value1 name2=value2",
    desc: "Specify one or more inputs directly on the command line, as --input NAME=VALUE. Accepts single-quoted YAML and JSON structures."
  option :input_file, type: :array,
    desc: "Load one or more input files, a YAML file with values for the profile to use"
  option :waiver_file, type: :array,
    desc: "Load one or more waiver files."
  option :attrs, type: :array,
    desc: "Legacy name for --input-file - deprecated."
  option :create_lockfile, type: :boolean,
    desc: "Write out a lockfile based on this execution (unless one already exists)"
  option :backend_cache, type: :boolean,
    desc: "Allow caching for backend command output. (default: true)"
  option :show_progress, type: :boolean,
    desc: "Show progress while executing tests."
  option :distinct_exit, type: :boolean, default: true,
    desc: "Exit with code 101 if any tests fail, and 100 if any are skipped (default).  If disabled, exit 0 on skips and 1 for failures."
  option :silence_deprecations, type: :array,
    banner: "[all]|[GROUP GROUP...]",
    desc: "Suppress deprecation warnings. See install_dir/etc/deprecations.json for list of GROUPs or use 'all'."
  option :diff, type: :boolean, default: true,
    desc: "Use --no-diff to suppress 'diff' output of failed textual test results."
  option :sort_results_by, type: :string, default: "file", banner: "--sort-results-by=none|control|file|random",
    desc: "After normal execution order, results are sorted by control ID, or by file (default), or randomly. None uses legacy unsorted mode."
  option :filter_empty_profiles, type: :boolean, default: false,
    desc: "Filter empty profiles (profiles without controls) from the report."
  option :command_timeout, type: :numeric,
    desc: "Maximum seconds to allow commands to run during execution.",
    long_desc: "Maximum seconds to allow commands to run during execution. A timed out command is considered an error."
  option :reporter_include_source, type: :boolean, default: false,
    desc: "Include full source code of controls in the CLI report"
end
exit_on_failure?() click to toggle source

github.com/erikhuda/thor/issues/244

# File lib/inspec/base_cli.rb, line 63
def self.exit_on_failure?
  true
end
format_platform_info(params: {}, indent: 0, color: 39, enable_color: true) click to toggle source
# File lib/inspec/base_cli.rb, line 190
def self.format_platform_info(params: {}, indent: 0, color: 39, enable_color: true)
  str = ""
  params.each do |item, info|
    data = info

    # Format Array for better output if applicable
    data = data.join(", ") if data.is_a?(Array)

    # Do not output fields of data is missing ('unknown' is fine)
    next if data.nil?

    data = "\e[1m\e[#{color}m#{data}\e[0m" if enable_color
    str << format("#{" " * indent}%-10s %s\n", item.to_s.capitalize + ":", data)
  end
  str
end
help(*args) click to toggle source
Calls superclass method
# File lib/inspec/base_cli.rb, line 184
def self.help(*args)
  super(*args)
  puts "\nAbout #{Inspec::Dist::PRODUCT_NAME}:"
  puts "  Patents: chef.io/patents\n\n"
end
profile_options() click to toggle source
# File lib/inspec/base_cli.rb, line 131
def self.profile_options
  option :profiles_path, type: :string,
    desc: "Folder which contains referenced profiles."
  option :vendor_cache, type: :string,
    desc: "Use the given path for caching dependencies. (default: ~/.inspec/cache)"
end
start(given_args = ARGV, config = {}) click to toggle source
Calls superclass method
# File lib/inspec/base_cli.rb, line 32
def self.start(given_args = ARGV, config = {})
  check_license! if config[:enforce_license] || config[:enforce_license].nil?

  super(given_args, config)
end
target_options() click to toggle source
# File lib/inspec/base_cli.rb, line 67
def self.target_options # rubocop:disable Metrics/MethodLength
  option :target, aliases: :t, type: :string,
    desc: "Simple targeting option using URIs, e.g. ssh://user:pass@host:port"
  option :backend, aliases: :b, type: :string,
    desc: "Choose a backend: local, ssh, winrm, docker."
  option :host, type: :string,
    desc: "Specify a remote host which is tested."
  option :port, aliases: :p, type: :numeric,
    desc: "Specify the login port for a remote scan."
  option :user, type: :string,
    desc: "The login user for a remote scan."
  option :password, type: :string, lazy_default: -1,
    desc: "Login password for a remote scan, if required."
  option :enable_password, type: :string, lazy_default: -1,
    desc: "Password for enable mode on Cisco IOS devices."
  option :key_files, aliases: :i, type: :array,
    desc: "Login key or certificate file for a remote scan."
  option :path, type: :string,
    desc: "Login path to use when connecting to the target (WinRM)."
  option :sudo, type: :boolean,
    desc: "Run scans with sudo. Only activates on Unix and non-root user."
  option :sudo_password, type: :string, lazy_default: -1,
    desc: "Specify a sudo password, if it is required."
  option :sudo_options, type: :string,
    desc: "Additional sudo options for a remote scan."
  option :sudo_command, type: :string,
    desc: "Alternate command for sudo."
  option :shell, type: :boolean,
    desc: "Run scans in a subshell. Only activates on Unix."
  option :shell_options, type: :string,
    desc: "Additional shell options."
  option :shell_command, type: :string,
    desc: "Specify a particular shell to use."
  option :ssl, type: :boolean,
    desc: "Use SSL for transport layer encryption (WinRM)."
  option :self_signed, type: :boolean,
    desc: "Allow remote scans with self-signed certificates (WinRM)."
  option :winrm_transport, type: :string, default: "negotiate",
    desc: "Specify which transport to use, defaults to negotiate (WinRM)."
  option :winrm_disable_sspi, type: :boolean,
    desc: "Whether to use disable sspi authentication, defaults to false (WinRM)."
  option :winrm_basic_auth_only, type: :boolean,
    desc: "Whether to use basic authentication, defaults to false (WinRM)."
  option :config, type: :string,
    desc: "Read configuration from JSON file (`-` reads from stdin)."
  option :json_config, type: :string, hide: true
  option :proxy_command, type: :string,
    desc: "Specifies the command to use to connect to the server"
  option :bastion_host, type: :string,
    desc: "Specifies the bastion host if applicable"
  option :bastion_user, type: :string,
    desc: "Specifies the bastion user if applicable"
  option :bastion_port, type: :string,
    desc: "Specifies the bastion port if applicable"
  option :insecure, type: :boolean, default: false,
    desc: "Disable SSL verification on select targets"
  option :target_id, type: :string,
    desc: "Provide a ID which will be included on reports"
  option :winrm_shell_type, type: :string, default: "powershell",
    desc: "Specify a shell type for winrm (eg. 'elevated' or 'powershell')"
  option :docker_url, type: :string,
    desc: "Provides path to Docker API endpoint (Docker)"
end

Public Instance Methods

exit(code) click to toggle source
# File lib/inspec/base_cli.rb, line 253
def exit(code)
  Inspec.deprecate(:inspec_ui_methods)
  ui.exit code
end
headline(title) click to toggle source
# File lib/inspec/base_cli.rb, line 238
def headline(title)
  Inspec.deprecate(:inspec_ui_methods)
  ui.headline(title)
end
li(entry) click to toggle source
# File lib/inspec/base_cli.rb, line 243
def li(entry)
  Inspec.deprecate(:inspec_ui_methods)
  ui.list_item(entry)
end
mark_text(text) click to toggle source
# File lib/inspec/base_cli.rb, line 232
def mark_text(text)
  Inspec.deprecate(:inspec_ui_methods)
  # Note that this one doesn't automatically print
  ui.emphasis(text, print: false)
end
plain_text(msg) click to toggle source
# File lib/inspec/base_cli.rb, line 248
def plain_text(msg)
  Inspec.deprecate(:inspec_ui_methods)
  ui.plain(msg + "\n")
end
ui() click to toggle source
# File lib/inspec/base_cli.rb, line 211
def ui
  return @ui if defined?(@ui)

  # Make a new UI object, respecting context
  if options[:color].nil?
    enable_color = true # If the command does not support the color option, default to on
  else
    enable_color = options[:color]
  end

  # UI will probe for TTY if nil - just send the raw option value
  enable_interactivity = options[:interactive]

  @ui = Inspec::UI.new(color: enable_color, interactive: enable_interactivity)
end
ui=(new_ui) click to toggle source

Rationale: converting this to attr_writer breaks Thor

# File lib/inspec/base_cli.rb, line 228
def ui=(new_ui) # rubocop: disable Style/TrivialAccessors
  @ui = new_ui
end

Private Instance Methods

config() click to toggle source
# File lib/inspec/base_cli.rb, line 282
def config
  @config ||= Inspec::Config.new(options) # 'options' here is CLI opts from Thor
end
configure_logger(o) click to toggle source
# File lib/inspec/base_cli.rb, line 327
def configure_logger(o)
  #
  # TODO(ssd): This is a bit gross, but this configures the
  # logging singleton Inspec::Log. Eventually it would be nice to
  # move internal debug logging to use this logging singleton.
  #
  loc = if o["log_location"]
          o["log_location"]
        elsif suppress_log_output?(o)
          $stderr
        else
          $stdout
        end

  Inspec::Log.init(loc)
  Inspec::Log.level = get_log_level(o["log_level"])

  o[:logger] = Logger.new(loc)
  # output json if we have activated the json formatter
  if o["log-format"] == "json"
    o[:logger].formatter = Logger::JSONFormatter.new
  end
  o[:logger].level = get_log_level(o["log_level"])
end
diagnose(_ = nil) click to toggle source
# File lib/inspec/base_cli.rb, line 278
def diagnose(_ = nil)
  config.diagnose
end
get_log_level(level) click to toggle source

get the log level DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN

# File lib/inspec/base_cli.rb, line 288
def get_log_level(level)
  valid = %w{debug info warn error fatal}

  if valid.include?(level)
    l = level
  else
    l = "info"
  end

  Logger.const_get(l.upcase)
end
pretty_handle_exception(exception) click to toggle source
# File lib/inspec/base_cli.rb, line 300
def pretty_handle_exception(exception)
  case exception
  when Inspec::Error
    $stderr.puts exception.message
    exit(1)
  else
    raise exception
  end
end
suppress_log_output?(opts) click to toggle source
# File lib/inspec/base_cli.rb, line 263
def suppress_log_output?(opts)
  return false if opts["reporter"].nil?

  match = ALL_OF_OUR_REPORTERS & opts["reporter"].keys

  unless match.empty?
    match.each do |m|
      # check to see if we are outputting to stdout
      return true if opts["reporter"][m]["stdout"] == true
    end
  end

  false
end
vendor_deps(path, opts) click to toggle source
# File lib/inspec/base_cli.rb, line 310
def vendor_deps(path, opts)
  require "inspec/profile_vendor"

  profile_path = path || Dir.pwd
  profile_vendor = Inspec::ProfileVendor.new(profile_path)

  if (profile_vendor.cache_path.exist? || profile_vendor.lockfile.exist?) && !opts[:overwrite]
    puts "Profile is already vendored. Use --overwrite."
    return false
  end

  profile_vendor.vendor!(opts)
  puts "Dependencies for profile #{profile_path} successfully vendored to #{profile_vendor.cache_path}"
rescue StandardError => e
  pretty_handle_exception(e)
end