lib/contrast/components/scope.rb in contrast-agent-5.1.0 vs lib/contrast/components/scope.rb in contrast-agent-5.2.0

- old
+ new

@@ -1,127 +1,134 @@ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details. # frozen_string_literal: true require 'fiber' -require 'monitor' require 'contrast/agent/scope' +require 'cs__scope/cs__scope' # This is the Scope component. # # It tracks /Contrast/ scope. That is, "are we currently doing assess or protect stuff within a patched method?" -- # this is how we avoid doing Contrast stuff on Contrast code or creating infinite loops -- or "are we in some other # execution context for which we need to special case?". module Contrast module Components - module Scope # :nodoc: - MONITOR = Monitor.new - EXECUTION_CONTEXT = {} # rubocop:disable Style/MutableConstant - - class Interface # :nodoc: - def initialize - # This is probably redundant with #scope_for_current_ec's nil check. - EXECUTION_CONTEXT[Fiber.current] = Contrast::Agent::Scope.new - end - - # This returns the scope governing the current execution context. Use this sparingly, preferring the instance - # & class methods to access and query scope, rather than interacting with the scope object directly. - def scope_for_current_ec - MONITOR.synchronize do - return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new - end - end - end - - module InstanceMethods # :nodoc: - # For each instance method on a scope, define a forwarder to the scope on the current execution context's scope. - def scope_for_current_ec - MONITOR.synchronize do - return EXECUTION_CONTEXT[Fiber.current] ||= Contrast::Agent::Scope.new - end - end - - def enter_contrast_scope! - scope_for_current_ec.enter_contrast_scope! - end - - def enter_deserialization_scope! - scope_for_current_ec.enter_deserialization_scope! - end - - def enter_split_scope! - scope_for_current_ec.enter_split_scope! - end - - def enter_scope! name - scope_for_current_ec.enter_scope! name - end - - def exit_contrast_scope! - scope_for_current_ec.exit_contrast_scope! - end - - def exit_deserialization_scope! - scope_for_current_ec.exit_deserialization_scope! - end - - def exit_split_scope! - scope_for_current_ec.exit_split_scope! - end - - def exit_scope! name - scope_for_current_ec.exit_scope! name - end - - def in_contrast_scope? - scope_for_current_ec.in_contrast_scope? - end - - def in_deserialization_scope? - scope_for_current_ec.in_deserialization_scope? - end - - def in_split_scope? - scope_for_current_ec.in_split_scope? - end - - def split_scope_depth - scope_for_current_ec.split_scope_depth - end - - def in_scope? name - scope_for_current_ec.in_scope? name - end - + # Constants defined in C: + # + # MONITOR = Mutex.new This was replaced from Monitor to Mutex. + # EXECUTION_CONTEXT = {} Hash containing all of the current ecs as keys pointing to their scope instances. + # EC_KEYS = [] set while we get the current ec, and used for sweeping the dead fibers in C. + # + # Methods defined in C: + # + # class Interface + # initializes the scope for the current fiber context. + # def initialize end; + # + # This returns the scope governing the current execution context. Use this sparingly, preferring the instance + # & class methods to access and query scope, rather than interacting with the scope object directly. + # def scope_for_current_ec end; + # end + # + # module Scope + # Singleton method for cleaning the dead fibers and unused scope instances, scope GC. + # def self.sweep_dead_ecs end; + # end + module Scope + # Methods defined in C: + # + # For each instance method on a scope, define a forwarder to the scope on the + # current execution context's scope. + # def scope_for_current_ec end; + # + # all the methods bellow are used as forwarders to be executed in the current ec. + # exp: in_contrast_scope? => scope_for_current_ec.enter_contrast_scope! + # + # Check if we are in contrast scope. + # def in_contrast_scope? end; + # @return [Boolean] check if we are in contrast scope + # if the scope is above 0 return true. + # + # check if we are in deserialization scope. + # def in_deserialization_scope? end; + # @return [Boolean] check if we are in contrast scope + # if the scope is above 0 return true. + # + # check if we are in split scope. + # def in_split_scope? end; + # @return [Boolean] check if we are in contrast scope + # if the scope is above 0 return true. + # + # enter contrast scope. + # def enter_contrast_scope! end; + # @return @contrast_scope [Integer] contrast scope increased. + # + # enter deserialization scope. + # def enter_deserialization_scope! end; + # @return @deserialization_scope [Integer] deserialization scope increased. + # + # enter split scope. + # def enter_split_scope! end; + # @return @split_scope [Integer] split scope increased. + # + # check split scope depth. + # def split_scope_depth end; + # @return @split_scope [Integer] split scope depth. + # + # exit contrast scope. + # def exit_contrast_scope! end; + # @return @contrast_scope [Integer] contrast scope decreased. + # + # exit deserialization scope. + # def exit_deserialization_scope! end; + # @return @deserialization_scope [Integer] deserialization scope decreased. + # + # exit split scope. + # def exit_split_scope! end; + # @return @split_scope [Integer] split scope decreased. + # + # Static methods to be used, the cases are defined by the usage from the above methods + # + # check if are in specific scope. + # def in_scope? name end; + # @param name [Symbol] scope symbol representing scope to check. + # @return [Boolean] check if we are in passed scope. + # + # enter specific scope. + # def enter_scope! name end; + # @param name [Symbol] scope symbol representing scope to enter. + # @return scope [Integer] entered scope value increased. + # + # exit specific scope. + # def exit_cope! name end; + # @param name [Symbol] scope symbol representing scope to exit. + # @return scope [Integer] entered scope value decreased. + module InstanceMethods + # Forwarder to execute block in contrast scope under current + # method execution context. On completion exits scope. def with_contrast_scope scope_for_current_ec.enter_contrast_scope! yield ensure scope_for_current_ec.exit_contrast_scope! end + # Forwarder to execute block in deserialization scope under current + # method execution context. On completion exits scope. def with_deserialization_scope scope_for_current_ec.enter_deserialization_scope! yield ensure scope_for_current_ec.exit_deserialization_scope! end + # Forwarder to execute block in split scope under current + # method execution context. On completion exits scope. def with_split_scope scope_for_current_ec.enter_split_scope! yield ensure scope_for_current_ec.exit_split_scope! - end - end - - def self.sweep_dead_ecs - # TODO: RUBY-534, #sweep_dead_ecs compensates for a lack of weak tables. when we can use WeakRef, we should - # investigate removing this call and instead use the WeakRef for the Execution Context's Keys or using our - # Finalizers Hash for Fibers - MONITOR.synchronize do - EXECUTION_CONTEXT.delete_if do |ec, _scope| - !ec.alive? - end end end ClassMethods = InstanceMethods end