# Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true cs__scoped_require 'contrast/components/interface' module Contrast module Utils # This utility allows us to report invalid configurations detected in # customer applications, as determined by Configuration Rules at runtime. module InvalidConfigurationUtil include Contrast::Components::Interface access_component :agent, :analysis, :contrast_service, :logging CS__PATH = 'path' CS__SESSION_ID = 'sessionId' CS__SNIPPET = 'snippet' # Build and report a finding for the given rule # # @param rule_id [String] the rule that was violated by the configuration # @param user_provided_options [Hash] the configuration value(s) which # violated the rule # @param call_location [Thread::Backtrace::Location] the location where # the bad configuration was set def cs__report_finding rule_id, user_provided_options, call_location finding = Contrast::Api::Dtm::Finding.new finding.rule_id = rule_id path = call_location.path # just get the file name, not the full path path = path.split(Contrast::Utils::ObjectShare::SLASH).last session_id = user_provided_options[:key].to_s if user_provided_options finding.version = Contrast::Agent::Assess::Policy::TriggerMethod::CURRENT_FINDING_VERSION finding.properties[CS__SESSION_ID] = Contrast::Utils::StringUtils.force_utf8(session_id) finding.properties[CS__PATH] = Contrast::Utils::StringUtils.force_utf8(path) file_path = call_location.absolute_path snippet = file_snippet(file_path, call_location) finding.properties[CS__SNIPPET] = Contrast::Utils::StringUtils.force_utf8(snippet) hash = Contrast::Utils::HashDigest.generate_config_hash(finding) finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash) finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding) activity = Contrast::Api::Dtm::Activity.new activity.findings << finding # If assess is enabled, we can just send the activity if ASSESS.enabled? build_tags(activity) CONTRAST_SERVICE.queue_message activity # Otherwise, if the Agent isn't ready, we have to queue the messages # until we know the starting state. else Contrast::Utils::ServiceSenderUtil.add_to_assess_messages activity end rescue StandardError => e logger.error('Unable to build a finding', e, rule: rule_id) end private # This seems silly to pull out, but we can ONLY call this in the case # where we have a configuration. Doing otherwise results in a bad error # case where we try to do other things, like logging, which behave # strangely without a config def build_tags activity activity.finding_tags = Contrast::Utils::StringUtils.force_utf8(ASSESS.tags) end def file_snippet file_path, call_location idx = call_location&.lineno if file_path && idx && File.exist?(file_path) idx = idx > 5 ? idx - 5 : 0 snippet = +'' File.foreach(file_path).with_index do |line, line_num| next unless line_num >= idx break if line_num > idx + 10 snippet << line snippet << Contrast::Utils::ObjectShare::NEW_LINE end return snippet end call_location&.label&.dup end end end end