# 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/framework/view_technologies_descriptor' cs__scoped_require 'contrast/framework/platform_version' cs__scoped_require 'contrast/framework/base_support' cs__scoped_require 'contrast/framework/rails_support' cs__scoped_require 'contrast/framework/sinatra_support' cs__scoped_require 'contrast/components/interface' cs__scoped_require 'contrast/utils/class_util' module Contrast module Framework # Allows access to framework specific information class Manager include Contrast::Components::Interface access_component :logging, :analysis # Order here does matter as the first framework listed will be the first one we pull information from # Rack will be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra # do not exist SUPPORTED_FRAMEWORKS = [ Contrast::Framework::RailsSupport, Contrast::Framework::SinatraSupport ].cs__freeze def initialize @_frameworks = SUPPORTED_FRAMEWORKS.map do |framework_klass| next unless enable_framework_support?(framework_klass.detection_class) logger.debug("Detected framework #{ framework_klass.detection_class } - enabling support") framework_klass end @_frameworks.compact! end def find_applicable_view_technologies scan_views_for_all_frameworks end def find_route_discovery_data routes_for_all_frameworks end def platform_version framework_version = first_framework_result :version, '' Contrast::Framework::PlatformVersion.from_string(framework_version) end def server_type first_framework_result :server_type, 'rack' end def app_name first_framework_result :application_name, 'root' end def get_route_dtm request result = nil @_frameworks.find do |framework_klass| # TODO: RUBY-763 Sinatra::Base#call patch adds the Route report next if framework_klass == Contrast::Framework::SinatraSupport result = framework_klass.current_route(request) end result end private def enable_framework_support? klass Contrast::Utils::ClassUtil.truly_defined?(klass) end def scan_views_for_all_frameworks data_for_all_frameworks :scan_views end def routes_for_all_frameworks data_for_all_frameworks :collect_routes end # This returns an array of all data from each framework in a flat, no-nil values array # # @param method_name [Symbol] the method to call on each FrameworkSupport class # @return [Array] def data_for_all_frameworks method_name data = @_frameworks.flat_map do |framework| framework.send(method_name) end.compact data end # This returns a single object from the first framework to successfully respond # # @param method_name [Symbol] the method to call on each FrameworkSupport class # @return [Object] - Determined by method to be invoked def first_framework_result method_name, default_value result = nil @_frameworks.each do |framework| result = framework.send(method_name) break if result end result || default_value end end end end