# 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/sinatra_support' module Sinatra # Our patch into the Sinatra::Base Class, allowing for the inventory of the # routes called by the application class Base alias_method :cs__patched_call!, :call! # publicly available method for Sinatra::Base things -- unfortunately, # getting the routes appear to require a lookup every time def call! *args cs__patched_map_route(*args) cs__patched_call!(*args) end private # Use logic copied from Sinatra::Base#process_route to determine which # pattern matches the request being invoked by Sinatra::Base#call! # # @param args [Array] we rely on the @settings object in # Sinatra::Base and the env variable passed in as args[0] def cs__patched_map_route *args context = Contrast::Agent::REQUEST_TRACKER.current return unless context env = args[0] return unless env # There isn't a Request object in the Sinatra::Base yet - it's made # during the #call! method, which we're patching - so we need to # access the env method = env[Rack::REQUEST_METHOD] route = env[Rack::PATH_INFO] route = route.to_s # get all potential routes for this request type routes = settings.routes[method] return unless routes # Do some cleanup that matches Sinatra::Base#process_route route = '/' if route.empty? && !settings.empty_path_info? route = route[0..-2] if !settings.strict_paths? && route != '/' && route.end_with?('/') # Find the route that matches this request. Since we're using # settings, we should resolve in the same precedence as Sinatra routes.each do |pattern, _, _| # Mustermann::Sinatra next unless pattern.params(route) dtm = Contrast::Framework::SinatraSupport.send(:route_to_coverage, cs__class, method, pattern) context.append_route_coverage(dtm) break end end end end