contrast-agent 6.15.1 → 6.15.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +2 -2
 - data/lib/contrast/agent/request/request_context.rb +2 -0
 - data/lib/contrast/agent/telemetry/base.rb +13 -4
 - data/lib/contrast/agent/telemetry/client.rb +3 -1
 - data/lib/contrast/agent/telemetry/telemetry.rb +6 -0
 - data/lib/contrast/agent/thread/thread_watcher.rb +7 -1
 - data/lib/contrast/agent/thread/worker_thread.rb +1 -1
 - data/lib/contrast/agent/version.rb +1 -1
 - data/lib/contrast/components/agent.rb +3 -0
 - data/lib/contrast/configuration.rb +9 -0
 - data/lib/contrast/framework/manager.rb +5 -2
 - data/lib/contrast/framework/sinatra/support.rb +22 -38
 - data/lib/contrast/funchook/funchook.rb +0 -1
 - data/lib/contrast/logger/aliased_logging.rb +26 -0
 - data/lib/contrast/utils/hash_digest.rb +7 -6
 - data/lib/contrast/utils/job_servers_running.rb +8 -6
 - data/lib/contrast/utils/log_utils.rb +1 -1
 - data/lib/contrast/utils/middleware_utils.rb +4 -0
 - data/lib/contrast/utils/net_http_base.rb +9 -3
 - metadata +13 -13
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: bb55239bd37c7b0d2c3adc47d2e47ff25e331694b9be68522a29f8e4ce8c1220
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 41a5a677403dd10c0dcd67673b32e32aa8f8ad04a82d963891f95ceaa25b46a1
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 94aaa0a8ed0b9fb08fb5c206bee3695cd1cc1f3a8b7752fa8c52abfb563a4e58fac60162ab13c4f06ceb785532ae1a76b93dcc8f348e99d29b112b265a88051b
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 658421a2a8558bac001eb707340f0bc763012d06b9fdcfe1137fb3ceff6f53966d7cc9af9bced07f08411b5e44af1ad5c4f5805b54abaae0ad71dcc81011fbb9
         
     | 
| 
         @@ -33,8 +33,8 @@ module Contrast 
     | 
|
| 
       33 
33 
     | 
    
         
             
                    # Parse the given controller and route from a Rack based application framework in order to create an instance
         
     | 
| 
       34 
34 
     | 
    
         
             
                    # of this class
         
     | 
| 
       35 
35 
     | 
    
         
             
                    #
         
     | 
| 
       36 
     | 
    
         
            -
                    # @param final_controller [Grape::API 
     | 
| 
       37 
     | 
    
         
            -
                    #   entrypoint of the route actively being executed
         
     | 
| 
      
 36 
     | 
    
         
            +
                    # @param final_controller [Class<Grape::API>, Class<Sinatra::Base>] the controller responsible for the
         
     | 
| 
      
 37 
     | 
    
         
            +
                    #   definition of the entrypoint of the route actively being executed
         
     | 
| 
       38 
38 
     | 
    
         
             
                    # @param method [String] the HTTP request method of the route actively being executed
         
     | 
| 
       39 
39 
     | 
    
         
             
                    # @param route_pattern [Grape::Router::Route, Mustermann::Sinatra] the pattern to which the url maps
         
     | 
| 
       40 
40 
     | 
    
         
             
                    # @param url [String] the literal url of the route actively being executed
         
     | 
| 
         @@ -135,6 +135,8 @@ module Contrast 
     | 
|
| 
       135 
135 
     | 
    
         
             
                    @observed_route = Contrast::Agent::Reporting::ObservedRoute.new
         
     | 
| 
       136 
136 
     | 
    
         
             
                    reporting_route = Contrast::Agent.framework_manager.get_route_information(@request)
         
     | 
| 
       137 
137 
     | 
    
         
             
                    append_to_observed_route(reporting_route)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  rescue StandardError => e
         
     | 
| 
      
 139 
     | 
    
         
            +
                    logger.error('Unable to determine current route', e)
         
     | 
| 
       138 
140 
     | 
    
         
             
                  end
         
     | 
| 
       139 
141 
     | 
    
         
             
                end
         
     | 
| 
       140 
142 
     | 
    
         
             
              end
         
     | 
| 
         @@ -7,12 +7,13 @@ require 'contrast/agent/telemetry/client' 
     | 
|
| 
       7 
7 
     | 
    
         
             
            require 'contrast/agent/thread/worker_thread'
         
     | 
| 
       8 
8 
     | 
    
         
             
            require 'contrast/agent/telemetry/telemetry'
         
     | 
| 
       9 
9 
     | 
    
         
             
            require 'contrast/agent/telemetry/exception'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'contrast/utils/job_servers_running'
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
12 
     | 
    
         
             
            module Contrast
         
     | 
| 
       12 
13 
     | 
    
         
             
              module Agent
         
     | 
| 
       13 
14 
     | 
    
         
             
                module Telemetry
         
     | 
| 
       14 
15 
     | 
    
         
             
                  # This class will initialize and hold everything needed for the telemetry
         
     | 
| 
       15 
     | 
    
         
            -
                  class Base < WorkerThread
         
     | 
| 
      
 16 
     | 
    
         
            +
                  class Base < WorkerThread # rubocop:disable Metrics/ClassLength
         
     | 
| 
       16 
17 
     | 
    
         
             
                    include Contrast::Components::Logger::InstanceMethods
         
     | 
| 
       17 
18 
     | 
    
         
             
                    # this is where we will send the data from the agents
         
     | 
| 
       18 
19 
     | 
    
         
             
                    URL = 'https://telemetry.ruby.contrastsecurity.com/'
         
     | 
| 
         @@ -48,6 +49,8 @@ module Contrast 
     | 
|
| 
       48 
49 
     | 
    
         
             
                      private
         
     | 
| 
       49 
50 
     | 
    
         | 
| 
       50 
51 
     | 
    
         
             
                      def telemetry_enabled?
         
     | 
| 
      
 52 
     | 
    
         
            +
                        return false if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
       51 
54 
     | 
    
         
             
                        opt_out_telemetry = return_value(:telemetry_opt_outs).to_s
         
     | 
| 
       52 
55 
     | 
    
         
             
                        return false if opt_out_telemetry.casecmp?('true') || opt_out_telemetry == '1'
         
     | 
| 
       53 
56 
     | 
    
         | 
| 
         @@ -56,7 +59,9 @@ module Contrast 
     | 
|
| 
       56 
59 
     | 
    
         
             
                        @_client = Contrast::Agent::Telemetry::Client.new
         
     | 
| 
       57 
60 
     | 
    
         
             
                        ip_opt_out_telemetry = @_client.initialize_connection(URL)
         
     | 
| 
       58 
61 
     | 
    
         
             
                        if ip_opt_out_telemetry.nil?
         
     | 
| 
       59 
     | 
    
         
            -
                           
     | 
| 
      
 62 
     | 
    
         
            +
                          # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 63 
     | 
    
         
            +
                          #   an infinite loop w/ telemetry
         
     | 
| 
      
 64 
     | 
    
         
            +
                          logger.debug("[Telemetry] Connection was not established properly!!! \n
         
     | 
| 
       60 
65 
     | 
    
         
             
                                      Telemetry reporting will be disabled!")
         
     | 
| 
       61 
66 
     | 
    
         
             
                          return false
         
     | 
| 
       62 
67 
     | 
    
         
             
                        end
         
     | 
| 
         @@ -74,6 +79,8 @@ module Contrast 
     | 
|
| 
       74 
79 
     | 
    
         
             
                    end
         
     | 
| 
       75 
80 
     | 
    
         | 
| 
       76 
81 
     | 
    
         
             
                    def attempt_to_start?
         
     | 
| 
      
 82 
     | 
    
         
            +
                      return unless super
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
       77 
84 
     | 
    
         
             
                      unless cs__class.enabled?
         
     | 
| 
       78 
85 
     | 
    
         
             
                        logger.info('[Telemetry] Telemetry service is disabled!')
         
     | 
| 
       79 
86 
     | 
    
         
             
                        return false
         
     | 
| 
         @@ -92,7 +99,7 @@ module Contrast 
     | 
|
| 
       92 
99 
     | 
    
         | 
| 
       93 
100 
     | 
    
         
             
                    def send_event event
         
     | 
| 
       94 
101 
     | 
    
         
             
                      if ::Contrast::AGENT.disabled?
         
     | 
| 
       95 
     | 
    
         
            -
                        logger. 
     | 
| 
      
 102 
     | 
    
         
            +
                        logger.debug('[Telemetry] Attempted to queue event with Agent disabled', caller: caller, event: event)
         
     | 
| 
       96 
103 
     | 
    
         
             
                        return
         
     | 
| 
       97 
104 
     | 
    
         
             
                      end
         
     | 
| 
       98 
105 
     | 
    
         | 
| 
         @@ -148,7 +155,9 @@ module Contrast 
     | 
|
| 
       148 
155 
     | 
    
         
             
                                sleep(retry_sleep_time) unless retry_sleep_time.nil?
         
     | 
| 
       149 
156 
     | 
    
         
             
                              end
         
     | 
| 
       150 
157 
     | 
    
         
             
                            rescue StandardError => e
         
     | 
| 
       151 
     | 
    
         
            -
                               
     | 
| 
      
 158 
     | 
    
         
            +
                              # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 159 
     | 
    
         
            +
                              #   an infinite loop w/ telemetry
         
     | 
| 
      
 160 
     | 
    
         
            +
                              logger.debug('[Telemetry] Could not send message to service from telemetry queue.', e)
         
     | 
| 
       152 
161 
     | 
    
         
             
                              stop!
         
     | 
| 
       153 
162 
     | 
    
         
             
                            end
         
     | 
| 
       154 
163 
     | 
    
         
             
                          end
         
     | 
| 
         @@ -100,7 +100,9 @@ module Contrast 
     | 
|
| 
       100 
100 
     | 
    
         
             
                    def get_event_json event
         
     | 
| 
       101 
101 
     | 
    
         
             
                      Array(event.to_controlled_hash).to_json
         
     | 
| 
       102 
102 
     | 
    
         
             
                    rescue Exception => e # rubocop:disable Lint/RescueException
         
     | 
| 
       103 
     | 
    
         
            -
                       
     | 
| 
      
 103 
     | 
    
         
            +
                      # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 104 
     | 
    
         
            +
                      #   an infinite loop w/ telemetry
         
     | 
| 
      
 105 
     | 
    
         
            +
                      logger.debug('[Telemetry] Unable to convert TelemetryEvent to JSON string', e, hsh)
         
     | 
| 
       104 
106 
     | 
    
         
             
                      raise(e)
         
     | 
| 
       105 
107 
     | 
    
         
             
                    end
         
     | 
| 
       106 
108 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -94,6 +94,12 @@ module Contrast 
     | 
|
| 
       94 
94 
     | 
    
         
             
                    def telemetry_exceptions_enabled?
         
     | 
| 
       95 
95 
     | 
    
         
             
                      opts_out_telemetry = return_value(:telemetry_opt_outs).to_s
         
     | 
| 
       96 
96 
     | 
    
         
             
                      return false if opts_out_telemetry.casecmp?('true') || opts_out_telemetry == '1'
         
     | 
| 
      
 97 
     | 
    
         
            +
                      # Double check if telemetry is enabled, this includes a check for Agent enable setting in the
         
     | 
| 
      
 98 
     | 
    
         
            +
                      # config. In case of disabled Agent the queue won't be created and the Telemetry would not
         
     | 
| 
      
 99 
     | 
    
         
            +
                      # be enabled. This check is here to prevent a loop of error creations that could no be added
         
     | 
| 
      
 100 
     | 
    
         
            +
                      # to a queue ( because the client cannot be initialized if the Agent is disabled or settings are
         
     | 
| 
      
 101 
     | 
    
         
            +
                      # missing).
         
     | 
| 
      
 102 
     | 
    
         
            +
                      return false unless Contrast::Agent::Telemetry::Base.enabled?
         
     | 
| 
       97 
103 
     | 
    
         | 
| 
       98 
104 
     | 
    
         
             
                      true
         
     | 
| 
       99 
105 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -7,6 +7,7 @@ require 'contrast/agent/reporting/reporting_workers/reporting_workers' 
     | 
|
| 
       7 
7 
     | 
    
         
             
            require 'contrast/agent/telemetry/base'
         
     | 
| 
       8 
8 
     | 
    
         
             
            require 'contrast/agent/protect/input_analyzer/worth_watching_analyzer'
         
     | 
| 
       9 
9 
     | 
    
         
             
            require 'contrast/config/diagnostics'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'contrast/utils/job_servers_running'
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
12 
     | 
    
         
             
            module Contrast
         
     | 
| 
       12 
13 
     | 
    
         
             
              module Agent
         
     | 
| 
         @@ -28,6 +29,11 @@ module Contrast 
     | 
|
| 
       28 
29 
     | 
    
         
             
                  attr_reader :reporter_app_settings_worker
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
31 
     | 
    
         
             
                  def initialize
         
     | 
| 
      
 32 
     | 
    
         
            +
                    if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
         
     | 
| 
      
 33 
     | 
    
         
            +
                      logger.info('Agent Disabled, shutting down all threads...')
         
     | 
| 
      
 34 
     | 
    
         
            +
                      return
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
       31 
37 
     | 
    
         
             
                    @heapdump_util = Contrast::Utils::HeapDumpUtil.new
         
     | 
| 
       32 
38 
     | 
    
         
             
                    @reporter = Contrast::Agent::Reporter.new
         
     | 
| 
       33 
39 
     | 
    
         
             
                    @reporter_heartbeat = Contrast::Agent::ReportingWorkers::ReporterHeartbeat.new
         
     | 
| 
         @@ -37,7 +43,7 @@ module Contrast 
     | 
|
| 
       37 
43 
     | 
    
         
             
                    @worth_watching_analyzer = Contrast::Agent::Protect::WorthWatchingInputAnalyzer.new unless protect_disabled?
         
     | 
| 
       38 
44 
     | 
    
         
             
                  end
         
     | 
| 
       39 
45 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
                  # @return [ 
     | 
| 
      
 46 
     | 
    
         
            +
                  # @return [Array<Contrast::Agent::WorkerThread>] map of process to thread startup status
         
     | 
| 
       41 
47 
     | 
    
         
             
                  def startup!
         
     | 
| 
       42 
48 
     | 
    
         
             
                    check_before_start
         
     | 
| 
       43 
49 
     | 
    
         | 
| 
         @@ -9,6 +9,7 @@ require 'contrast/components/security_logger' 
     | 
|
| 
       9 
9 
     | 
    
         
             
            require 'contrast/components/heap_dump'
         
     | 
| 
       10 
10 
     | 
    
         
             
            require 'contrast/components/ruby_component'
         
     | 
| 
       11 
11 
     | 
    
         
             
            require 'contrast/components/polling'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'contrast/agent/hooks/tracepoint_hook'
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
14 
     | 
    
         
             
            module Contrast
         
     | 
| 
       14 
15 
     | 
    
         
             
              module Components
         
     | 
| 
         @@ -23,6 +24,8 @@ module Contrast 
     | 
|
| 
       23 
24 
     | 
    
         
             
                    attr_reader :canon_name
         
     | 
| 
       24 
25 
     | 
    
         
             
                    # @return [Array]
         
     | 
| 
       25 
26 
     | 
    
         
             
                    attr_reader :config_values
         
     | 
| 
      
 27 
     | 
    
         
            +
                    # @return [Boolean]
         
     | 
| 
      
 28 
     | 
    
         
            +
                    attr_accessor :enable
         
     | 
| 
       26 
29 
     | 
    
         | 
| 
       27 
30 
     | 
    
         
             
                    CANON_NAME = 'agent'
         
     | 
| 
       28 
31 
     | 
    
         
             
                    CONFIG_VALUES = %w[enabled? omit_body?].cs__freeze
         
     | 
| 
         @@ -53,6 +53,7 @@ module Contrast 
     | 
|
| 
       53 
53 
     | 
    
         
             
                # @return [String,nil]
         
     | 
| 
       54 
54 
     | 
    
         
             
                attr_reader :config_file
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                CONTRAST_ENV_MARKER = 'CONTRAST__'
         
     | 
| 
       56 
57 
     | 
    
         
             
                DEFAULT_YAML_PATH  = 'contrast_security.yaml'
         
     | 
| 
       57 
58 
     | 
    
         
             
                MILLISECOND_MARKER = '_ms'
         
     | 
| 
       58 
59 
     | 
    
         
             
                CONVERSION = {}.cs__freeze
         
     | 
| 
         @@ -67,6 +68,14 @@ module Contrast 
     | 
|
| 
       67 
68 
     | 
    
         
             
                  config_kv = deep_symbolize_all_keys(load_config)
         
     | 
| 
       68 
69 
     | 
    
         
             
                  config_sources = assign_source_to(config_kv, Contrast::Components::Config::Sources::YAML)
         
     | 
| 
       69 
70 
     | 
    
         | 
| 
      
 71 
     | 
    
         
            +
                  unless cli_options
         
     | 
| 
      
 72 
     | 
    
         
            +
                    cli_options = {}
         
     | 
| 
      
 73 
     | 
    
         
            +
                    ENV.each do |key, value|
         
     | 
| 
      
 74 
     | 
    
         
            +
                      next unless key.to_s.start_with?(CONTRAST_ENV_MARKER)
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                      cli_options[key] = value
         
     | 
| 
      
 77 
     | 
    
         
            +
                    end
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
       70 
79 
     | 
    
         
             
                  # Overlay CLI options - they take precedence over config file
         
     | 
| 
       71 
80 
     | 
    
         
             
                  cli_options = deep_symbolize_all_keys(cli_options)
         
     | 
| 
       72 
81 
     | 
    
         
             
                  if cli_options
         
     | 
| 
         @@ -11,6 +11,7 @@ require 'contrast/framework/rack/support' 
     | 
|
| 
       11 
11 
     | 
    
         
             
            require 'contrast/framework/rails/support'
         
     | 
| 
       12 
12 
     | 
    
         
             
            require 'contrast/framework/sinatra/support'
         
     | 
| 
       13 
13 
     | 
    
         
             
            require 'contrast/utils/class_util'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'contrast/utils/job_servers_running'
         
     | 
| 
       14 
15 
     | 
    
         | 
| 
       15 
16 
     | 
    
         
             
            module Contrast
         
     | 
| 
       16 
17 
     | 
    
         
             
              module Framework
         
     | 
| 
         @@ -28,6 +29,8 @@ module Contrast 
     | 
|
| 
       28 
29 
     | 
    
         
             
                  ].cs__freeze
         
     | 
| 
       29 
30 
     | 
    
         | 
| 
       30 
31 
     | 
    
         
             
                  def initialize
         
     | 
| 
      
 32 
     | 
    
         
            +
                    return if Contrast::AGENT.disabled? || Contrast::Utils::JobServersRunning.job_servers_running?
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       31 
34 
     | 
    
         
             
                    @_frameworks = SUPPORTED_FRAMEWORKS.map do |framework_klass|
         
     | 
| 
       32 
35 
     | 
    
         
             
                      next unless enable_framework_support?(framework_klass.detection_class)
         
     | 
| 
       33 
36 
     | 
    
         | 
| 
         @@ -116,8 +119,8 @@ module Contrast 
     | 
|
| 
       116 
119 
     | 
    
         
             
                  # @param request [Contrast::Agent::Request] the current request.
         
     | 
| 
       117 
120 
     | 
    
         
             
                  # @return [Contrast::Agent::Reporting::RouteCoverage] the current route as a Dtm.
         
     | 
| 
       118 
121 
     | 
    
         
             
                  def get_route_information request
         
     | 
| 
       119 
     | 
    
         
            -
                    @_frameworks 
     | 
| 
       120 
     | 
    
         
            -
                        reject(&:nil?) 
     | 
| 
      
 122 
     | 
    
         
            +
                    @_frameworks&.lazy&.map { |framework_support| framework_support.current_route_coverage(request) }&.
         
     | 
| 
      
 123 
     | 
    
         
            +
                        reject(&:nil?)&.first
         
     | 
| 
       121 
124 
     | 
    
         
             
                  end
         
     | 
| 
       122 
125 
     | 
    
         | 
| 
       123 
126 
     | 
    
         
             
                  # Sometimes the framework we want to instrument is loaded after our agent code. To catch that case, we'll detect
         
     | 
| 
         @@ -67,30 +67,39 @@ module Contrast 
     | 
|
| 
       67 
67 
     | 
    
         
             
                      # Given the current request - return a RouteCoverage object
         
     | 
| 
       68 
68 
     | 
    
         | 
| 
       69 
69 
     | 
    
         
             
                      # @param request [Contrast::Agent::Request] a contrast tracked request.
         
     | 
| 
       70 
     | 
    
         
            -
                      # @param  
     | 
| 
      
 70 
     | 
    
         
            +
                      # @param _controller [::Sinatra::Base] optionally use this controller instead of global ::Sinatra::Base.
         
     | 
| 
       71 
71 
     | 
    
         
             
                      # @return [Contrast::Agent::Reporting::RouteCoverage, nil] a Dtm describing the route
         
     | 
| 
       72 
72 
     | 
    
         
             
                      # matched to the request if a match was found.
         
     | 
| 
       73 
     | 
    
         
            -
                      def current_route_coverage request,  
     | 
| 
       74 
     | 
    
         
            -
                        return unless sinatra_controller?(controller)
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
      
 73 
     | 
    
         
            +
                      def current_route_coverage request, _controller = ::Sinatra::Base, full_route = nil
         
     | 
| 
       76 
74 
     | 
    
         
             
                        method = request.env[::Rack::REQUEST_METHOD] # GET, PUT, POST, etc...
         
     | 
| 
       77 
     | 
    
         
            -
             
     | 
| 
      
 75 
     | 
    
         
            +
                        route = _cleaned_route(request)
         
     | 
| 
       78 
76 
     | 
    
         
             
                        # Find route match--checking superclasses if necessary.
         
     | 
| 
       79 
     | 
    
         
            -
                         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                        sinatra_controllers.each do |potential_controller|
         
     | 
| 
      
 78 
     | 
    
         
            +
                          next unless sinatra_controller?(potential_controller)
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                          next if potential_controller.nil? || potential_controller.cs__class == NilClass
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
      
 82 
     | 
    
         
            +
                          route_patterns = potential_controller.routes.fetch(method) { [] }.
         
     | 
| 
      
 83 
     | 
    
         
            +
                              map(&:first)
         
     | 
| 
      
 84 
     | 
    
         
            +
                          route_pattern = route_patterns.find do |matcher|
         
     | 
| 
      
 85 
     | 
    
         
            +
                            matcher.params(route) # ::Mustermann::Sinatra match.
         
     | 
| 
      
 86 
     | 
    
         
            +
                          end
         
     | 
| 
      
 87 
     | 
    
         
            +
                          next unless route_pattern
         
     | 
| 
       83 
88 
     | 
    
         | 
| 
       84 
     | 
    
         
            -
             
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
      
 89 
     | 
    
         
            +
                          full_route ||= request.env[::Rack::PATH_INFO]
         
     | 
| 
      
 90 
     | 
    
         
            +
                          new_route_coverage = Contrast::Agent::Reporting::RouteCoverage.new
         
     | 
| 
      
 91 
     | 
    
         
            +
                          new_route_coverage.attach_rack_based_data(potential_controller, method, route_pattern, full_route)
         
     | 
| 
      
 92 
     | 
    
         
            +
                          return new_route_coverage
         
     | 
| 
      
 93 
     | 
    
         
            +
                        end
         
     | 
| 
      
 94 
     | 
    
         
            +
                        nil
         
     | 
| 
       87 
95 
     | 
    
         
             
                      end
         
     | 
| 
       88 
96 
     | 
    
         | 
| 
       89 
97 
     | 
    
         
             
                      # Search object space for sinatra controllers--any class that subclasses ::Sinatra::Base.
         
     | 
| 
       90 
98 
     | 
    
         
             
                      #
         
     | 
| 
       91 
     | 
    
         
            -
                      # @return [Array<::Sinatra::Base 
     | 
| 
      
 99 
     | 
    
         
            +
                      # @return [Array<Class<::Sinatra::Base>>] sinatra controlelrs
         
     | 
| 
       92 
100 
     | 
    
         
             
                      def sinatra_controllers
         
     | 
| 
       93 
     | 
    
         
            -
                         
     | 
| 
      
 101 
     | 
    
         
            +
                        @_sinatra_controllers ||=
         
     | 
| 
      
 102 
     | 
    
         
            +
                          [::Sinatra::Base] + ObjectSpace.each_object(Class).select { |clazz| sinatra_controller?(clazz) }
         
     | 
| 
       94 
103 
     | 
    
         
             
                      end
         
     | 
| 
       95 
104 
     | 
    
         | 
| 
       96 
105 
     | 
    
         
             
                      def retrieve_request env
         
     | 
| 
         @@ -112,31 +121,6 @@ module Contrast 
     | 
|
| 
       112 
121 
     | 
    
         | 
| 
       113 
122 
     | 
    
         
             
                      private
         
     | 
| 
       114 
123 
     | 
    
         | 
| 
       115 
     | 
    
         
            -
                      # Given a controller and a route to match against, find the route_pattern and class that will serve the
         
     | 
| 
       116 
     | 
    
         
            -
                      # route. This is recursive as Sinatra's routing is recursive from subclass to super.
         
     | 
| 
       117 
     | 
    
         
            -
                      #
         
     | 
| 
       118 
     | 
    
         
            -
                      # @param controller [Sinatra::Base, #routes] a Sinatra application.
         
     | 
| 
       119 
     | 
    
         
            -
                      # @param method [::Rack::REQUEST_METHOD] GET, POST, PUT, etc...
         
     | 
| 
       120 
     | 
    
         
            -
                      # @param route [String] the relative route passed from Rack.
         
     | 
| 
       121 
     | 
    
         
            -
                      # @return [Array[Sinatra::Base, Mustermann::Sinatra], nil] Either the controller that
         
     | 
| 
       122 
     | 
    
         
            -
                      # will handle the route along with the route pattern or nil if no match.
         
     | 
| 
       123 
     | 
    
         
            -
                      def _route_recurse controller, method, route
         
     | 
| 
       124 
     | 
    
         
            -
                        return if controller.nil? || controller.cs__class == NilClass
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                        route_patterns = controller.routes.fetch(method) { [] }.
         
     | 
| 
       127 
     | 
    
         
            -
                            map(&:first)
         
     | 
| 
       128 
     | 
    
         
            -
                        route_pattern = route_patterns&.find do |matcher|
         
     | 
| 
       129 
     | 
    
         
            -
                          matcher.params(route) # ::Mustermann::Sinatra match.
         
     | 
| 
       130 
     | 
    
         
            -
                        end
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                        return controller, route_pattern if route_pattern
         
     | 
| 
       133 
     | 
    
         
            -
             
     | 
| 
       134 
     | 
    
         
            -
                        # Check routes defined in superclass if present.
         
     | 
| 
       135 
     | 
    
         
            -
                        return unless controller.superclass&.instance_variable_get(:@routes)
         
     | 
| 
       136 
     | 
    
         
            -
             
     | 
| 
       137 
     | 
    
         
            -
                        _route_recurse(controller.superclass, method, route)
         
     | 
| 
       138 
     | 
    
         
            -
                      end
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
124 
     | 
    
         
             
                      # Get route and do some cleanup matching that of Sinatra::Base#process_route.
         
     | 
| 
       141 
125 
     | 
    
         
             
                      #
         
     | 
| 
       142 
126 
     | 
    
         
             
                      # @param request [Contrast::Agent::Request] a contrast tracked request.
         
     | 
| 
         @@ -40,12 +40,38 @@ module Contrast 
     | 
|
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
                  private
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
      
 43 
     | 
    
         
            +
                  # There's a weird circular import in our code that we don't have time to untangle. This is 100% bad code and
         
     | 
| 
      
 44 
     | 
    
         
            +
                  # I'm sorry to the future team, but this is all we got.
         
     | 
| 
      
 45 
     | 
    
         
            +
                  #
         
     | 
| 
      
 46 
     | 
    
         
            +
                  # If the configuration we need is not available, we'll skip out for now - it means the agent isn't ready. We
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # won't get telemetry exceptions at this point, but that means the agent hasn't initialized and there's a
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # chance we're not supposed to. Essentially, we fail closed.
         
     | 
| 
      
 49 
     | 
    
         
            +
                  #
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # Once we have the agent initialized, we'll use the value to check. Since it cannot change once set, we'll use
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # the saved value.
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #
         
     | 
| 
      
 53 
     | 
    
         
            +
                  # - HM
         
     | 
| 
      
 54 
     | 
    
         
            +
                  #
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def buildable?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    if @_buildable.nil?
         
     | 
| 
      
 58 
     | 
    
         
            +
                      return false unless defined?(Contrast) &&
         
     | 
| 
      
 59 
     | 
    
         
            +
                          defined?(Contrast::Agent) &&
         
     | 
| 
      
 60 
     | 
    
         
            +
                          defined?(Contrast::Agent::Telemetry)
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                      @_buildable = Contrast::Agent::Telemetry.exceptions_enabled?
         
     | 
| 
      
 63 
     | 
    
         
            +
                    end
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @_buildable
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
       43 
67 
     | 
    
         
             
                  # @param type [ALIASED_FATAL, ALIASED_ERROR, ALIASED_WARN] the type of error, used to indicate the function used
         
     | 
| 
       44 
68 
     | 
    
         
             
                  #   for logging
         
     | 
| 
       45 
69 
     | 
    
         
             
                  # @param message [String] the exception message
         
     | 
| 
       46 
70 
     | 
    
         
             
                  # @param exception [Exception] The exception or error
         
     | 
| 
       47 
71 
     | 
    
         
             
                  # @param data [Object] Any structured data
         
     | 
| 
       48 
72 
     | 
    
         
             
                  def build_exception type, message = nil, exception = nil, data = nil
         
     | 
| 
      
 73 
     | 
    
         
            +
                    return unless buildable?
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
       49 
75 
     | 
    
         
             
                    stack_trace = wrapped_caller_locations
         
     | 
| 
       50 
76 
     | 
    
         
             
                    caller_idx = stack_trace&.find_index { |stack| stack.to_s.include?(type) } || 0
         
     | 
| 
       51 
77 
     | 
    
         
             
                    # The caller_stack is the method in which the error occurred, so has to be above this method
         
     | 
| 
         @@ -44,14 +44,15 @@ module Contrast 
     | 
|
| 
       44 
44 
     | 
    
         
             
                      update(route.signature)
         
     | 
| 
       45 
45 
     | 
    
         
             
                      if (observation = route.observations[0])
         
     | 
| 
       46 
46 
     | 
    
         
             
                        update(observation.verb)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      else
         
     | 
| 
      
 48 
     | 
    
         
            +
                        update(request.request_method)
         
     | 
| 
       47 
49 
     | 
    
         
             
                      end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                    return unless request ||= context&.request
         
     | 
| 
      
 50 
     | 
    
         
            +
                    else
         
     | 
| 
      
 51 
     | 
    
         
            +
                      return unless request ||= context&.request
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 53 
     | 
    
         
            +
                      update(request.normalized_uri) # the normalized URL used to access the method in the route.
         
     | 
| 
      
 54 
     | 
    
         
            +
                      update(request.request_method)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    end
         
     | 
| 
       55 
56 
     | 
    
         
             
                  end
         
     | 
| 
       56 
57 
     | 
    
         | 
| 
       57 
58 
     | 
    
         
             
                  # Update to CRC checksum the event source name and source type.
         
     | 
| 
         @@ -1,15 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Copyright (c) 2023 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
         
     | 
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
            require 'contrast/components/ 
     | 
| 
      
 4 
     | 
    
         
            +
            require 'contrast/components/ruby_component'
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            module Contrast
         
     | 
| 
       7 
7 
     | 
    
         
             
              module Utils
         
     | 
| 
       8 
8 
     | 
    
         
             
                # A module that detects whether any job servers attached to
         
     | 
| 
       9 
9 
     | 
    
         
             
                # the application are running
         
     | 
| 
       10 
10 
     | 
    
         
             
                module JobServersRunning
         
     | 
| 
       11 
     | 
    
         
            -
                  extend Contrast::Components::Logger::InstanceMethods
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
11 
     | 
    
         
             
                  class << self
         
     | 
| 
       14 
12 
     | 
    
         
             
                    def job_servers_running?
         
     | 
| 
       15 
13 
     | 
    
         
             
                      sidekiq_running? || rake_running?
         
     | 
| 
         @@ -20,7 +18,6 @@ module Contrast 
     | 
|
| 
       20 
18 
     | 
    
         
             
                    def sidekiq_running?
         
     | 
| 
       21 
19 
     | 
    
         
             
                      return unless defined?(Sidekiq) && Sidekiq.cs__respond_to?(:server?) && Sidekiq.server?
         
     | 
| 
       22 
20 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                      logger.trace('Detected the spawn of a Sidekiq process')
         
     | 
| 
       24 
21 
     | 
    
         
             
                      true
         
     | 
| 
       25 
22 
     | 
    
         
             
                    end
         
     | 
| 
       26 
23 
     | 
    
         | 
| 
         @@ -32,13 +29,18 @@ module Contrast 
     | 
|
| 
       32 
29 
     | 
    
         
             
                        return
         
     | 
| 
       33 
30 
     | 
    
         
             
                      end
         
     | 
| 
       34 
31 
     | 
    
         | 
| 
       35 
     | 
    
         
            -
                       
     | 
| 
      
 32 
     | 
    
         
            +
                      # This might be called before component even exist, so we backup to
         
     | 
| 
      
 33 
     | 
    
         
            +
                      # default disabled rake tasks.
         
     | 
| 
      
 34 
     | 
    
         
            +
                      disabled_rake_tasks = if Contrast.const_defined?(:APP_CONTEXT) # rubocop:disable Security/Module/ConstDefined
         
     | 
| 
      
 35 
     | 
    
         
            +
                                              Contrast::APP_CONTEXT.disabled_agent_rake_tasks
         
     | 
| 
      
 36 
     | 
    
         
            +
                                            else
         
     | 
| 
      
 37 
     | 
    
         
            +
                                              Contrast::Components::Ruby::Interface::DISABLED_RAKE_TASK_LIST
         
     | 
| 
      
 38 
     | 
    
         
            +
                                            end
         
     | 
| 
       36 
39 
     | 
    
         
             
                      has_disabled_task = Rake.application.top_level_tasks.any? do |top_level_task|
         
     | 
| 
       37 
40 
     | 
    
         
             
                        disabled_rake_tasks.include?(top_level_task)
         
     | 
| 
       38 
41 
     | 
    
         
             
                      end
         
     | 
| 
       39 
42 
     | 
    
         
             
                      return false unless has_disabled_task
         
     | 
| 
       40 
43 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
                      logger.trace('Detected startup within Rake task')
         
     | 
| 
       42 
44 
     | 
    
         
             
                      true
         
     | 
| 
       43 
45 
     | 
    
         
             
                    end
         
     | 
| 
       44 
46 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -51,7 +51,7 @@ module Contrast 
     | 
|
| 
       51 
51 
     | 
    
         
             
                    logger.extend(Contrast::Logger::Application)
         
     | 
| 
       52 
52 
     | 
    
         
             
                    logger.extend(Contrast::Logger::Request)
         
     | 
| 
       53 
53 
     | 
    
         
             
                    logger.extend(Contrast::Logger::Time)
         
     | 
| 
       54 
     | 
    
         
            -
                    logger.extend(Contrast::Logger::AliasedLogging) 
     | 
| 
      
 54 
     | 
    
         
            +
                    logger.extend(Contrast::Logger::AliasedLogging)
         
     | 
| 
       55 
55 
     | 
    
         
             
                  end
         
     | 
| 
       56 
56 
     | 
    
         | 
| 
       57 
57 
     | 
    
         
             
                  # Determine the valid path to which to log, given the precedence of config > settings > default.
         
     | 
| 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            require 'contrast/config/yaml_file'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'contrast/utils/job_servers_running'
         
     | 
| 
       5 
6 
     | 
    
         | 
| 
       6 
7 
     | 
    
         
             
            module Contrast
         
     | 
| 
       7 
8 
     | 
    
         
             
              module Utils
         
     | 
| 
         @@ -30,6 +31,9 @@ module Contrast 
     | 
|
| 
       30 
31 
     | 
    
         
             
                      logger.error('!!! CONFIG FILE IS INVALID - DISABLING CONTRAST AGENT !!!')
         
     | 
| 
       31 
32 
     | 
    
         
             
                    elsif ::Contrast::AGENT.disabled?
         
     | 
| 
       32 
33 
     | 
    
         
             
                      logger.warn('Contrast disabled by configuration. Continuing without instrumentation.')
         
     | 
| 
      
 34 
     | 
    
         
            +
                    elsif Contrast::Utils::JobServersRunning.job_servers_running?
         
     | 
| 
      
 35 
     | 
    
         
            +
                      logger.info('Server job detected disabling Agent...')
         
     | 
| 
      
 36 
     | 
    
         
            +
                      ::Contrast::AGENT.disable!
         
     | 
| 
       33 
37 
     | 
    
         
             
                    else
         
     | 
| 
       34 
38 
     | 
    
         
             
                      ::Contrast::AGENT.enable!
         
     | 
| 
       35 
39 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -43,7 +43,9 @@ module Contrast 
     | 
|
| 
       43 
43 
     | 
    
         
             
                    logger.debug('Client verified', service: service_name, url: url)
         
     | 
| 
       44 
44 
     | 
    
         
             
                    net_http_client
         
     | 
| 
       45 
45 
     | 
    
         
             
                  rescue StandardError => e
         
     | 
| 
       46 
     | 
    
         
            -
                     
     | 
| 
      
 46 
     | 
    
         
            +
                    # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 47 
     | 
    
         
            +
                    #   an infinite loop w/ telemetry
         
     | 
| 
      
 48 
     | 
    
         
            +
                    logger.debug('Connection failed', e, service: service_name, url: url)
         
     | 
| 
       47 
49 
     | 
    
         
             
                    nil
         
     | 
| 
       48 
50 
     | 
    
         
             
                  end
         
     | 
| 
       49 
51 
     | 
    
         | 
| 
         @@ -72,7 +74,9 @@ module Contrast 
     | 
|
| 
       72 
74 
     | 
    
         
             
                         Errno::ETIMEDOUT, Errno::ESHUTDOWN, Errno::EHOSTDOWN, Errno::EHOSTUNREACH, Errno::EISCONN,
         
     | 
| 
       73 
75 
     | 
    
         
             
                         Errno::ECONNABORTED, Errno::ENETRESET, Errno::ENETUNREACH => e
         
     | 
| 
       74 
76 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                     
     | 
| 
      
 77 
     | 
    
         
            +
                    # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 78 
     | 
    
         
            +
                    #   an infinite loop w/ telemetry
         
     | 
| 
      
 79 
     | 
    
         
            +
                    logger.debug("#{ service_name } connection failed", e.message)
         
     | 
| 
       76 
80 
     | 
    
         
             
                    false
         
     | 
| 
       77 
81 
     | 
    
         
             
                  end
         
     | 
| 
       78 
82 
     | 
    
         | 
| 
         @@ -110,7 +114,9 @@ module Contrast 
     | 
|
| 
       110 
114 
     | 
    
         
             
                      client.key = OpenSSL::PKey::RSA.new(File.read(Contrast::API.certification_key_file)).to_s
         
     | 
| 
       111 
115 
     | 
    
         
             
                    end
         
     | 
| 
       112 
116 
     | 
    
         
             
                  rescue Errno::ENOENT => e
         
     | 
| 
       113 
     | 
    
         
            -
                     
     | 
| 
      
 117 
     | 
    
         
            +
                    # TODO: RUBY-2033 we cannot log the error above debug level here b/c it results in
         
     | 
| 
      
 118 
     | 
    
         
            +
                    #   an infinite loop w/ telemetry
         
     | 
| 
      
 119 
     | 
    
         
            +
                    logger.debug('Custom certificates failed', e.message)
         
     | 
| 
       114 
120 
     | 
    
         
             
                  end
         
     | 
| 
       115 
121 
     | 
    
         | 
| 
       116 
122 
     | 
    
         
             
                  # sets default setting for client validation of certificates and
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: contrast-agent
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 6.15. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 6.15.3
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - galen.palmer@contrastsecurity.com
         
     | 
| 
         @@ -13,7 +13,7 @@ authors: 
     | 
|
| 
       13 
13 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       14 
14 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       15 
15 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       16 
     | 
    
         
            -
            date: 2023-02- 
     | 
| 
      
 16 
     | 
    
         
            +
            date: 2023-02-23 00:00:00.000000000 Z
         
     | 
| 
       17 
17 
     | 
    
         
             
            dependencies:
         
     | 
| 
       18 
18 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       19 
19 
     | 
    
         
             
              name: bundler
         
     | 
| 
         @@ -678,22 +678,22 @@ email: 
     | 
|
| 
       678 
678 
     | 
    
         
             
            executables: []
         
     | 
| 
       679 
679 
     | 
    
         
             
            extensions:
         
     | 
| 
       680 
680 
     | 
    
         
             
            - ext/cs__common/extconf.rb
         
     | 
| 
       681 
     | 
    
         
            -
            - ext/ 
     | 
| 
       682 
     | 
    
         
            -
            - ext/ 
     | 
| 
      
 681 
     | 
    
         
            +
            - ext/cs__assess_marshal_module/extconf.rb
         
     | 
| 
      
 682 
     | 
    
         
            +
            - ext/cs__assess_yield_track/extconf.rb
         
     | 
| 
      
 683 
     | 
    
         
            +
            - ext/cs__scope/extconf.rb
         
     | 
| 
       683 
684 
     | 
    
         
             
            - ext/cs__assess_kernel/extconf.rb
         
     | 
| 
       684 
     | 
    
         
            -
            - ext/ 
     | 
| 
       685 
     | 
    
         
            -
            - ext/ 
     | 
| 
      
 685 
     | 
    
         
            +
            - ext/cs__assess_array/extconf.rb
         
     | 
| 
      
 686 
     | 
    
         
            +
            - ext/cs__os_information/extconf.rb
         
     | 
| 
       686 
687 
     | 
    
         
             
            - ext/cs__assess_string/extconf.rb
         
     | 
| 
      
 688 
     | 
    
         
            +
            - ext/cs__assess_hash/extconf.rb
         
     | 
| 
       687 
689 
     | 
    
         
             
            - ext/cs__assess_regexp/extconf.rb
         
     | 
| 
       688 
     | 
    
         
            -
            - ext/cs__tests/extconf.rb
         
     | 
| 
       689 
690 
     | 
    
         
             
            - ext/cs__assess_module/extconf.rb
         
     | 
| 
       690 
     | 
    
         
            -
            - ext/ 
     | 
| 
       691 
     | 
    
         
            -
            - ext/ 
     | 
| 
       692 
     | 
    
         
            -
            - ext/cs__scope/extconf.rb
         
     | 
| 
      
 691 
     | 
    
         
            +
            - ext/cs__assess_string_interpolation/extconf.rb
         
     | 
| 
      
 692 
     | 
    
         
            +
            - ext/cs__tests/extconf.rb
         
     | 
| 
       693 
693 
     | 
    
         
             
            - ext/cs__assess_test/extconf.rb
         
     | 
| 
       694 
     | 
    
         
            -
            - ext/ 
     | 
| 
       695 
     | 
    
         
            -
            - ext/ 
     | 
| 
       696 
     | 
    
         
            -
            - ext/ 
     | 
| 
      
 694 
     | 
    
         
            +
            - ext/cs__assess_fiber_track/extconf.rb
         
     | 
| 
      
 695 
     | 
    
         
            +
            - ext/cs__assess_basic_object/extconf.rb
         
     | 
| 
      
 696 
     | 
    
         
            +
            - ext/cs__contrast_patch/extconf.rb
         
     | 
| 
       697 
697 
     | 
    
         
             
            extra_rdoc_files: []
         
     | 
| 
       698 
698 
     | 
    
         
             
            files:
         
     | 
| 
       699 
699 
     | 
    
         
             
            - ".clang-format"
         
     |