# Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true module Contrast module Utils # helper methods for Contrast::Agent::Middleware # including disclaimers, deprecation notices, error handling, setup module MiddlewareUtils private # TODO: RUBY-1609 update this part of the method for Ruby Version and Year LANGUAGE_DEPRECATION_VERSION = '2.7' LANGUAGE_DEPRECATION_YEAR = '2023' LANGUAGE_DEPRECATION_WARNING = "[Contrast Security] [DEPRECATION] Support for Ruby #{ LANGUAGE_DEPRECATION_VERSION } will be removed in "\ "April #{ LANGUAGE_DEPRECATION_YEAR }. Please contact Customer Support prior if you require continued support." def setup_agent ::Contrast::SETTINGS.reset_state inform_deprecations telemetry_disclaimer if ::Contrast::CONFIG.invalid? ::Contrast::AGENT.disable! logger.error('!!! CONFIG FILE IS INVALID - DISABLING CONTRAST AGENT !!!') elsif ::Contrast::AGENT.disabled? logger.warn('Contrast disabled by configuration. Continuing without instrumentation.') else ::Contrast::AGENT.enable! end end SECURITY_EXCEPTION_MARKER = 'Contrast::SecurityException' # We're only going to suppress SecurityExceptions indicating a blocked attack. And, only if the # config.agent.ruby.exceptions.capture? is set # @raise [Contrast::SecurityException] if exceptions.capture? is allowed def handle_exception exception if security_exception?(exception) exception_control = ::Contrast::AGENT.exception_control raise(exception) unless exception_control[:enable] [exception_control[:status], {}, [exception_control[:message]]] else logger.debug('Re-throwing original error', exception) raise(exception) end end # Is the given exception one raised by our Protect code? # # @param exception [Exception] # @return [Boolean] def security_exception? exception exception.is_a?(Contrast::SecurityException) || exception.message&.include?(SECURITY_EXCEPTION_MARKER) end # As we deprecate support to prepare to remove dead code, we need to inform our users still relying on the now # deprecated and soon to be removed functionality. This method handles doing that by leveraging the standard # Kernel#warn approach def inform_deprecations # Warn customers that they're on a version that's losing support in the next year. Kernel.warn(LANGUAGE_DEPRECATION_WARNING) if RUBY_VERSION.start_with?(LANGUAGE_DEPRECATION_VERSION) end # Displays Telemetry disclaimer if Telemetry is enabled. # if .telemetry file doesn't exist we create one and then show the disclaimer. # if the file already exists we do nothing. def telemetry_disclaimer return unless Contrast::Agent::Telemetry::Base.enabled? return unless Contrast::Utils::Telemetry.create_telemetry_file logger.info(Contrast::Utils::Telemetry.disclaimer) $stdout.print(Contrast::Utils::Telemetry.disclaimer) true end # @raise [Contrast::SecurityException] raises error to prevent an attack def application_code env logger.trace_with_time('application') do app.call(env) end rescue Contrast::SecurityException => e logger.trace('Security Exception raised during application lifecycle to prevent an attack', e) raise(e) end end end end