# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/utils/boolean_util' require 'contrast/utils/env_configuration_item' require 'contrast/utils/object_share' require 'contrast/configuration' module Contrast module Components # This component encapsulates reference to the configuration file. # At the time of writing, the configuration file is a yaml file reflecting # the 'common agent configuration' specification. # 'Config' and 'configuration' are to be interpreted as referring # specifically to these files and specifications, they are not generic # terms for other avenues of user configuration. # # This component is responsible for... # - encapsulating file access & concomitant error conditions # - implementing validity checks with respect to the specification # - memoizing/streamline field accesses # # Config fails fast. if it's not valid, the agent should break, and # it should break LOUDLY. Better to waste half an hour of the sysadmin's # time than to silently fail to deliver functionality. module Config CONTRAST_ENV_MARKER = 'CONTRAST__' class Interface # :nodoc: def initialize build end def build log: true @_valid = nil @config = Contrast::Configuration.new defaults overrides validate(log: log) end alias_method :rebuild, :build # Prefer abstraction, but use #raw if you need. # grep 'CONFIG.raw' for opportunities to refactor. def raw @config end def root raw.root end def enabled? @_enabled = !Contrast::Utils::BooleanUtil.false?(raw.enable) if @_enabled.nil? @_enabled end def disabled? !enabled? end def protect? @_protect = Contrast::Utils::BooleanUtil.true?(raw.protect.enable) if @_protect.nil? @_protect end def assess? @_assess = Contrast::Utils::BooleanUtil.true?(raw.assess.enable) if @_assess.nil? @_assess end def session_id @_session_id ||= raw.application.session_id || Contrast::Utils::ObjectShare::EMPTY_STRING end def session_metadata @_session_metadata ||= raw.application.session_metadata || Contrast::Utils::ObjectShare::EMPTY_STRING end def valid? @_valid = validate(log: false) if @_valid.nil? end def invalid? !valid? end private SESSION_VARIABLES = "Invalid configuration. Setting both application.session_id and application.session_metadata is not allowed.\n" def validate log: false # The config has information about how to construct the logger. # If the config is invalid, and you want to know about it, then # you have a circular dependency if you try to log it, # hence `log: false`. if !session_id.empty? && !session_metadata.empty? if log cs__class.log_error(SESSION_VARIABLES) else puts SESSION_VARIABLES end return false end true end def defaults raw.agent.service.host ||= Contrast::Configuration::DEFAULT_HOST raw.agent.service.port ||= Contrast::Configuration::DEFAULT_PORT end def overrides env_overrides end def env_overrides # For env variables resembling CONTRAST__WHATEVER__NESTED_VALUE # override raw.whatever.nested_value ENV.each do |env_key, env_value| next unless env_key.to_s.start_with?(CONTRAST_ENV_MARKER) config_item = Contrast::Utils::EnvConfigurationItem.new(env_key, env_value) raw.assign_value_to_path_array(config_item.dot_path_array, config_item.value) end end end COMPONENT_INTERFACE = Interface.new end end end