# Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'contrast/components/scope' # This is a reasonable place for the Kernel#catch hook to live. # No current plans for component re-design, but if we had some kind of # "do this when a component is hooked in" thing, this would live there. # For now, it's over-engineering to live anywhere else. -ajm module Kernel # :nodoc: alias_method :cs__catch, :catch # In the event of a `throw`, we need to override `catch` # to save & restore scope state: # # scope_level == 0 # # catch(:abc) do # with_contrast_scope do # throw :abc # will leak # end # end # # scope_level == 1 # # Frankly, this isn't how scope should be used. This is in place of # proper `ensure` blocks within the instrumentation call stack. # This will actually /create/ scope leaks if you're doing something like: # # catch(:ohno) do # enter scope # end # # abc() # # exit scope # # i.e. if you intend to change net scope across a catch block boundary. private def catch *args, &block # Save current scope level scope_level = Contrast::Components::Scope::COMPONENT_INTERFACE.scope_for_current_ec.instance_variable_get(:@contrast_scope) # Run original catch with block. retval = cs__catch(*args, &block) # Restore scope. Contrast::Components::Scope::COMPONENT_INTERFACE.scope_for_current_ec.instance_variable_set(:@contrast_scope, scope_level) retval end end