contrast-agent 4.11.0 → 4.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +1 -0
  3. data/ext/cs__assess_module/cs__assess_module.c +48 -0
  4. data/ext/cs__assess_module/cs__assess_module.h +7 -0
  5. data/ext/cs__common/cs__common.c +24 -7
  6. data/ext/cs__common/cs__common.h +12 -2
  7. data/ext/cs__contrast_patch/cs__contrast_patch.c +48 -11
  8. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -2
  9. data/ext/cs__os_information/cs__os_information.c +31 -0
  10. data/ext/cs__os_information/cs__os_information.h +7 -0
  11. data/ext/{cs__protect_kernel → cs__os_information}/extconf.rb +0 -0
  12. data/lib/contrast/agent/assess/contrast_event.rb +1 -1
  13. data/lib/contrast/agent/assess/contrast_object.rb +1 -1
  14. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +2 -0
  15. data/lib/contrast/agent/assess/policy/policy_node.rb +6 -6
  16. data/lib/contrast/agent/assess/policy/policy_scanner.rb +5 -0
  17. data/lib/contrast/agent/assess/policy/preshift.rb +19 -6
  18. data/lib/contrast/agent/assess/policy/propagation_method.rb +2 -116
  19. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  20. data/lib/contrast/agent/assess/policy/propagator/center.rb +1 -1
  21. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +2 -0
  22. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +2 -154
  23. data/lib/contrast/agent/assess/policy/source_method.rb +2 -71
  24. data/lib/contrast/agent/assess/policy/trigger_method.rb +45 -110
  25. data/lib/contrast/agent/assess/policy/trigger_node.rb +62 -21
  26. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +1 -1
  27. data/lib/contrast/agent/assess/property/tagged.rb +66 -189
  28. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +40 -6
  29. data/lib/contrast/agent/deadzone/policy/policy.rb +6 -0
  30. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +1 -0
  31. data/lib/contrast/agent/metric_telemetry_event.rb +26 -0
  32. data/lib/contrast/agent/middleware.rb +14 -62
  33. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +0 -1
  34. data/lib/contrast/agent/patching/policy/method_policy.rb +3 -44
  35. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +111 -0
  36. data/lib/contrast/agent/patching/policy/patch.rb +37 -238
  37. data/lib/contrast/agent/patching/policy/patcher.rb +15 -50
  38. data/lib/contrast/agent/reporting/report.rb +21 -0
  39. data/lib/contrast/agent/reporting/reporter.rb +142 -0
  40. data/lib/contrast/agent/reporting/reporting_events/finding.rb +90 -0
  41. data/lib/contrast/agent/reporting/reporting_events/preflight.rb +25 -0
  42. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +56 -0
  43. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +37 -0
  44. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +127 -0
  45. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +168 -0
  46. data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +66 -0
  47. data/lib/contrast/agent/request.rb +2 -81
  48. data/lib/contrast/agent/request_context.rb +18 -126
  49. data/lib/contrast/agent/request_context_extend.rb +138 -0
  50. data/lib/contrast/agent/request_handler.rb +7 -3
  51. data/lib/contrast/agent/response.rb +2 -73
  52. data/lib/contrast/agent/rule_set.rb +2 -4
  53. data/lib/contrast/agent/startup_metrics_telemetry_event.rb +94 -0
  54. data/lib/contrast/agent/static_analysis.rb +5 -3
  55. data/lib/contrast/agent/telemetry.rb +137 -0
  56. data/lib/contrast/agent/telemetry_event.rb +33 -0
  57. data/lib/contrast/agent/thread_watcher.rb +66 -11
  58. data/lib/contrast/agent/version.rb +1 -1
  59. data/lib/contrast/agent.rb +21 -1
  60. data/lib/contrast/api/communication/connection_status.rb +10 -7
  61. data/lib/contrast/api/communication/messaging_queue.rb +37 -3
  62. data/lib/contrast/api/communication/response_processor.rb +15 -8
  63. data/lib/contrast/api/communication/service_lifecycle.rb +13 -3
  64. data/lib/contrast/api/communication/socket.rb +6 -8
  65. data/lib/contrast/api/communication/socket_client.rb +29 -12
  66. data/lib/contrast/api/communication/speedracer.rb +37 -1
  67. data/lib/contrast/api/communication/tcp_socket.rb +4 -3
  68. data/lib/contrast/api/communication/unix_socket.rb +1 -0
  69. data/lib/contrast/api/decorators/finding.rb +45 -0
  70. data/lib/contrast/components/api.rb +90 -0
  71. data/lib/contrast/components/app_context.rb +10 -41
  72. data/lib/contrast/components/app_context_extend.rb +78 -0
  73. data/lib/contrast/components/assess.rb +7 -0
  74. data/lib/contrast/components/base.rb +23 -0
  75. data/lib/contrast/components/config.rb +92 -13
  76. data/lib/contrast/components/contrast_service.rb +11 -0
  77. data/lib/contrast/components/sampling.rb +2 -2
  78. data/lib/contrast/config/agent_configuration.rb +1 -1
  79. data/lib/contrast/config/api_configuration.rb +27 -0
  80. data/lib/contrast/config/api_proxy_configuration.rb +14 -0
  81. data/lib/contrast/config/application_configuration.rb +2 -3
  82. data/lib/contrast/config/assess_configuration.rb +3 -2
  83. data/lib/contrast/config/base_configuration.rb +17 -28
  84. data/lib/contrast/config/certification_configuration.rb +15 -0
  85. data/lib/contrast/config/env_variables.rb +18 -0
  86. data/lib/contrast/config/heap_dump_configuration.rb +6 -6
  87. data/lib/contrast/config/inventory_configuration.rb +1 -5
  88. data/lib/contrast/config/protect_rule_configuration.rb +1 -1
  89. data/lib/contrast/config/request_audit_configuration.rb +18 -0
  90. data/lib/contrast/config/root_configuration.rb +1 -0
  91. data/lib/contrast/config/ruby_configuration.rb +6 -6
  92. data/lib/contrast/config/service_configuration.rb +2 -2
  93. data/lib/contrast/config.rb +1 -1
  94. data/lib/contrast/configuration.rb +4 -2
  95. data/lib/contrast/extension/assess/array.rb +5 -7
  96. data/lib/contrast/framework/manager.rb +22 -44
  97. data/lib/contrast/framework/manager_extend.rb +50 -0
  98. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +9 -6
  99. data/lib/contrast/framework/rails/patch/support.rb +31 -29
  100. data/lib/contrast/framework/rails/railtie.rb +1 -1
  101. data/lib/contrast/framework/sinatra/support.rb +2 -1
  102. data/lib/contrast/logger/application.rb +4 -0
  103. data/lib/contrast/logger/log.rb +8 -103
  104. data/lib/contrast/utils/assess/propagation_method_utils.rb +129 -0
  105. data/lib/contrast/utils/assess/property/tagged_utils.rb +165 -0
  106. data/lib/contrast/utils/assess/source_method_utils.rb +83 -0
  107. data/lib/contrast/utils/assess/tracking_util.rb +20 -15
  108. data/lib/contrast/utils/assess/trigger_method_utils.rb +138 -0
  109. data/lib/contrast/utils/class_util.rb +65 -54
  110. data/lib/contrast/utils/exclude_key.rb +20 -0
  111. data/lib/contrast/utils/findings.rb +62 -0
  112. data/lib/contrast/utils/hash_digest.rb +10 -73
  113. data/lib/contrast/utils/hash_digest_extend.rb +86 -0
  114. data/lib/contrast/utils/head_dump_utils_extend.rb +74 -0
  115. data/lib/contrast/utils/heap_dump_util.rb +2 -65
  116. data/lib/contrast/utils/invalid_configuration_util.rb +29 -0
  117. data/lib/contrast/utils/io_util.rb +1 -1
  118. data/lib/contrast/utils/log_utils.rb +108 -0
  119. data/lib/contrast/utils/lru_cache.rb +4 -2
  120. data/lib/contrast/utils/metrics_hash.rb +59 -0
  121. data/lib/contrast/utils/middleware_utils.rb +87 -0
  122. data/lib/contrast/utils/net_http_base.rb +158 -0
  123. data/lib/contrast/utils/object_share.rb +1 -0
  124. data/lib/contrast/utils/os.rb +23 -0
  125. data/lib/contrast/utils/patching/policy/patch_utils.rb +232 -0
  126. data/lib/contrast/utils/patching/policy/patcher_utils.rb +54 -0
  127. data/lib/contrast/utils/request_utils.rb +88 -0
  128. data/lib/contrast/utils/response_utils.rb +97 -0
  129. data/lib/contrast/utils/substitution_utils.rb +167 -0
  130. data/lib/contrast/utils/tag_util.rb +9 -9
  131. data/lib/contrast/utils/telemetry.rb +79 -0
  132. data/lib/contrast/utils/telemetry_client.rb +90 -0
  133. data/lib/contrast/utils/telemetry_identifier.rb +130 -0
  134. data/lib/contrast.rb +19 -1
  135. data/resources/assess/policy.json +12 -6
  136. data/resources/deadzone/policy.json +86 -5
  137. data/ruby-agent.gemspec +7 -6
  138. data/service_executables/VERSION +1 -1
  139. data/service_executables/linux/contrast-service +0 -0
  140. data/service_executables/mac/contrast-service +0 -0
  141. metadata +68 -26
  142. data/ext/cs__protect_kernel/cs__protect_kernel.c +0 -47
  143. data/ext/cs__protect_kernel/cs__protect_kernel.h +0 -12
  144. data/lib/contrast/config/default_value.rb +0 -17
  145. data/lib/contrast/extension/protect/kernel.rb +0 -29
@@ -9,16 +9,18 @@ require 'contrast/framework/rails/support'
9
9
  require 'contrast/framework/grape/support'
10
10
  require 'contrast/framework/sinatra/support'
11
11
  require 'contrast/utils/class_util'
12
+ require 'contrast/framework/manager_extend'
12
13
 
13
14
  module Contrast
14
15
  module Framework
15
16
  # Allows access to framework specific information
16
17
  class Manager
17
18
  include Contrast::Components::Logger::InstanceMethods
19
+ include Contrast::Framework::ManagerExtend
18
20
 
19
- # Order here does matter as the first framework listed will be the first one we pull information from
20
- # Rack will be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra
21
- # do not exist
21
+ # Order here does matter as the first framework listed will be the first one we pull information from Rack will
22
+ # be a special case that may involve updating some logic to handle only applying Rack if Rails/Sinatra do not
23
+ # exist
22
24
  SUPPORTED_FRAMEWORKS = [
23
25
  Contrast::Framework::Rails::Support, Contrast::Framework::Sinatra::Support,
24
26
  Contrast::Framework::Grape::Support, Contrast::Framework::Rack::Support
@@ -34,9 +36,8 @@ module Contrast
34
36
  @_frameworks.compact!
35
37
  end
36
38
 
37
- # Patches that have to be applied as early as possible to catch calls
38
- # that happen prior to the first Request, typically those around
39
- # configuration.
39
+ # Patches that have to be applied as early as possible to catch calls that happen prior to the first Request,
40
+ # typically those around configuration.
40
41
  def before_load_patches!
41
42
  @_before_load_patches ||= begin
42
43
  SUPPORTED_FRAMEWORKS.each(&:before_load_patches!)
@@ -66,6 +67,10 @@ module Contrast
66
67
  Contrast::Framework::PlatformVersion.from_string(framework_version)
67
68
  end
68
69
 
70
+ def platform_version_string
71
+ first_framework_result :version, ''
72
+ end
73
+
69
74
  def server_type
70
75
  first_framework_result :server_type, 'rack'
71
76
  end
@@ -81,8 +86,8 @@ module Contrast
81
86
 
82
87
  # Build a request from the provided env, based on the framework(s) we're currently supporting.
83
88
  #
84
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values
85
- # of this particular Request
89
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
90
+ # this particular Request
86
91
  # @return [::Rack::Request] either a rack request or subclass thereof.
87
92
  def retrieve_request env
88
93
  # If we're mounted on Rails, use Rails.
@@ -99,8 +104,8 @@ module Contrast
99
104
  logger.warn('Unable to retrieve_request', e)
100
105
  end
101
106
 
102
- # @param env [Hash] the various variables stored by this and other Middlewares to know the state
103
- # and values of this particular Request
107
+ # @param env [Hash] the various variables stored by this and other Middlewares to know the state and values of
108
+ # this particular Request
104
109
  # @return [Boolean] true if at least one framework is streaming the response; false if none are streaming
105
110
  def streaming? env
106
111
  result = false
@@ -111,13 +116,14 @@ module Contrast
111
116
  result
112
117
  end
113
118
 
114
- # Iterate through current frameworks and return the current request's route. This will be the first
115
- # non-nil result.
119
+ # Iterate through current frameworks and return the current request's route. This will be the first non-nil
120
+ # result.
116
121
  #
117
122
  # @param request [Contrast::Agent::Request] the current request.
118
123
  # @return [Contrast::Api::Dtm::RouteCoverage] the current route as a Dtm.
119
124
  def get_route_dtm request
120
- @_frameworks.lazy.map { |framework_support| framework_support.current_route(request) }.reject(&:nil?).first
125
+ @_frameworks.lazy.map { |framework_support| framework_support.current_route(request) }.
126
+ reject(&:nil?).first
121
127
  end
122
128
 
123
129
  # Sometimes the framework we want to instrument is loaded after our agent code. To catch that case, we'll detect
@@ -125,6 +131,9 @@ module Contrast
125
131
  # have support enabled, we'll enable it now. We'll also need to catch up on any other startup actions that we've
126
132
  # missed. Most likely, this is only necessary for those applications which have applications mounted on them.
127
133
  #
134
+ # TODO: RUBY-1354
135
+ # TODO: RUBY-1356
136
+ #
128
137
  # @param mod [Module] the module or class that was just loaded
129
138
  def register_late_framework mod
130
139
  return unless mod
@@ -147,37 +156,6 @@ module Contrast
147
156
  rescue StandardError => e
148
157
  logger.warn('Unable to register a late framework', e, module: mod.cs__name)
149
158
  end
150
-
151
- private
152
-
153
- def enable_framework_support? klass
154
- Contrast::Utils::ClassUtil.truly_defined?(klass)
155
- end
156
-
157
- def routes_for_all_frameworks
158
- data_for_all_frameworks :collect_routes
159
- end
160
-
161
- # This returns an array of all data from each framework in a flat, no-nil values array
162
- #
163
- # @param method_name [Symbol] the method to call on each FrameworkSupport class
164
- # @return [Array]
165
- def data_for_all_frameworks method_name
166
- @_frameworks.flat_map { |framework| framework.send(method_name) }.compact
167
- end
168
-
169
- # This returns a single object from the first framework to successfully respond
170
- #
171
- # @param method_name [Symbol] the method to call on each FrameworkSupport class
172
- # @return [Object] - Determined by method to be invoked
173
- def first_framework_result method_name, default_value
174
- result = nil
175
- @_frameworks.each do |framework|
176
- result = framework.send(method_name)
177
- break if result
178
- end
179
- result || default_value
180
- end
181
159
  end
182
160
  end
183
161
  end
@@ -0,0 +1,50 @@
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
+ require 'contrast/extension/module'
6
+ require 'contrast/framework/platform_version'
7
+ require 'contrast/framework/rack/support'
8
+ require 'contrast/framework/rails/support'
9
+ require 'contrast/framework/grape/support'
10
+ require 'contrast/framework/sinatra/support'
11
+ require 'contrast/utils/class_util'
12
+
13
+ module Contrast
14
+ module Framework
15
+ # Allows access to framework specific information
16
+ module ManagerExtend
17
+ private
18
+
19
+ def enable_framework_support? klass
20
+ Contrast::Utils::ClassUtil.truly_defined?(klass)
21
+ end
22
+
23
+ def routes_for_all_frameworks
24
+ data_for_all_frameworks :collect_routes
25
+ end
26
+
27
+ # This returns an array of all data from each framework in a flat, no-nil values array
28
+ #
29
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
30
+ # @return [Array]
31
+ def data_for_all_frameworks method_name
32
+ @_frameworks.flat_map { |framework| framework.send(method_name) }.
33
+ compact
34
+ end
35
+
36
+ # This returns a single object from the first framework to successfully respond
37
+ #
38
+ # @param method_name [Symbol] the method to call on each FrameworkSupport class
39
+ # @return [Object] - Determined by method to be invoked
40
+ def first_framework_result method_name, default_value
41
+ result = nil
42
+ @_frameworks.each do |framework|
43
+ result = framework.send(method_name)
44
+ break if result
45
+ end
46
+ result || default_value
47
+ end
48
+ end
49
+ end
50
+ end
@@ -5,10 +5,14 @@ module Contrast
5
5
  module Framework
6
6
  module Rails
7
7
  module Patch
8
- # This class acts as our patch into the ActionController::Live::Buffer
9
- # class, allowing us to track the close event on streamed responses.
8
+ # This class acts as our patch into the ActionController::Live::Buffer class, allowing us to track the close
9
+ # event on streamed responses.
10
10
  module ActionControllerLiveBuffer
11
11
  class << self
12
+ # TODO: RUBY-1353
13
+ # TODO: RUBY-1355
14
+ # TODO: RUBY-1357
15
+ # TODO: RUBY-1357
12
16
  def send_messages
13
17
  return unless (context = Contrast::Agent::REQUEST_TRACKER.current)
14
18
 
@@ -20,10 +24,9 @@ module Contrast
20
24
  def instrument
21
25
  @_instrument ||= begin
22
26
  ::ActionController::Live::Buffer.class_eval do
23
- # normally pre->in->post filters are applied however, in a streamed response
24
- # we can run into a case where it's pre -> in -> post -> more infilters
25
- # in order to submit anything found during the infilters after the response has
26
- # been written we need to explicitly send them
27
+ # normally pre->in->post filters are applied however, in a streamed response we can run into a case
28
+ # where it's pre -> in -> post -> more infilters in order to submit anything found during the
29
+ # infilters after the response has been written we need to explicitly send them
27
30
  alias_method :cs__close, :close
28
31
  def close
29
32
  Contrast::Framework::Rails::Patch::ActionControllerLiveBuffer.send_messages
@@ -38,37 +38,39 @@ module Contrast
38
38
  instrumenting_module:
39
39
  'Contrast::Framework::Rails::Patch::RailsApplicationConfiguration')
40
40
  ])
41
- if RUBY_VERSION < '2.6.0'
42
- patches.merge([
43
- # TODO: RUBY-714 remove w/ EOL of 2.5
44
- #
45
- # @deprecated Everything past here is used for Rewriting and can
46
- # be removed once we no longer support 2.5.
47
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
48
- 'ActionController::Railties::Helper::ClassMethods',
49
- 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
50
- method_to_instrument: :inherited,
51
- instrumenting_module:
52
- 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
53
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
54
- 'ActiveRecord::AttributeMethods::Read::ClassMethods',
55
- 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
56
- instrumenting_module:
57
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
58
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
59
- 'ActiveRecord::Scoping::Named::ClassMethods',
60
- 'contrast/framework/rails/rewrite/active_record_named',
61
- instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
62
- Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
- 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
64
- 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
65
- method_to_instrument: :inherited,
66
- instrumenting_module:
67
- 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
68
- ])
69
- end
41
+ patches.merge(special_after_load_patches) if RUBY_VERSION < '2.6.0'
70
42
  patches
71
43
  end
44
+
45
+ def special_after_load_patches
46
+ [
47
+ # TODO: RUBY-714 remove w/ EOL of 2.5
48
+ #
49
+ # @deprecated Everything past here is used for Rewriting and can
50
+ # be removed once we no longer support 2.5.
51
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
52
+ 'ActionController::Railties::Helper::ClassMethods',
53
+ 'contrast/framework/rails/rewrite/action_controller_railties_helper_inherited',
54
+ method_to_instrument: :inherited,
55
+ instrumenting_module:
56
+ 'Contrast::Framework::Rails::Rewrite::ActionControllerRailtiesHelperInherited'),
57
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
58
+ 'ActiveRecord::AttributeMethods::Read::ClassMethods',
59
+ 'contrast/framework/rails/rewrite/active_record_attribute_methods_read',
60
+ instrumenting_module:
61
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordAttributeMethodsRead'),
62
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
63
+ 'ActiveRecord::Scoping::Named::ClassMethods',
64
+ 'contrast/framework/rails/rewrite/active_record_named',
65
+ instrumenting_module: 'Contrast::Framework::Rails::Rewrite::ActiveRecordNamed'),
66
+ Contrast::Agent::Patching::Policy::AfterLoadPatch.new(
67
+ 'ActiveRecord::AttributeMethods::TimeZoneConversion::ClassMethods',
68
+ 'contrast/framework/rails/rewrite/active_record_time_zone_inherited',
69
+ method_to_instrument: :inherited,
70
+ instrumenting_module:
71
+ 'Contrast::Framework::Rails::Rewrite::ActiveRecordTimeZoneInherited')
72
+ ]
73
+ end
72
74
  end
73
75
  end
74
76
  end
@@ -14,7 +14,7 @@ module Contrast
14
14
  initializer 'Contrast Ruby Agent Initializer' do |app|
15
15
  log_rails = defined?(Rails) && defined?(Rails.logger)
16
16
 
17
- Rails.logger.debug("In railtie ::#{ app.middleware.inspect }") if log_rails
17
+ Rails.logger.debug { "In railtie ::#{ app.middleware.inspect }" } if log_rails
18
18
 
19
19
  if ::Contrast::APP_CONTEXT.instrument_middleware_stack?
20
20
  ::Contrast::AGENT.insert_middleware(app)
@@ -106,7 +106,8 @@ module Contrast
106
106
  def _route_recurse controller, method, route
107
107
  return if controller.nil? || controller.cs__class == NilClass
108
108
 
109
- route_patterns = controller.routes.fetch(method) { [] }.map(&:first)
109
+ route_patterns = controller.routes.fetch(method) { [] }.
110
+ map(&:first)
110
111
  route_pattern = route_patterns&.find do |matcher|
111
112
  matcher.params(route) # ::Mustermann::Sinatra match.
112
113
  end
@@ -1,6 +1,8 @@
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/utils/exclude_key'
5
+
4
6
  module Contrast
5
7
  module Logger
6
8
  # Our decorator for the Ougai logger allowing for the logging of the
@@ -29,6 +31,8 @@ module Contrast
29
31
  loggable = ::Contrast::CONFIG.loggable
30
32
  info('Current configuration', configuration: loggable)
31
33
  env_keys = ENV.keys.select do |env_key|
34
+ next if Contrast::Utils::ExcludeKey.excludable? env_key.to_s
35
+
32
36
  env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER)
33
37
  end
34
38
  env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
@@ -11,6 +11,7 @@ require 'contrast/logger/format'
11
11
  require 'contrast/logger/request'
12
12
  require 'contrast/logger/time'
13
13
  require 'contrast/components/config'
14
+ require 'contrast/utils/log_utils'
14
15
 
15
16
  module Contrast
16
17
  # This module allows us to dynamically weave timing into our code, so that only when the time is actually needed do
@@ -26,9 +27,9 @@ module Contrast
26
27
  # Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
27
28
  # logger set to TRACE.
28
29
  #
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.
30
+ # @param: clazz [Class] the class of the method to time.
31
+ # @param: method [Symbol] the method to time.
32
+ # @param: method [String] optional custom logging message.
32
33
  def add_method_to_trace_timing clazz, method, msg = nil
33
34
  methods_to_time.append(METHOD_INFO.new(clazz, method, msg, false))
34
35
  enable_trace_timing if logger.level == ::Ougai::Logging::TRACE
@@ -37,9 +38,9 @@ module Contrast
37
38
  # Add a method to the list of methods to be trace timed if logger set to TRACE. Enables trace timing after if
38
39
  # logger set to TRACE.
39
40
  #
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
41
+ # @param: meth_spec [METHOD_INFO] specs about the method to be timed.
42
+ # @param: class_method [Boolean] whether this is or isn't a class/module method.
43
+ def trace_time_class_method meth_spec, class_method # rubocop:disable Metrics/AbcSize
43
44
  untimed_func_symbol = "untimed_#{ meth_spec.method_name }".to_sym
44
45
  send_to = class_method ? meth_spec.clazz.cs__singleton_class : meth_spec.clazz
45
46
  meth_spec.clazz.class_eval do
@@ -105,12 +106,7 @@ module Contrast
105
106
  class Log
106
107
  include Singleton
107
108
  include ::Contrast::TraceTiming
108
-
109
- DEFAULT_NAME = 'contrast.log'
110
- DEFAULT_LEVEL = ::Ougai::Logging::Severity::INFO
111
- VALID_LEVELS = ::Ougai::Logging::Severity::SEV_LABEL
112
- STDOUT_STR = 'STDOUT'
113
- STDERR_STR = 'STDERR'
109
+ include Contrast::Utils::LogUtils
114
110
 
115
111
  attr_reader :previous_path, :previous_level
116
112
 
@@ -167,97 +163,6 @@ module Contrast
167
163
  dir_name = File.dirname(File.absolute_path(path))
168
164
  File.writable?(dir_name)
169
165
  end
170
-
171
- private
172
-
173
- def build path: STDOUT_STR, level_const: DEFAULT_LEVEL
174
- logger = case path
175
- when STDOUT_STR, STDERR_STR
176
- ::Ougai::Logger.new(Object.cs__const_get(path))
177
- else
178
- ::Ougai::Logger.new(path)
179
- end
180
- add_contrast_loggers(logger)
181
- logger.progname = 'Contrast Agent'
182
- logger.level = level_const
183
- logger.formatter = Contrast::Logger::Format.new
184
- logger.formatter.datetime_format = '%Y-%m-%dT%H:%M:%S.%L%z'
185
- logger
186
- end
187
-
188
- def add_contrast_loggers logger
189
- logger.extend(Contrast::Logger::Application)
190
- logger.extend(Contrast::Logger::Request)
191
- logger.extend(Contrast::Logger::Time)
192
- end
193
-
194
- # Determine the valid path to which to log, given the precedence of config > settings > default.
195
- #
196
- # @param log_file [String, nil] the file to which to log as provided by the settings retrieved from the
197
- # TeamServer.
198
- # @return [String] the path to which to log or STDOUT / STDERR if one of those values provided.
199
- def find_valid_path log_file
200
- config = ::Contrast::CONFIG.root.agent.logger
201
- config_path = config&.path&.length.to_i.positive? ? config.path : nil
202
- valid_path(config_path || log_file)
203
- end
204
-
205
- def valid_path path
206
- path = path.nil? ? Contrast::Utils::ObjectShare::EMPTY_STRING : path
207
- return path if path == STDOUT_STR
208
- return path if path == STDERR_STR
209
-
210
- path = DEFAULT_NAME if path.empty?
211
- if write_permission?(path)
212
- path
213
- elsif write_permission?(DEFAULT_NAME)
214
- # Log once when the path is invalid. We'll change to this path, so no
215
- # need to log again.
216
- if previous_path != DEFAULT_NAME
217
- puts "[!] Unable to write to '#{ path }'. Writing to default log '#{ DEFAULT_NAME }' instead."
218
- end
219
- DEFAULT_NAME
220
- else
221
- # Log once when the path is invalid. We'll change to this path, so no
222
- # need to log again.
223
- puts "[!] Unable to write to '#{ path }'. Writing to standard out instead." if previous_path != STDOUT_STR
224
- STDOUT_STR
225
- end
226
- end
227
-
228
- # Determine the valid level to which to log, given the precedence of config > settings > default.
229
- #
230
- # @param log_level [String, nil] the level at which to log as provided by the settings retrieved from the
231
- # TeamServer.
232
- # @return [::Ougai::Logging::Severity] the level at which to log
233
- def find_valid_level log_level
234
- config = ::Contrast::CONFIG.root.agent.logger
235
- config_level = config&.level&.length&.positive? ? config.level : nil
236
-
237
- valid_level(config_level || log_level)
238
- end
239
-
240
- def valid_level level
241
- level ||= DEFAULT_LEVEL
242
- level = level.upcase
243
- if VALID_LEVELS.include?(level)
244
- Object.cs__const_get("::Ougai::Logging::Severity::#{ level }")
245
- else
246
- DEFAULT_LEVEL
247
- end
248
- rescue StandardError
249
- DEFAULT_LEVEL
250
- end
251
-
252
- # Log that the Agent log has changed and include some default information at the start of the log.
253
- def log_update
254
- logger.debug('Initialized new contrast agent logger')
255
- logger.debug_with_time('middleware: log environment') do
256
- logger.application_environment
257
- logger.application_configuration
258
- logger.application_libraries
259
- end
260
- end
261
166
  end
262
167
  end
263
168
  end
@@ -0,0 +1,129 @@
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
+ module Contrast
5
+ module Utils
6
+ module Assess
7
+ # This module will include all methods for some internal validations in the PropagationMethod module
8
+ # and some other module methods from the same place, so we can ease the main module
9
+ module PropagationMethodUtils
10
+ APPEND_ACTION = 'APPEND'
11
+ CENTER_ACTION = 'CENTER'
12
+ INSERT_ACTION = 'INSERT'
13
+ KEEP_ACTION = 'KEEP'
14
+ NEXT_ACTION = 'NEXT'
15
+ NOOP_ACTION = 'NOOP'
16
+ PREPEND_ACTION = 'PREPEND'
17
+ REPLACE_ACTION = 'REPLACE'
18
+ REMOVE_ACTION = 'REMOVE'
19
+ REVERSE_ACTION = 'REVERSE'
20
+ SPLAT_ACTION = 'SPLAT'
21
+ SPLIT_ACTION = 'SPLIT'
22
+ DB_WRITE_ACTION = 'DB_WRITE'
23
+ CUSTOM_ACTION = 'CUSTOM'
24
+
25
+ ZERO_LENGTH_ACTIONS = [DB_WRITE_ACTION, CUSTOM_ACTION, KEEP_ACTION, REPLACE_ACTION, SPLAT_ACTION].cs__freeze
26
+
27
+ PROPAGATION_ACTIONS = {
28
+ APPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Append,
29
+ CENTER_ACTION => Contrast::Agent::Assess::Policy::Propagator::Center,
30
+ INSERT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Insert,
31
+ KEEP_ACTION => Contrast::Agent::Assess::Policy::Propagator::Keep,
32
+ NEXT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Next,
33
+ NOOP_ACTION => nil,
34
+ PREPEND_ACTION => Contrast::Agent::Assess::Policy::Propagator::Prepend,
35
+ REPLACE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Replace,
36
+ REMOVE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Remove,
37
+ REVERSE_ACTION => Contrast::Agent::Assess::Policy::Propagator::Reverse,
38
+ SPLAT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Splat,
39
+ SPLIT_ACTION => Contrast::Agent::Assess::Policy::Propagator::Split
40
+ }.cs__freeze
41
+
42
+ def determine_target propagation_node, ret, object, args
43
+ target = propagation_node.targets[0]
44
+ case target
45
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
46
+ object
47
+ when Contrast::Utils::ObjectShare::RETURN_KEY
48
+ ret
49
+ else
50
+ args[target]
51
+ end
52
+ end
53
+
54
+ # Custom actions tend to be the more complex of our propagations. Often, the method has to make decisions
55
+ # about the target based on the context with which the method was called. As such, defer determining if the
56
+ # target is valid to that method.
57
+ #
58
+ # In all other cases, a target is valid for propagation if it is not nil
59
+ #
60
+ # @param target [Object] the thing to which to propagate
61
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
62
+ # propagation event.
63
+ # @return [Boolean]
64
+ def valid_target? target, propagation_node
65
+ return true if propagation_node.action == CUSTOM_ACTION
66
+
67
+ !!target
68
+ end
69
+
70
+ # If the action required needs a length and the target does not have one, the length is not valid
71
+ #
72
+ # @param target [Object] the thing to which to propagate
73
+ # @param action [String] the name of the action taken during this propagation
74
+ # @return [Boolean]
75
+ def valid_length? target, action
76
+ return true if ZERO_LENGTH_ACTIONS.include?(action)
77
+
78
+ if Contrast::Utils::DuckUtils.quacks_to?(target, :length)
79
+ target.length != 0 # rubocop:disable Style/ZeroLengthPredicate
80
+ else
81
+ !target.to_s.empty?
82
+ end
83
+ end
84
+
85
+ # Before we do any work, we should check if we even need to. If the source and target of this patcher are
86
+ # not tracked, there's no need to do anything. A copy of nothing is still nothing.
87
+ #
88
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
89
+ # propagation event.
90
+ # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
91
+ # the invocation of the patched method.
92
+ # @param target [Object] the thing to which to propagate
93
+ # @return [Boolean]
94
+ def can_propagate? propagation_node, preshift, target
95
+ return false unless appropriate_target?(propagation_node, target)
96
+ return true if Contrast::Utils::Assess::TrackingUtil.tracked?(target)
97
+ return false unless preshift
98
+
99
+ propagation_node.sources.each do |source|
100
+ case source
101
+ when Contrast::Utils::ObjectShare::OBJECT_KEY
102
+ return true if Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.object)
103
+ else
104
+ # has to be P, there's no ret source type (yet? ever?)
105
+ return true if preshift.args && Contrast::Utils::Assess::TrackingUtil.tracked?(preshift.args[source])
106
+ end
107
+ end
108
+ false
109
+ end
110
+
111
+ # We cannot propagate to frozen things that have not been updated to work with our property tracking,
112
+ # unless they're duplicable and the return. We probably shouldn't propagate to frozen things at all, as
113
+ # they're supposed to be immutable, but third parties do jenky things, so allow it as long as it is safe to
114
+ # do.
115
+ #
116
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
117
+ # propagation event.
118
+ # @param target [Object] the Target to which to propagate.
119
+ # @return [Boolean] if the target can be propagated to
120
+ def appropriate_target? propagation_node, target
121
+ # special handle Returns b/c we can do unfreezing magic during propagation
122
+ return true if propagation_node.targets[0] == Contrast::Utils::ObjectShare::RETURN_KEY
123
+
124
+ Contrast::Agent::Assess::Tracker.trackable?(target)
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end