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

- old
+ new

@@ -1,27 +1,19 @@ -# encoding: utf-8 # Copyright 2015 Dominik Richter -# author: Dominik Richter -# author: Christoph Hartmann -require 'forwardable' -require 'openssl' -require 'inspec/input_registry' -require 'inspec/polyfill' -require 'inspec/cached_fetcher' -require 'inspec/file_provider' -require 'inspec/source_reader' -require 'inspec/metadata' -require 'inspec/backend' -require 'inspec/rule' -require 'inspec/log' -require 'inspec/profile_context' -require 'inspec/runtime_profile' -require 'inspec/method_source' -require 'inspec/dependencies/cache' -require 'inspec/dependencies/lockfile' -require 'inspec/dependencies/dependency_set' +require "forwardable" +require "openssl" +require "pathname" +require "inspec/input_registry" +require "inspec/cached_fetcher" # TODO: split or rename +require "inspec/source_reader" +require "inspec/profile_context" +require "inspec/runtime_profile" +require "inspec/method_source" +require "inspec/dependencies/cache" +require "inspec/dependencies/lockfile" +require "inspec/dependencies/dependency_set" module Inspec class Profile extend Forwardable @@ -34,18 +26,18 @@ # TODO: use relative file provider # TODO: use source reader for Cache as well def self.copy_deps_into_cache(file_provider, opts) # filter content cache = file_provider.files.find_all do |entry| - entry.start_with?('vendor') + entry.start_with?("vendor") end content = Hash[cache.map { |x| [x, file_provider.binread(x)] }] keys = content.keys keys.each do |key| next if content[key].nil? # remove prefix - rel = Pathname.new(key).relative_path_from(Pathname.new('vendor')).to_s + rel = Pathname.new(key).relative_path_from(Pathname.new("vendor")).to_s tar = Pathname.new(opts[:vendor_cache].path).join(rel) FileUtils.mkdir_p tar.dirname.to_s Inspec::Log.debug "Copy #{tar} to cache directory" File.binwrite(tar.to_s, content[key]) @@ -111,22 +103,22 @@ # This is necessary since we store the RuntimeProfile on the backend object. If a user runs `inspec exec` # with multiple profiles, only the RuntimeProfile for the last-loaded profile will be available if # we share the backend between profiles. # # This will cause issues if a profile attempts to load a file via `inspec.profile.file` - train_options = options.reject { |k, _| k == 'target' } # See https://github.com/chef/inspec/pull/1646 + train_options = options.reject { |k, _| k == "target" } # See https://github.com/chef/inspec/pull/1646 @backend = options[:backend].nil? ? Inspec::Backend.create(Inspec::Config.new(train_options)) : options[:backend].dup @runtime_profile = RuntimeProfile.new(self) @backend.profile = @runtime_profile # The AttributeRegistry is in charge of keeping track of inputs; # it is the single source of truth. Now that we have a profile object, # we can create any inputs that were provided by various mechanisms. options[:runner_conf] ||= Inspec::Config.cached if options[:runner_conf].key?(:attrs) - Inspec.deprecate(:rename_attributes_to_inputs, 'Use --input-file on the command line instead of --attrs.') + Inspec.deprecate(:rename_attributes_to_inputs, "Use --input-file on the command line instead of --attrs.") options[:runner_conf][:input_file] = options[:runner_conf].delete(:attrs) end Inspec::InputRegistry.bind_profile_inputs( # Every input only exists in the context of a profile @@ -172,11 +164,11 @@ # @returns [TrueClass, FalseClass] def supports_platform? if @supports_platform.nil? @supports_platform = metadata.supports_platform?(@backend) end - if @backend.backend.class.to_s == 'Train::Transports::Mock::Connection' + if @backend.backend.class.to_s == "Train::Transports::Mock::Connection" @supports_platform = true end @supports_platform end @@ -212,11 +204,11 @@ # Check for anything that might be a regex in the list, and make it official include_list.each_with_index do |inclusion, index| next if inclusion.is_a?(Regexp) # Insist the user wrap the regex in slashes to demarcate it as a regex - next unless inclusion.start_with?('/') && inclusion.end_with?('/') + next unless inclusion.start_with?("/") && inclusion.end_with?("/") inclusion = inclusion[1..-2] # Trim slashes begin re = Regexp.new(inclusion) include_list[index] = re rescue RegexpError => e @@ -242,18 +234,18 @@ d = dep.profile # this will force a dependent profile load so we are only going to add # this metadata if the parent profile is supported. if supports_platform? && !d.supports_platform? # since ruby 1.9 hashes are ordered so we can just use index values here - metadata.dependencies[i][:status] = 'skipped' + metadata.dependencies[i][:status] = "skipped" msg = "Skipping profile: '#{d.name}' on unsupported platform: '#{d.backend.platform.name}/#{d.backend.platform.release}'." metadata.dependencies[i][:skip_message] = msg next elsif metadata.dependencies[i] # Currently wrapper profiles will load all dependencies, and then we # load them again when we dive down. This needs to be re-done. - metadata.dependencies[i][:status] = 'loaded' + metadata.dependencies[i][:status] = "loaded" end c = d.load_libraries @runner_context.add_resources(c) end @@ -313,15 +305,15 @@ end res[:sha256] = sha256 res[:parent_profile] = parent_profile unless parent_profile.nil? if !supports_platform? - res[:status] = 'skipped' + res[:status] = "skipped" msg = "Skipping profile: '#{name}' on unsupported platform: '#{backend.platform.name}/#{backend.platform.release}'." res[:skip_message] = msg else - res[:status] = 'loaded' + res[:status] = "loaded" end # convert legacy os-* supports to their platform counterpart if res[:supports] && !res[:supports].empty? res[:supports].each do |support| @@ -378,66 +370,66 @@ m_errors, m_warnings = metadata.valid m_errors.each { |msg| error.call(meta_path, 0, 0, nil, msg) } m_warnings.each { |msg| warn.call(meta_path, 0, 0, nil, msg) } m_unsupported = metadata.unsupported m_unsupported.each { |u| warn.call(meta_path, 0, 0, nil, "doesn't support: #{u}") } - @logger.info 'Metadata OK.' if m_errors.empty? && m_unsupported.empty? + @logger.info "Metadata OK." if m_errors.empty? && m_unsupported.empty? # only run the vendor check if the legacy profile-path is not used as argument if @legacy_profile_path == false # verify that a lockfile is present if we have dependencies if !metadata.dependencies.empty? - error.call(meta_path, 0, 0, nil, 'Your profile needs to be vendored with `inspec vendor`.') if !lockfile_exists? + error.call(meta_path, 0, 0, nil, "Your profile needs to be vendored with `inspec vendor`.") if !lockfile_exists? end if lockfile_exists? # verify if metadata and lockfile are out of sync if lockfile.deps.size != metadata.dependencies.size - error.call(meta_path, 0, 0, nil, 'inspec.yml and inspec.lock are out-of-sync. Please re-vendor with `inspec vendor`.') + error.call(meta_path, 0, 0, nil, "inspec.yml and inspec.lock are out-of-sync. Please re-vendor with `inspec vendor`.") end # verify if metadata and lockfile have the same dependency names - metadata.dependencies.each { |dep| + metadata.dependencies.each do |dep| # Skip if the dependency does not specify a name next if dep[:name].nil? # TODO: should we also verify that the soure is the same? if !lockfile.deps.map { |x| x[:name] }.include? dep[:name] error.call(meta_path, 0, 0, nil, "Cannot find #{dep[:name]} in lockfile. Please re-vendor with `inspec vendor`.") end - } + end end end # extract profile name result[:summary][:profile] = metadata.params[:name] count = controls_count result[:summary][:controls] = count if count == 0 - warn.call(nil, nil, nil, nil, 'No controls or tests were defined.') + warn.call(nil, nil, nil, nil, "No controls or tests were defined.") else @logger.info("Found #{count} controls.") end # iterate over hash of groups - params[:controls].each { |id, control| + params[:controls].each do |id, control| sfile = control[:source_location][:ref] sline = control[:source_location][:line] - error.call(sfile, sline, nil, id, 'Avoid controls with empty IDs') if id.nil? or id.empty? - next if id.start_with? '(generated ' + error.call(sfile, sline, nil, id, "Avoid controls with empty IDs") if id.nil? || id.empty? + next if id.start_with? "(generated " warn.call(sfile, sline, nil, id, "Control #{id} has no title") if control[:title].to_s.empty? warn.call(sfile, sline, nil, id, "Control #{id} has no descriptions") if control[:descriptions][:default].to_s.empty? warn.call(sfile, sline, nil, id, "Control #{id} has impact > 1.0") if control[:impact].to_f > 1.0 warn.call(sfile, sline, nil, id, "Control #{id} has impact < 0.0") if control[:impact].to_f < 0.0 - warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? or control[:checks].empty? - } + warn.call(sfile, sline, nil, id, "Control #{id} has no tests defined") if control[:checks].nil? || control[:checks].empty? + end # profile is valid if we could not find any error result[:summary][:valid] = result[:errors].empty? - @logger.info 'Control definitions OK.' if result[:warnings].empty? + @logger.info "Control definitions OK." if result[:warnings].empty? result end def controls_count params[:controls].values.length @@ -459,39 +451,39 @@ # filter files that should not be part of the profile # TODO ignore all .files, but add the files to debug output # display all files that will be part of the archive - @logger.debug 'Add the following files to archive:' - files.each { |f| @logger.debug ' ' + f } + @logger.debug "Add the following files to archive:" + files.each { |f| @logger.debug " " + f } if opts[:zip] # generate zip archive - require 'inspec/archive/zip' + require "inspec/archive/zip" zag = Inspec::Archive::ZipArchiveGenerator.new zag.archive(root_path, files, dst) else # generate tar archive - require 'inspec/archive/tar' + require "inspec/archive/tar" tag = Inspec::Archive::TarArchiveGenerator.new tag.archive(root_path, files, dst) end - @logger.info 'Finished archive generation.' + @logger.info "Finished archive generation." true end def locked_dependencies @locked_dependencies ||= load_dependencies end def lockfile_exists? - @source_reader.target.files.include?('inspec.lock') + @source_reader.target.files.include?("inspec.lock") end def lockfile_path - File.join(cwd, 'inspec.lock') + File.join(cwd, "inspec.lock") end def root_path @source_reader.target.prefix end @@ -504,16 +496,16 @@ # TODO(ssd): Relative path handling really needs to be carefully # thought through, especially with respect to relative paths in # tarballs. # def cwd - @target.is_a?(String) && File.directory?(@target) ? @target : './' + @target.is_a?(String) && File.directory?(@target) ? @target : "./" end def lockfile @lockfile ||= if lockfile_exists? - Inspec::Lockfile.from_content(@source_reader.target.read('inspec.lock')) + Inspec::Lockfile.from_content(@source_reader.target.read("inspec.lock")) else generate_lockfile end end @@ -548,18 +540,18 @@ # get all dependency checksums deps = Hash[locked_dependencies.list.map { |k, v| [k, v.profile.sha256] }] res = OpenSSL::Digest::SHA256.new files = source_reader.tests.to_a + source_reader.libraries.to_a + - source_reader.data_files.to_a + - [['inspec.yml', source_reader.metadata.content]] + - [['inspec.lock.deps', YAML.dump(deps)]] + source_reader.data_files.to_a + + [["inspec.yml", source_reader.metadata.content]] + + [["inspec.lock.deps", YAML.dump(deps)]] files.sort_by { |a| a[0] } .map { |f| res << f[0] << "\0" << f[1] << "\0" } - res.digest.unpack('H*')[0] + res.digest.unpack("H*")[0] end private # Create an archive name for this profile and an additional options @@ -571,17 +563,17 @@ if (name = opts[:output]) return Pathname.new(name) end name = params[:name] || - raise('Cannot create an archive without a profile name! Please '\ - 'specify the name in metadata or use --output to create the archive.') + raise("Cannot create an archive without a profile name! Please "\ + "specify the name in metadata or use --output to create the archive.") version = params[:version] || - raise('Cannot create an archive without a profile version! Please '\ - 'specify the version in metadata or use --output to create the archive.') - ext = opts[:zip] ? 'zip' : 'tar.gz' - slug = name.downcase.strip.tr(' ', '-').gsub(/[^\w-]/, '_') + raise("Cannot create an archive without a profile version! Please "\ + "specify the version in metadata or use --output to create the archive.") + ext = opts[:zip] ? "zip" : "tar.gz" + slug = name.downcase.strip.tr(" ", "-").gsub(/[^\w-]/, "_") Pathname.new(Dir.pwd).join("#{slug}-#{version}.#{ext}") end def load_params params = @source_reader.metadata.params @@ -594,10 +586,10 @@ def load_checks_params(params) load_libraries tests = collect_tests params[:controls] = controls = {} params[:groups] = groups = {} - prefix = @source_reader.target.prefix || '' + prefix = @source_reader.target.prefix || "" tests&.each do |rule| next if rule.nil? f = load_rule_filepath(prefix, rule) load_rule(rule, f, controls, groups) end