# encoding: utf-8 # author: Dominik Richter # author: Christoph Hartmann require 'inspec/log' require 'inspec/rule' require 'inspec/resource' require 'inspec/library_eval_context' require 'inspec/control_eval_context' require 'inspec/require_loader' require 'securerandom' require 'inspec/objects/attribute' module Inspec class ProfileContext def self.for_profile(profile, backend, attributes) new(profile.name, backend, { 'profile' => profile, 'attributes' => attributes, 'check_mode' => profile.check_mode }) end attr_reader :attributes, :backend, :profile_name, :profile_id, :resource_registry attr_accessor :rules def initialize(profile_id, backend, conf) if backend.nil? raise 'ProfileContext is initiated with a backend == nil. ' \ 'This is a backend error which must be fixed upstream.' end @profile_id = profile_id @backend = backend @conf = conf.dup @profile_name = @conf['profile'].profile_name || @profile_id if @conf['profile'] @skip_only_if_eval = @conf['check_mode'] @rules = {} @control_subcontexts = [] @lib_subcontexts = [] @require_loader = ::Inspec::RequireLoader.new Inspec::AttributeRegistry.register_profile_alias(@profile_id, @profile_name) if @profile_id != @profile_name @attributes = Inspec::AttributeRegistry.list_attributes_for_profile(@profile_id) # A local resource registry that only contains resources defined # in the transitive dependency tree of the loaded profile. @resource_registry = Inspec::Resource.new_registry @library_eval_context = Inspec::LibraryEvalContext.create(@resource_registry, @require_loader) @current_load = nil end def dependencies if @conf['profile'].nil? {} else @conf['profile'].locked_dependencies end end def to_resources_dsl Inspec::Resource.create_dsl(self) end def control_eval_context @control_eval_context ||= begin ctx = Inspec::ControlEvalContext.create(self, to_resources_dsl) ctx.new(@backend, @conf, dependencies, @require_loader, @skip_only_if_eval) end end def reload_dsl @control_eval_context = nil end def profile_supports_platform? return true if @conf['profile'].nil? @conf['profile'].supports_platform? end def profile_supports_inspec_version? return true if @conf['profile'].nil? @conf['profile'].supports_runtime? end def remove_rule(id) @rules[id] = nil if @rules.key?(id) @control_subcontexts.each do |c| c.remove_rule(id) end end def all_controls ret = @rules.values ret += @control_subcontexts.map(&:all_rules).flatten ret end alias all_rules all_controls def subcontext_by_name(name) found = @lib_subcontexts.find { |c| c.profile_id == name } if !found @lib_subcontexts.each do |c| found = c.subcontext_by_name(name) break if found end end found end def add_resources(context) @resource_registry.merge!(context.resource_registry) control_eval_context.add_resources(context) @lib_subcontexts << context reload_dsl end def add_subcontext(context) @control_subcontexts << context end def load_libraries(libs) lib_prefix = 'libraries' + File::SEPARATOR autoloads = [] libs.sort_by! { |l| l[1] } # Sort on source path so load order is deterministic libs.each do |content, source, line| path = source if source.start_with?(lib_prefix) path = source.sub(lib_prefix, '') autoloads.push(path) if File.dirname(path) == '.' end @require_loader.add(path, content, source, line) end # load all files directly that are flat inside the libraries folder autoloads.each do |path| next unless path.end_with?('.rb') load_library_file(*@require_loader.load(path)) unless @require_loader.loaded?(path) end reload_dsl end def load_control_file(*args) # Set `skip_file` to `false` between file loads to prevent skips from spanning multiple control files control_eval_context.skip_file = false load_with_context(control_eval_context, *args) end alias load load_control_file def load_library_file(*args) load_with_context(@library_eval_context, *args) end def load_with_context(context, content, source = nil, line = nil) Inspec::Log.debug("Loading #{source || ''} into #{self}") @current_load = { file: source } if content.is_a? Proc context.instance_eval(&content) elsif source.nil? && line.nil? context.instance_eval(content) else context.instance_eval(content, source || 'unknown', line || 1) end end def unregister_rule(id) @rules.delete(full_id(@profile_id, id)) end attr_reader :current_load def register_rule(r) # get the full ID file = if @current_load.nil? 'unknown' else @current_load[:file] || 'unknown' end r.instance_variable_set(:@__file, file) r.instance_variable_set(:@__group_title, current_load[:title]) # add the rule to the registry fid = full_id(Inspec::Rule.profile_id(r), Inspec::Rule.rule_id(r)) existing = @rules[fid] if existing.nil? @rules[fid] = r else Inspec::Rule.merge(existing, r) end end def register_attribute(name, options = {}) # we need to return an attribute object, to allow dermination of default values attribute = Inspec::AttributeRegistry.register_attribute(name, @profile_id, options) attribute.value = @conf['attributes'][name] unless @conf['attributes'].nil? || @conf['attributes'][name].nil? attribute.value end def set_header(field, val) @current_load[field] = val end private def full_id(pid, rid) return rid.to_s if pid.to_s.empty? pid.to_s + '/' + rid.to_s end end end