class Inspec::Metadata

The Metadata class represents a profile's metadata. This includes the metadata stored in the profile's metadata.rb file, as well as inferred metadata like if this profile supports the current runtime and the intended target. This class does NOT represent the runtime state of a profile during execution. See lib/inspec/profile.rb for the runtime representation of a profile.

A Metadata object may be created and finalized with invalid data. This allows the check CLI command to analyse the issues. Use valid? to determine if the metadata is coherent.

Attributes

content[RW]
params[RW]
ref[R]

Public Class Methods

finalize(metadata, profile_id, options, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 205
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
finalize_name(metadata, profile_id, original_target) click to toggle source
# File lib/inspec/metadata.rb, line 183
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
finalize_supports(supports, logger) click to toggle source
# File lib/inspec/metadata.rb, line 175
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 []
  end
end
finalize_supports_elem(elem, logger) click to toggle source
# File lib/inspec/metadata.rb, line 152
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
    Inspec.deprecate(
      :supports_syntax,
      "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
from_file(path, profile_id, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 248
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
from_ref(ref, content, profile_id, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 233
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
from_ruby(ref, content, profile_id, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 226
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
from_yaml(ref, content, profile_id, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 218
def self.from_yaml(ref, content, profile_id, logger = nil)
  require "erb" unless defined?(Erb)
  res = Metadata.new(ref, logger)
  res.params = YAML.load(ERB.new(content).result)
  res.content = content
  finalize(res, profile_id, {}, logger)
end
new(ref, logger = nil) click to toggle source
# File lib/inspec/metadata.rb, line 24
def initialize(ref, logger = nil)
  @ref = ref
  @logger = logger || Logger.new(nil)
  @content = ""
  @params = {}
  @missing_methods = []
end
symbolize_keys(obj) click to toggle source
# File lib/inspec/metadata.rb, line 141
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

Public Instance Methods

dependencies() click to toggle source
# File lib/inspec/metadata.rb, line 50
def dependencies
  params[:depends] || []
end
inspec_requirement() click to toggle source
# File lib/inspec/metadata.rb, line 60
def inspec_requirement
  # using Gem::Requirement here to allow nil values which
  # translate to [">= 0"]
  Gem::Requirement.create(params[:inspec_version])
end
method_missing(sth, *args) click to toggle source
# File lib/inspec/metadata.rb, line 132
def method_missing(sth, *args)
  @logger.warn "#{ref} doesn't support: #{sth} #{args}"
  @missing_methods.push(sth)
end
supports(sth, version = nil) click to toggle source
# File lib/inspec/metadata.rb, line 54
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
supports_platform?(backend) click to toggle source
# File lib/inspec/metadata.rb, line 71
def supports_platform?(backend)
  require "inspec/resources/platform" # break circularity in load
  backend.platform.supported?(params[:supports])
end
supports_runtime?() click to toggle source
# File lib/inspec/metadata.rb, line 66
def supports_runtime?
  running = Gem::Version.new(Inspec::VERSION)
  inspec_requirement.satisfied_by?(running)
end
unsupported() click to toggle source
# File lib/inspec/metadata.rb, line 137
def unsupported
  @missing_methods
end
valid() click to toggle source

return all warn and errors

# File lib/inspec/metadata.rb, line 77
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

  unless supports_runtime?
    warnings.push("The current inspec version #{Inspec::VERSION} cannot satisfy profile inspec_version constraint #{params[:inspec_version]}")
  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 license is set, ensure it is in SPDX format or marked as proprietary
  if !params[:license].nil? && !valid_license?(params[:license])
    warnings.push("License '#{params[:license]}' needs to be in SPDX format or marked as 'Proprietary'. See https://spdx.org/licenses/.")
  end

  [errors, warnings]
end
valid?() click to toggle source

returns true or false

# File lib/inspec/metadata.rb, line 116
def valid?
  errors, _warnings = valid
  errors.empty? && unsupported.empty?
end
valid_license?(value) click to toggle source
# File lib/inspec/metadata.rb, line 128
def valid_license?(value)
  value =~ /^Proprietary[,;]?\b/ || Spdx.valid_license?(value)
end
valid_version?(value) click to toggle source
# File lib/inspec/metadata.rb, line 121
def valid_version?(value)
  Semverse::Version.new(value)
  true
rescue Semverse::InvalidVersionFormat
  false
end