class Inspec::Metadata
Use valid? to determine if the metadata is coherent.
This allows the check CLI command to analyse the issues.
A Metadata object may be created and finalized with invalid data.
Extract metadata.rb information
def self.finalize(metadata, profile_id, options, logger = nil)
def self.finalize(metadata, profile_id, options, logger = nil) return nil if metadata.nil? param = metadata.params || {} options ||= {} param['version'] = param['version'].to_s unless param['version'].nil? metadata.params = symbolize_keys(param) metadata.params[:supports] = finalize_supports(metadata.params[:supports], logger) finalize_name(metadata, profile_id, options[:target]) metadata end
def self.finalize_name(metadata, profile_id, original_target)
def self.finalize_name(metadata, profile_id, original_target) # profile_id always overwrites whatever already exists as the name unless profile_id.to_s.empty? metadata.params[:name] = profile_id.to_s return end # don't overwrite an existing name return unless metadata.params[:name].nil? # if there's a title, there is no need to set a name too return unless metadata.params[:title].nil? # create a new name based on the original target if it exists # Crudely slug the target to not contain slashes, to avoid breaking # unit tests that look for warning sequences return if original_target.to_s.empty? metadata.params[:title] = "tests from #{original_target}" metadata.params[:name] = metadata.params[:title].gsub(%r{[\/\\]}, '.') end
def self.finalize_supports(supports, logger)
def self.finalize_supports(supports, logger) case x = supports when Hash then [finalize_supports_elem(x, logger)] when Array then x.map { |e| finalize_supports_elem(e, logger) }.compact when nil then [] else logger ||= Logger.new(nil) logger.warn( "Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\ "supports:\n - os-family: #{x}\n\n", ) [{ :'os-family' => x }] # rubocop:disable Style/HashSyntax end end
def self.finalize_supports_elem(elem, logger)
def self.finalize_supports_elem(elem, logger) case x = elem when Hash x[:release] = x[:release].to_s if x[:release] x when Array logger.warn( 'Failed to read supports entry that is an array. Please use '\ 'the `supports: {os-family: xyz}` syntax.', ) nil when nil then nil else logger ||= Logger.new(nil) logger.warn( "Do not use deprecated `supports: #{x}` syntax. Instead use:\n"\ "supports:\n - os-family: #{x}\n\n", ) { :'os-family' => x } # rubocop:disable Style/HashSyntax end end
def self.from_file(path, profile_id, logger = nil)
def self.from_file(path, profile_id, logger = nil) unless File.file?(path) logger ||= Logger.new(nil) logger.error "Can't find metadata file #{path}" return nil end from_ref(File.basename(path), File.read(path), profile_id, logger) end
def self.from_ref(ref, content, profile_id, logger = nil)
def self.from_ref(ref, content, profile_id, logger = nil) # NOTE there doesn't have to exist an actual file, it may come from an # archive (i.e., content) case File.basename(ref) when 'inspec.yml' from_yaml(ref, content, profile_id, logger) when 'metadata.rb' from_ruby(ref, content, profile_id, logger) else logger ||= Logger.new(nil) logger.error "Don't know how to handle metadata in #{ref}" nil end end
def self.from_ruby(ref, content, profile_id, logger = nil)
def self.from_ruby(ref, content, profile_id, logger = nil) res = Metadata.new(ref, logger) res.instance_eval(content, ref, 1) res.content = content finalize(res, profile_id, {}, logger) end
def self.from_yaml(ref, content, profile_id, logger = nil)
def self.from_yaml(ref, content, profile_id, logger = nil) res = Metadata.new(ref, logger) res.params = YAML.load(content) res.content = content finalize(res, profile_id, {}, logger) end
def self.symbolize_keys(obj)
def self.symbolize_keys(obj) return obj.map { |i| symbolize_keys(i) } if obj.is_a?(Array) return obj unless obj.is_a?(Hash) obj.each_with_object({}) do |(k, v), h| v = symbolize_keys(v) if v.is_a?(Hash) v = symbolize_keys(v) if v.is_a?(Array) h[k.to_sym] = v end end
def dependencies
def dependencies params[:depends] || [] end
def initialize(ref, logger = nil)
def initialize(ref, logger = nil) @ref = ref @logger = logger || Logger.new(nil) @content = '' @params = {} @missing_methods = [] end
def inspec_requirement
def inspec_requirement # using Gem::Requirement here to allow nil values which # translate to [">= 0"] Gem::Requirement.create(params[:inspec_version]) end
def method_missing(sth, *args)
def method_missing(sth, *args) @logger.warn "#{ref} doesn't support: #{sth} #{args}" @missing_methods.push(sth) end
def supports(sth, version = nil)
def supports(sth, version = nil) # Ignore supports with metadata.rb. This file is legacy and the way it # it handles `supports` deprecated. A deprecation warning will be printed # already. end
def supports_platform?(backend)
def supports_platform?(backend) backend.platform.supported?(params[:supports]) end
def supports_runtime?
def supports_runtime? running = Gem::Version.new(Inspec::VERSION) inspec_requirement.satisfied_by?(running) end
def unsupported
def unsupported @missing_methods end
def valid # rubocop:disable Metrics/AbcSize
return all warn and errors
def valid # rubocop:disable Metrics/AbcSize errors = [] warnings = [] %w{name version}.each do |field| next unless params[field.to_sym].nil? errors.push("Missing profile #{field} in #{ref}") end if %r{[\/\\]} =~ params[:name] errors.push("The profile name (#{params[:name]}) contains a slash" \ ' which is not permitted. Please remove all slashes from `inspec.yml`.') end # if version is set, ensure it is correct if !params[:version].nil? && !valid_version?(params[:version]) errors.push('Version needs to be in SemVer format') end %w{title summary maintainer copyright license}.each do |field| next unless params[field.to_sym].nil? warnings.push("Missing profile #{field} in #{ref}") end # if version is set, ensure it is in SPDX format if !params[:license].nil? && !Spdx.valid_license?(params[:license]) warnings.push("License '#{params[:license]}' needs to be in SPDX format. See https://spdx.org/licenses/.") end [errors, warnings] end
def valid?
def valid? errors, _warnings = valid errors.empty? && unsupported.empty? end
def valid_version?(value)
def valid_version?(value) Semverse::Version.new(value) true rescue Semverse::InvalidVersionFormat false end