contrast-agent 4.8.0 → 4.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +6 -1
  3. data/.rspec +0 -1
  4. data/.rspec_parallel +6 -0
  5. data/.simplecov +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +0 -1
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +0 -2
  8. data/lib/contrast/agent/assess/contrast_event.rb +1 -5
  9. data/lib/contrast/agent/assess/contrast_object.rb +0 -3
  10. data/lib/contrast/agent/assess/finalizers/hash.rb +2 -5
  11. data/lib/contrast/agent/assess/policy/patcher.rb +5 -4
  12. data/lib/contrast/agent/assess/policy/policy.rb +1 -1
  13. data/lib/contrast/agent/assess/policy/policy_scanner.rb +2 -6
  14. data/lib/contrast/agent/assess/policy/preshift.rb +16 -12
  15. data/lib/contrast/agent/assess/policy/propagation_method.rb +102 -59
  16. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -7
  17. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +31 -11
  18. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -9
  19. data/lib/contrast/agent/assess/policy/propagator/split.rb +10 -6
  20. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +3 -3
  21. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +6 -7
  22. data/lib/contrast/agent/assess/policy/source_method.rb +18 -22
  23. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +0 -4
  24. data/lib/contrast/agent/assess/policy/trigger_method.rb +62 -88
  25. data/lib/contrast/agent/assess/policy/trigger_node.rb +1 -1
  26. data/lib/contrast/agent/assess/property/evented.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +3 -4
  28. data/lib/contrast/agent/at_exit_hook.rb +3 -3
  29. data/lib/contrast/agent/class_reopener.rb +6 -5
  30. data/lib/contrast/agent/disable_reaction.rb +4 -5
  31. data/lib/contrast/agent/exclusion_matcher.rb +2 -7
  32. data/lib/contrast/agent/inventory/database_config.rb +117 -0
  33. data/lib/contrast/agent/inventory/dependency_analysis.rb +2 -6
  34. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -10
  35. data/lib/contrast/agent/inventory/policy/datastores.rb +5 -6
  36. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  37. data/lib/contrast/agent/middleware.rb +15 -13
  38. data/lib/contrast/agent/patching/policy/after_load_patch.rb +6 -3
  39. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +21 -16
  40. data/lib/contrast/agent/patching/policy/module_policy.rb +2 -4
  41. data/lib/contrast/agent/patching/policy/patch.rb +13 -8
  42. data/lib/contrast/agent/patching/policy/patch_status.rb +3 -7
  43. data/lib/contrast/agent/patching/policy/patcher.rb +14 -14
  44. data/lib/contrast/agent/patching/policy/policy.rb +2 -4
  45. data/lib/contrast/agent/patching/policy/policy_node.rb +2 -3
  46. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +1 -1
  47. data/lib/contrast/agent/protect/policy/policy.rb +1 -1
  48. data/lib/contrast/agent/protect/policy/rule_applicator.rb +3 -5
  49. data/lib/contrast/agent/protect/rule/base.rb +10 -10
  50. data/lib/contrast/agent/protect/rule/cmd_injection.rb +4 -5
  51. data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -53
  52. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -5
  53. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +137 -0
  54. data/lib/contrast/agent/protect/rule/sqli.rb +7 -70
  55. data/lib/contrast/agent/reaction_processor.rb +3 -4
  56. data/lib/contrast/agent/request.rb +13 -7
  57. data/lib/contrast/agent/request_context.rb +36 -34
  58. data/lib/contrast/agent/request_handler.rb +5 -3
  59. data/lib/contrast/agent/response.rb +2 -3
  60. data/lib/contrast/agent/rewriter.rb +4 -3
  61. data/lib/contrast/agent/rule_set.rb +5 -4
  62. data/lib/contrast/agent/scope.rb +32 -20
  63. data/lib/contrast/agent/service_heartbeat.rb +2 -3
  64. data/lib/contrast/agent/static_analysis.rb +7 -6
  65. data/lib/contrast/agent/thread.rb +2 -4
  66. data/lib/contrast/agent/thread_watcher.rb +3 -4
  67. data/lib/contrast/agent/tracepoint_hook.rb +20 -7
  68. data/lib/contrast/agent/version.rb +1 -1
  69. data/lib/contrast/api/communication/messaging_queue.rb +16 -11
  70. data/lib/contrast/api/communication/response_processor.rb +11 -11
  71. data/lib/contrast/api/communication/service_lifecycle.rb +9 -5
  72. data/lib/contrast/api/communication/socket_client.rb +18 -14
  73. data/lib/contrast/api/communication/speedracer.rb +5 -6
  74. data/lib/contrast/api/decorators/address.rb +2 -3
  75. data/lib/contrast/api/decorators/agent_startup.rb +7 -9
  76. data/lib/contrast/api/decorators/application_startup.rb +9 -10
  77. data/lib/contrast/api/decorators/application_update.rb +0 -4
  78. data/lib/contrast/api/decorators/http_request.rb +3 -7
  79. data/lib/contrast/api/decorators/instrumentation_mode.rb +3 -5
  80. data/lib/contrast/api/decorators/message.rb +7 -7
  81. data/lib/contrast/api/decorators/route_coverage.rb +24 -1
  82. data/lib/contrast/api/decorators/trace_event_object.rb +2 -3
  83. data/lib/contrast/components/agent.rb +13 -15
  84. data/lib/contrast/components/app_context.rb +7 -11
  85. data/lib/contrast/components/assess.rb +19 -16
  86. data/lib/contrast/components/base.rb +40 -0
  87. data/lib/contrast/components/config.rb +1 -2
  88. data/lib/contrast/components/contrast_service.rb +8 -11
  89. data/lib/contrast/components/heap_dump.rb +5 -4
  90. data/lib/contrast/components/inventory.rb +2 -7
  91. data/lib/contrast/components/logger.rb +14 -10
  92. data/lib/contrast/components/protect.rb +10 -13
  93. data/lib/contrast/components/sampling.rb +5 -5
  94. data/lib/contrast/components/scope.rb +9 -32
  95. data/lib/contrast/components/settings.rb +1 -5
  96. data/lib/contrast/config/base_configuration.rb +14 -6
  97. data/lib/contrast/configuration.rb +22 -19
  98. data/lib/contrast/extension/assess/array.rb +3 -15
  99. data/lib/contrast/extension/assess/eval_trigger.rb +2 -23
  100. data/lib/contrast/extension/assess/fiber.rb +6 -16
  101. data/lib/contrast/extension/assess/hash.rb +3 -13
  102. data/lib/contrast/extension/assess/kernel.rb +3 -14
  103. data/lib/contrast/extension/assess/marshal.rb +6 -14
  104. data/lib/contrast/extension/assess/regexp.rb +5 -15
  105. data/lib/contrast/extension/assess/string.rb +6 -31
  106. data/lib/contrast/extension/extension.rb +61 -0
  107. data/lib/contrast/extension/kernel.rb +2 -4
  108. data/lib/contrast/extension/protect/kernel.rb +0 -15
  109. data/lib/contrast/framework/grape/support.rb +174 -0
  110. data/lib/contrast/framework/manager.rb +44 -9
  111. data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -6
  112. data/lib/contrast/framework/rack/support.rb +1 -1
  113. data/lib/contrast/framework/rails/patch/assess_configuration.rb +5 -8
  114. data/lib/contrast/framework/rails/patch/support.rb +43 -36
  115. data/lib/contrast/framework/rails/railtie.rb +8 -6
  116. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
  117. data/lib/contrast/framework/rails/support.rb +60 -13
  118. data/lib/contrast/framework/sinatra/support.rb +1 -1
  119. data/lib/contrast/funchook/funchook.rb +4 -3
  120. data/lib/contrast/logger/application.rb +1 -6
  121. data/lib/contrast/logger/log.rb +103 -13
  122. data/lib/contrast/logger/request.rb +0 -4
  123. data/lib/contrast/tasks/config.rb +0 -1
  124. data/lib/contrast/tasks/service.rb +1 -6
  125. data/lib/contrast/utils/assess/sampling_util.rb +2 -3
  126. data/lib/contrast/utils/assess/tracking_util.rb +2 -4
  127. data/lib/contrast/utils/class_util.rb +26 -19
  128. data/lib/contrast/utils/heap_dump_util.rb +5 -3
  129. data/lib/contrast/utils/invalid_configuration_util.rb +4 -3
  130. data/lib/contrast/utils/io_util.rb +46 -40
  131. data/lib/contrast/utils/job_servers_running.rb +4 -3
  132. data/lib/contrast/utils/lru_cache.rb +43 -0
  133. data/lib/contrast/utils/os.rb +2 -3
  134. data/lib/contrast/utils/ruby_ast_rewriter.rb +16 -13
  135. data/lib/contrast/utils/string_utils.rb +2 -3
  136. data/lib/contrast/utils/tag_util.rb +26 -19
  137. data/lib/contrast.rb +24 -14
  138. data/resources/assess/policy.json +197 -2
  139. data/resources/deadzone/policy.json +10 -0
  140. data/ruby-agent.gemspec +13 -3
  141. data/service_executables/VERSION +1 -1
  142. data/service_executables/linux/contrast-service +0 -0
  143. data/service_executables/mac/contrast-service +0 -0
  144. metadata +91 -25
  145. data/lib/contrast/components/interface.rb +0 -196
  146. data/lib/contrast/delegators/input_analysis.rb +0 -12
  147. data/lib/contrast/utils/inventory_util.rb +0 -114
@@ -5,21 +5,106 @@ require 'logger'
5
5
  require 'ougai'
6
6
  require 'singleton'
7
7
 
8
- require 'contrast/components/interface'
9
8
  require 'contrast/extension/module'
10
9
  require 'contrast/logger/application'
11
10
  require 'contrast/logger/format'
12
11
  require 'contrast/logger/request'
13
12
  require 'contrast/logger/time'
13
+ require 'contrast/components/config'
14
14
 
15
15
  module Contrast
16
+ # This module allows us to dynamically weave timing into our code, so that only when the time is actually needed do
17
+ # we pay the penalty for that timing block
18
+ module TraceTiming
19
+ def methods_to_time
20
+ @_methods_to_time ||= []
21
+ end
22
+
23
+ # Store info about methods for later patching.
24
+ METHOD_INFO = Struct.new(:clazz, :method_name, :custom_msg, :aliased)
25
+
26
+ # Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
27
+ # logger set to TRACE.
28
+ #
29
+ # @params: clazz [Class] the class of the method to time.
30
+ # @params: method [Symbol] the method to time.
31
+ # @params: method [String] optional custom logging message.
32
+ def add_method_to_trace_timing clazz, method, msg = nil
33
+ methods_to_time.append(METHOD_INFO.new(clazz, method, msg, false))
34
+ enable_trace_timing if logger.level == ::Ougai::Logging::TRACE
35
+ end
36
+
37
+ # Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
38
+ # logger set to TRACE.
39
+ #
40
+ # @params: method_spec [METHOD_INFO] specs about the method to be timed.
41
+ # @params: class_method [Boolean] whether this is or isn't a class/module method.
42
+ def trace_time_class_method meth_spec, class_method
43
+ untimed_func_symbol = "untimed_#{ meth_spec.method_name }".to_sym
44
+ send_to = class_method ? meth_spec.clazz.cs__singleton_class : meth_spec.clazz
45
+ meth_spec.clazz.class_eval do
46
+ include Contrast::Components::Logger::InstanceMethods
47
+ extend Contrast::Components::Logger::InstanceMethods
48
+
49
+ send_to.send(:alias_method, untimed_func_symbol, meth_spec.method_name)
50
+ meth_spec.aliased = true
51
+
52
+ log_message = "Elapsed time for #{ meth_spec.method_name }."
53
+ log_message = meth_spec.custom_message if meth_spec.custom_msg
54
+
55
+ send_to.send(:define_method, meth_spec.method_name) do |*args, **kwargs, &block| # rubocop:disable Performance/Kernel/DefineMethod
56
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
57
+ rv = if kwargs.empty?
58
+ send(untimed_func_symbol, *args, &block)
59
+ else
60
+ send(untimed_func_symbol, *args, **kwargs, &block)
61
+ end
62
+ delta = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
63
+ logger.trace(log_message, elapsed: delta * 1000)
64
+ rv
65
+ end
66
+ end
67
+ end
68
+
69
+ # Enable trace timing of methods specified in @_methods_to_time via aliasing.
70
+ def enable_trace_timing
71
+ methods_to_time.each do |meth_spec|
72
+ next if meth_spec.aliased
73
+
74
+ is_class_method = meth_spec.clazz.singleton_methods(false).include?(meth_spec.method_name)
75
+ trace_time_class_method meth_spec, is_class_method
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ module Contrast
82
+ # Used as a wrapper around our logging. The module option specifically adds in a new method for error that raises the
83
+ # logged exception, used in testing so that we can see if anything unexpected happens without it being swallowed
84
+ # while still providing safe options for customers.
16
85
  module Logger
17
- # This class functions to serve as a wrapper around our logging, as we need
18
- # to be able to dynamically update level based on updates to TeamServer.
86
+ # For development set following env var to raise logged exceptions instead of just logging.
87
+ if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
88
+ Ougai::Logger.class_eval do
89
+ alias_method :cs__error, :error
90
+ alias_method :cs__warn, :warn
91
+
92
+ def error *args, **kwargs
93
+ if kwargs.empty?
94
+ cs__error(*args)
95
+ else
96
+ cs__error(*args, **kwargs)
97
+ end
98
+ args.each { |arg| raise arg if arg && arg.cs__class < Exception }
99
+ end
100
+ end
101
+ end
102
+
103
+ # This class functions to serve as a wrapper around our logging, as we need to be able to dynamically update
104
+ # level based on updates to TeamServer.
19
105
  class Log
20
106
  include Singleton
21
- include Contrast::Components::Interface
22
- access_component :config
107
+ include ::Contrast::TraceTiming
23
108
 
24
109
  DEFAULT_NAME = 'contrast.log'
25
110
  DEFAULT_LEVEL = ::Ougai::Logging::Severity::INFO
@@ -33,8 +118,8 @@ module Contrast
33
118
  update
34
119
  end
35
120
 
36
- # Given new settings from TeamServer, update our logging to use the new
37
- # file and level, assuming they weren't set by local configuration.
121
+ # Given new settings from TeamServer, update our logging to use the new file and level, assuming they weren't
122
+ # set by local configuration.
38
123
  #
39
124
  # @param log_file [String] the file to which to log, as provided by TeamServer settings
40
125
  # @param log_level [String] the level at which to log, as provided by TeamServer settings
@@ -51,6 +136,8 @@ module Contrast
51
136
  @previous_path = current_path
52
137
  @previous_level = current_level_const
53
138
 
139
+ enable_trace_timing if current_level_const == ::Ougai::Logging::TRACE
140
+
54
141
  @_logger = build(path: current_path, level_const: current_level_const)
55
142
  # If we're logging to a new path, then let's start it w/ our helpful
56
143
  # data gathering messages
@@ -60,6 +147,9 @@ module Contrast
60
147
  logger.error('Unable to process update to LoggerManager.', e)
61
148
  else
62
149
  puts 'Unable to process update to LoggerManager.'
150
+ raise e if ENV['CONTRAST__AGENT__RUBY_MORE_COWBELL']
151
+
152
+ puts e.message
63
153
  puts e.backtrace.join("\n")
64
154
  end
65
155
  end
@@ -107,8 +197,8 @@ module Contrast
107
197
  # TeamServer.
108
198
  # @return [String] the path to which to log or STDOUT / STDERR if one of those values provided.
109
199
  def find_valid_path log_file
110
- config = CONFIG.root.agent.logger
111
- config_path = config.path&.length.to_i.positive? ? config.path : nil
200
+ config = ::Contrast::CONFIG.root.agent.logger
201
+ config_path = config&.path&.length.to_i.positive? ? config.path : nil
112
202
  valid_path(config_path || log_file)
113
203
  end
114
204
 
@@ -141,8 +231,9 @@ module Contrast
141
231
  # TeamServer.
142
232
  # @return [::Ougai::Logging::Severity] the level at which to log
143
233
  def find_valid_level log_level
144
- config = CONFIG.root.agent.logger
145
- config_level = config.level&.length&.positive? ? config.level : nil
234
+ config = ::Contrast::CONFIG.root.agent.logger
235
+ config_level = config&.level&.length&.positive? ? config.level : nil
236
+
146
237
  valid_level(config_level || log_level)
147
238
  end
148
239
 
@@ -158,8 +249,7 @@ module Contrast
158
249
  DEFAULT_LEVEL
159
250
  end
160
251
 
161
- # Log that the Agent log has changed and include some default information
162
- # at the start of the log.
252
+ # Log that the Agent log has changed and include some default information at the start of the log.
163
253
  def log_update
164
254
  logger.debug('Initialized new contrast agent logger')
165
255
  logger.debug_with_time('middleware: log environment') do
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2021 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/interface'
5
4
  require 'contrast/utils/timer'
6
5
 
7
6
  module Contrast
@@ -9,9 +8,6 @@ module Contrast
9
8
  # Our decorator for the Ougai logger allowing for the logging of the
10
9
  # request lifecycle, used to provide context during troubleshooting.
11
10
  module Request
12
- include Contrast::Components::Interface
13
- access_component :config
14
-
15
11
  # Utility method to log the start of a request
16
12
  def request_start
17
13
  debug('Beginning request analysis')
@@ -7,7 +7,6 @@ module Contrast
7
7
  # A Rake task to generate a contrast_security.yaml file with some basic settings
8
8
  module Config
9
9
  extend Rake::DSL
10
- include Contrast::Components::Interface
11
10
  DEFAULT_CONFIG = {
12
11
  'api' => {
13
12
  'url' => 'Enter your Contrast URL ex: https://app.contrastsecurity.com/Contrast',
@@ -1,7 +1,6 @@
1
1
  # Copyright (c) 2021 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/interface'
5
4
  require 'contrast/utils/os'
6
5
 
7
6
  module Contrast
@@ -10,14 +9,10 @@ module Contrast
10
9
  # forked from the application
11
10
  module Service
12
11
  extend Rake::DSL
13
- include Contrast::Components::Interface
14
-
15
- access_component :contrast_service
16
-
17
12
  # Start the service if it is not already running
18
13
  def self.start_service
19
14
  puts 'Starting Contrast Service'
20
- service_log = CONTRAST_SERVICE.logger_path
15
+ service_log = ::Contrast::CONTRAST_SERVICE.logger_path
21
16
  if File.writable?(service_log)
22
17
  spawn('contrast_service', out: File::NULL, err: service_log)
23
18
  else
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'singleton'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/sampling'
6
6
 
7
7
  module Contrast
8
8
  module Utils
@@ -11,8 +11,7 @@ module Contrast
11
11
  class SamplingUtil
12
12
  include Singleton
13
13
 
14
- include Contrast::Components::Interface
15
- access_component :sampling
14
+ extend Contrast::Components::Sampling::InstanceMethods
16
15
 
17
16
  def initialize
18
17
  @requests = {}
@@ -2,7 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/assess/tracker'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/logger'
6
6
  require 'contrast/utils/duck_utils'
7
7
 
8
8
  module Contrast
@@ -10,9 +10,7 @@ module Contrast
10
10
  module Assess
11
11
  # TrackingUtil has methods for determining if a object is being tracked
12
12
  class TrackingUtil
13
- include Contrast::Components::Interface
14
-
15
- access_component :logging
13
+ extend Contrast::Components::Logger::InstanceMethods
16
14
 
17
15
  class << self
18
16
  # Public interface to our tracking check, isolating the internals
@@ -3,11 +3,13 @@
3
3
 
4
4
  require 'contrast/extension/module'
5
5
  require 'contrast/utils/object_share'
6
+ require 'contrast/utils/lru_cache'
6
7
 
7
8
  module Contrast
8
9
  module Utils
9
10
  # Utility methods for exploring the complete space of Objects
10
11
  class ClassUtil
12
+ @lru_cache = LRUCache.new
11
13
  class << self
12
14
  # some classes have had things prepended to them, like Marshal in Rails
13
15
  # 5 and higher. Their ActiveSupport::MarshalWithAutoloading will break
@@ -47,27 +49,32 @@ module Contrast
47
49
  # @param object [Object, nil] the entity to convert to a String
48
50
  # @return [String] the human readable form of the String, as defined by
49
51
  # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/vulnerability/capture-snapshot.md
52
+
50
53
  def to_contrast_string object
51
- # Only treat object like a string if it actually is a string
54
+ # After implementing the LRU Cache, we firstly need to check if already had that object cached
55
+ # and if we have it - we can return it directly
56
+ return @lru_cache[object.__id__] if @lru_cache.key? object.__id__
57
+
58
+ # Only treat object like a string if it actually is a string+
52
59
  # some subclasses of String override string methods we depend on
53
- if object.cs__class == String
54
- cached = to_cached_string(object)
55
- return cached if cached
56
-
57
- object.dup
58
- elsif object.nil?
59
- Contrast::Utils::ObjectShare::NIL_STRING
60
- elsif object.cs__is_a?(Symbol)
61
- ":#{ object }"
62
- elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
63
- "#{ object.cs__name }@#{ object.__id__ }"
64
- elsif object.cs__is_a?(Regexp)
65
- object.source
66
- elsif use_to_s?(object)
67
- object.to_s
68
- else
69
- "#{ object.cs__class.cs__name }@#{ object.__id__ }"
70
- end
60
+ @lru_cache[object.__id__] = if object.cs__class == String
61
+ cached = to_cached_string(object)
62
+ return cached if cached
63
+
64
+ object.dup
65
+ elsif object.nil?
66
+ Contrast::Utils::ObjectShare::NIL_STRING
67
+ elsif object.cs__is_a?(Symbol)
68
+ ":#{ object }"
69
+ elsif object.cs__is_a?(Module) || object.cs__is_a?(Class)
70
+ "#{ object.cs__name }@#{ object.__id__ }"
71
+ elsif object.cs__is_a?(Regexp)
72
+ object.source
73
+ elsif use_to_s?(object)
74
+ object.to_s
75
+ else
76
+ "#{ object.cs__class.cs__name }@#{ object.__id__ }"
77
+ end
71
78
  end
72
79
 
73
80
  # The method const_defined? can cause autoload, which is bad for us.
@@ -3,14 +3,16 @@
3
3
 
4
4
  require 'objspace'
5
5
  require 'singleton'
6
- require 'contrast/components/interface'
6
+ require 'contrast/components/heap_dump'
7
+ require 'contrast/components/logger'
7
8
 
8
9
  module Contrast
9
10
  module Utils
10
11
  # Implementation of a heap dump util to automate generation
11
12
  class HeapDumpUtil < Contrast::Agent::WorkerThread
12
- include Contrast::Components::Interface
13
- access_component :heap_dump, :logging
13
+ extend Contrast::Components::Logger::InstanceMethods
14
+ include Contrast::Components::Logger::InstanceMethods
15
+ extend Contrast::Components::HeapDump::InstanceMethods
14
16
 
15
17
  LOG_ERROR_DUMPS = 'Unable to generate heap dumps'
16
18
  FILE_WRITE_FLAGS = 'w'
@@ -2,15 +2,16 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/assess/policy/trigger_method'
5
- require 'contrast/components/interface'
5
+ require 'contrast/components/logger'
6
+ require 'contrast/components/scope'
6
7
 
7
8
  module Contrast
8
9
  module Utils
9
10
  # This utility allows us to report invalid configurations detected in
10
11
  # customer applications, as determined by Configuration Rules at runtime.
11
12
  module InvalidConfigurationUtil
12
- include Contrast::Components::Interface
13
- access_component :analysis, :app_context, :logging, :scope
13
+ include Contrast::Components::Logger::InstanceMethods
14
+ include Contrast::Components::Scope::InstanceMethods
14
15
 
15
16
  CS__PATH = 'path'
16
17
  CS__SESSION_ID = 'sessionId'
@@ -1,50 +1,56 @@
1
1
  # Copyright (c) 2021 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/interface'
4
+ require 'contrast/components/logger'
5
5
 
6
6
  module Contrast
7
7
  module Utils
8
8
  # Util for information about an IO
9
- class IOUtil
10
- include Contrast::Components::Interface
11
-
12
- access_component :logging
13
-
14
- # We're only going to call rewind on things that we believe are safe to
15
- # call it on. This method white lists those cases and returns false in
16
- # all others.
17
- def self.should_rewind? potential_io
18
- return true if potential_io.is_a?(StringIO)
19
- return false unless io?(potential_io)
20
-
21
- should_rewind_io?(potential_io)
22
- rescue StandardError => e
23
- logger.debug('Encountered an issue determining if rewindable', e, module: potential_io.cs__class.cs__name)
24
- false
25
- end
26
-
27
- # IO cannot be used with streams such as pipes, ttys, and sockets.
28
- def self.should_rewind_io? io
29
- return false if io.tty?
30
-
31
- status = io.stat
32
- return false unless status
33
- return false if status.pipe?
34
- return false if status.socket?
35
-
36
- true
37
- end
38
-
39
- # A class is IO if it is a decedent or delegate of IO
40
- def self.io? object
41
- return true if object.is_a?(IO)
42
-
43
- # DelegateClass, which is a Delegator, defines __getobj__ as a way to
44
- # get the object that the class wraps around (delegates to)
45
- return false unless object.is_a?(Delegator)
46
-
47
- object.__getobj__.is_a?(IO)
9
+ module IOUtil
10
+ extend Contrast::Components::Logger::InstanceMethods
11
+
12
+ class << self
13
+ # We're only going to call rewind on things that we believe are safe to
14
+ # call it on. This method white lists those cases and returns false in
15
+ # all others.
16
+ def should_rewind? potential_io
17
+ return true if potential_io.is_a?(StringIO)
18
+ return false unless io?(potential_io)
19
+
20
+ should_rewind_io?(potential_io)
21
+ rescue StandardError => e
22
+ logger.debug('Encountered an issue determining if rewindable', e, module: potential_io.cs__class.cs__name)
23
+ false
24
+ end
25
+
26
+ # A class is IO if it is a decedent or delegate of IO
27
+ def io? object
28
+ return true if object.is_a?(IO)
29
+
30
+ # DelegateClass, which is a Delegator, defines __getobj__ as a way to
31
+ # get the object that the class wraps around (delegates to)
32
+ return false unless object.is_a?(Delegator)
33
+
34
+ object.__getobj__.is_a?(IO)
35
+ end
36
+
37
+ private
38
+
39
+ # IO rewind cannot be used with streams such as pipes, ttys, and sockets or for ones which have been closed.
40
+ #
41
+ # @param io [IO] the input to check for the ability to rewind
42
+ # @return [Boolean] if the given IO can be rewound
43
+ def should_rewind_io? io
44
+ return false if io.closed?
45
+ return false if io.tty?
46
+
47
+ status = io.stat
48
+ return false unless status
49
+ return false if status.pipe?
50
+ return false if status.socket?
51
+
52
+ true
53
+ end
48
54
  end
49
55
  end
50
56
  end
@@ -1,13 +1,14 @@
1
1
  # Copyright (c) 2021 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/logger'
5
+
4
6
  module Contrast
5
7
  module Utils
6
8
  # A module that detects whether any job servers attached to
7
9
  # the application are running
8
10
  module JobServersRunning
9
- include Contrast::Components::Interface
10
- access_component :app_context, :logging
11
+ extend Contrast::Components::Logger::InstanceMethods
11
12
 
12
13
  class << self
13
14
  def job_servers_running?
@@ -31,7 +32,7 @@ module Contrast
31
32
  return
32
33
  end
33
34
 
34
- disabled_rake_tasks = APP_CONTEXT.disabled_agent_rake_tasks
35
+ disabled_rake_tasks = ::Contrast::APP_CONTEXT.disabled_agent_rake_tasks
35
36
  has_disabled_task = Rake.application.top_level_tasks.any? do |top_level_task|
36
37
  disabled_rake_tasks.include?(top_level_task)
37
38
  end
@@ -0,0 +1,43 @@
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ require 'contrast/components/logger'
5
+
6
+ module Contrast
7
+ module Utils
8
+ # A LRU(Least Recently Used) Cache store.
9
+ class LRUCache
10
+ def initialize capacity = 500
11
+ raise StandardError 'Capacity must be bigger than 0' if capacity <= 0
12
+
13
+ @capacity = capacity
14
+ @cache = {}
15
+ end
16
+
17
+ def [] key
18
+ val = @cache.delete(key)
19
+ @cache[key] = val if val
20
+ val
21
+ end
22
+
23
+ def []= key, value
24
+ @cache.delete(key)
25
+ @cache[key] = value
26
+ @cache.shift if @cache.size > @capacity
27
+ value # rubocop:disable Lint/Void
28
+ end
29
+
30
+ def keys
31
+ @cache.keys
32
+ end
33
+
34
+ def key? key
35
+ @cache.key?(key)
36
+ end
37
+
38
+ def values
39
+ @cache.values
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,7 +1,7 @@
1
1
  # Copyright (c) 2021 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/interface'
4
+ require 'contrast/components/scope'
5
5
 
6
6
  module Contrast
7
7
  module Utils
@@ -9,8 +9,7 @@ module Contrast
9
9
  # which will not change at runtime, such as the operating system, the
10
10
  # Utility memozies to avoid multiple lookups.
11
11
  module OS
12
- include Contrast::Components::Interface
13
- access_component :scope
12
+ extend Contrast::Components::Scope::InstanceMethods
14
13
 
15
14
  class << self
16
15
  def running?
@@ -36,36 +36,39 @@ module Contrast
36
36
  # the replace within the given node.
37
37
  def on_dstr node
38
38
  return if node.children.all? { |child_node| child_node.type == :str }
39
-
40
39
  new_content = +'('
41
- node.children.each_with_index do |child_node, index|
40
+ idx = 0
41
+ while idx < node.children.length
42
+ #node.children.each_with_index do |child_node, index|
43
+ child_node = node.children[idx]
42
44
  # A begin node looks like #{some_code} in ruby, we do a substring
43
45
  # from [2...-1] to get rid of the #{ & trailing }.
44
46
  if child_node.type == :begin
45
47
  code = child_node.
46
- location.
47
- expression.
48
- source_buffer.
49
- source[child_node.location.begin.begin_pos...child_node.location.end.end_pos]
48
+ location.
49
+ expression.
50
+ source_buffer.
51
+ source[child_node.location.begin.begin_pos...child_node.location.end.end_pos]
50
52
  code = code[2...-1]
51
53
  new_content += "((#{ code }).to_s)"
52
54
 
53
- # For interpolations that use class, instance, or global variables,
54
- # those are NOT within a begin block, but instead are a ivar, cvar,
55
- # or gvar node, not stripping of interpolation markers required.
55
+ # For interpolations that use class, instance, or global variables,
56
+ # those are NOT within a begin block, but instead are a ivar, cvar,
57
+ # or gvar node, not stripping of interpolation markers required.
56
58
  elsif VARIABLES.include?(child_node.type)
57
59
  variable = child_node.children.first
58
60
  new_content << "((#{ variable }).to_s)"
59
61
 
60
- # When interpolation includes strings before or after an
61
- # interpolation they are simple :str nodes, in order to preserve
62
- # escaping we need to do a dump of the string value.
62
+ # When interpolation includes strings before or after an
63
+ # interpolation they are simple :str nodes, in order to preserve
64
+ # escaping we need to do a dump of the string value.
63
65
  elsif child_node.type == :str
64
66
  literal_value = child_node.children.first
65
67
  literal_value = literal_value.dump[1...-1]
66
68
  new_content << "\"#{ literal_value }\""
67
69
  end
68
- new_content << ' + ' unless index == node.children.length - 1
70
+ new_content << ' + ' unless idx == node.children.length - 1
71
+ idx += 1
69
72
  end
70
73
  new_content << ')'
71
74
  if node.location.cs__respond_to?(:heredoc_body)
@@ -1,14 +1,13 @@
1
1
  # Copyright (c) 2021 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/interface'
4
+ require 'contrast/components/logger'
5
5
 
6
6
  module Contrast
7
7
  module Utils
8
8
  # Utilities for encoding and normalizing strings
9
9
  class StringUtils
10
- include Contrast::Components::Interface
11
- access_component :logging
10
+ include Contrast::Components::Logger::InstanceMethods
12
11
 
13
12
  UTF8 = 'utf-8'
14
13
  HTTP_PREFIX = 'HTTP_'