contrast-agent 3.8.5 → 3.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (153) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_array/cs__assess_array.c +1 -1
  3. data/ext/cs__assess_module/cs__assess_module.c +0 -1
  4. data/ext/cs__assess_yield_track/cs__assess_yield_track.c +34 -0
  5. data/ext/cs__assess_yield_track/cs__assess_yield_track.h +12 -0
  6. data/ext/{cs__scope → cs__assess_yield_track}/extconf.rb +0 -0
  7. data/ext/cs__common/cs__common.c +6 -6
  8. data/ext/cs__common/cs__common.h +3 -1
  9. data/ext/cs__contrast_patch/cs__contrast_patch.c +142 -119
  10. data/ext/cs__contrast_patch/cs__contrast_patch.h +3 -0
  11. data/funchook/autom4te.cache/requests +48 -48
  12. data/funchook/config.log +2 -2
  13. data/lib/contrast/agent.rb +15 -5
  14. data/lib/contrast/agent/assess.rb +0 -1
  15. data/lib/contrast/agent/assess/contrast_event.rb +9 -8
  16. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +68 -18
  17. data/lib/contrast/agent/assess/policy/policy.rb +0 -14
  18. data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/preshift.rb +1 -1
  20. data/lib/contrast/agent/assess/policy/propagation_method.rb +4 -2
  21. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  22. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
  23. data/lib/contrast/agent/assess/policy/propagator/splat.rb +2 -2
  24. data/lib/contrast/agent/assess/policy/propagator/split.rb +166 -1
  25. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +1 -0
  26. data/lib/contrast/agent/assess/policy/source_method.rb +199 -140
  27. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +30 -0
  28. data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +36 -0
  29. data/lib/contrast/agent/assess/policy/trigger_method.rb +238 -153
  30. data/lib/contrast/agent/assess/policy/trigger_node.rb +54 -9
  31. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +13 -0
  32. data/lib/contrast/agent/assess/properties.rb +29 -0
  33. data/lib/contrast/agent/assess/rule/csrf/csrf_applicator.rb +35 -31
  34. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +1 -1
  35. data/lib/contrast/agent/class_reopener.rb +98 -55
  36. data/lib/contrast/agent/feature_state.rb +1 -1
  37. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  38. data/lib/contrast/agent/logger_manager.rb +2 -2
  39. data/lib/contrast/agent/middleware.rb +1 -3
  40. data/lib/contrast/agent/patching/policy/after_load_patch.rb +40 -4
  41. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +33 -8
  42. data/lib/contrast/agent/patching/policy/method_policy.rb +20 -7
  43. data/lib/contrast/agent/patching/policy/patch.rb +54 -23
  44. data/lib/contrast/agent/patching/policy/patch_status.rb +0 -2
  45. data/lib/contrast/agent/patching/policy/patcher.rb +10 -11
  46. data/lib/contrast/agent/patching/policy/policy.rb +4 -0
  47. data/lib/contrast/agent/patching/policy/policy_node.rb +14 -1
  48. data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -1
  49. data/lib/contrast/agent/protect/policy/policy.rb +6 -6
  50. data/lib/contrast/agent/protect/rule/base.rb +1 -1
  51. data/lib/contrast/agent/protect/rule/deserialization.rb +3 -25
  52. data/lib/contrast/agent/protect/rule/sqli.rb +1 -1
  53. data/lib/contrast/agent/railtie.rb +11 -5
  54. data/lib/contrast/agent/request.rb +1 -19
  55. data/lib/contrast/agent/request_context.rb +1 -1
  56. data/lib/contrast/agent/rewriter.rb +4 -3
  57. data/lib/contrast/agent/scope.rb +116 -19
  58. data/lib/contrast/agent/service_heartbeat.rb +5 -2
  59. data/lib/contrast/agent/settings_state.rb +12 -8
  60. data/lib/contrast/agent/version.rb +1 -1
  61. data/lib/contrast/api.rb +1 -0
  62. data/lib/contrast/api/speedracer.rb +2 -2
  63. data/lib/contrast/components/agent.rb +26 -7
  64. data/lib/contrast/components/app_context.rb +8 -45
  65. data/lib/contrast/components/contrast_service.rb +3 -4
  66. data/lib/contrast/components/interface.rb +1 -1
  67. data/lib/contrast/components/scope.rb +56 -26
  68. data/lib/contrast/config/ruby_configuration.rb +8 -3
  69. data/lib/contrast/delegators.rb +9 -0
  70. data/lib/contrast/delegators/application_update.rb +32 -0
  71. data/lib/contrast/extensions/framework/rack/cookie.rb +24 -0
  72. data/lib/contrast/extensions/framework/rack/request.rb +24 -0
  73. data/lib/contrast/extensions/framework/rack/response.rb +23 -0
  74. data/lib/contrast/extensions/framework/rails/action_controller_railties_helper_inherited.rb +20 -0
  75. data/lib/contrast/extensions/framework/rails/active_record.rb +26 -0
  76. data/lib/contrast/extensions/framework/rails/active_record_named.rb +53 -0
  77. data/lib/contrast/extensions/framework/rails/active_record_time_zone_inherited.rb +21 -0
  78. data/lib/contrast/extensions/framework/rails/buffer.rb +28 -0
  79. data/lib/contrast/extensions/framework/rails/configuration.rb +27 -0
  80. data/lib/contrast/extensions/framework/sinatra/base.rb +59 -0
  81. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess.rb +12 -11
  82. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/array.rb +4 -3
  83. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/assess_extension.rb +0 -2
  84. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/basic_object.rb +1 -1
  85. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/erb.rb +0 -0
  86. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/exec_trigger.rb +0 -0
  87. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/fiber.rb +3 -4
  88. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/hash.rb +0 -0
  89. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/kernel.rb +1 -1
  90. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/module.rb +1 -1
  91. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/regexp.rb +0 -0
  92. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/string.rb +0 -0
  93. data/lib/contrast/{core_extensions → extensions/ruby_core}/assess/tilt_template_trigger.rb +0 -0
  94. data/lib/contrast/extensions/ruby_core/assess/xpath_library_trigger.rb +40 -0
  95. data/lib/contrast/{core_extensions → extensions/ruby_core}/delegator.rb +0 -0
  96. data/lib/contrast/{core_extensions → extensions/ruby_core}/eval_trigger.rb +1 -1
  97. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory.rb +0 -0
  98. data/lib/contrast/{core_extensions → extensions/ruby_core}/inventory/datastores.rb +1 -1
  99. data/lib/contrast/extensions/ruby_core/module.rb +17 -0
  100. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect.rb +0 -0
  101. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_command_injection_rule.rb +8 -6
  102. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_deserialization_rule.rb +7 -5
  103. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_no_sqli_rule.rb +5 -3
  104. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_path_traversal_rule.rb +31 -27
  105. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_sqli_rule.rb +5 -3
  106. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/applies_xxe_rule.rb +9 -7
  107. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/kernel.rb +0 -0
  108. data/lib/contrast/{core_extensions → extensions/ruby_core}/protect/psych.rb +1 -1
  109. data/lib/contrast/{core_extensions → extensions/ruby_core}/thread.rb +0 -0
  110. data/lib/contrast/framework/base_support.rb +63 -0
  111. data/lib/contrast/framework/manager.rb +109 -0
  112. data/lib/contrast/framework/platform_version.rb +21 -0
  113. data/lib/contrast/framework/rails_support.rb +88 -0
  114. data/lib/contrast/framework/sinatra_application_helper.rb +49 -0
  115. data/lib/contrast/framework/sinatra_support.rb +94 -0
  116. data/lib/contrast/framework/view_technologies_descriptor.rb +20 -0
  117. data/lib/contrast/utils/assess/tracking_util.rb +2 -4
  118. data/lib/contrast/utils/class_util.rb +92 -37
  119. data/lib/contrast/utils/duck_utils.rb +59 -39
  120. data/lib/contrast/utils/environment_util.rb +5 -75
  121. data/lib/contrast/utils/freeze_util.rb +3 -7
  122. data/lib/contrast/utils/invalid_configuration_util.rb +5 -5
  123. data/lib/contrast/utils/job_servers_running.rb +39 -0
  124. data/lib/contrast/utils/ruby_ast_rewriter.rb +2 -2
  125. data/lib/contrast/utils/service_response_util.rb +0 -6
  126. data/lib/contrast/utils/sinatra_helper.rb +6 -0
  127. data/lib/contrast/utils/stack_trace_utils.rb +1 -1
  128. data/resources/assess/policy.json +74 -23
  129. data/resources/inventory/policy.json +1 -1
  130. data/resources/protect/policy.json +11 -9
  131. data/resources/rubocops/object/frozen_cop.rb +1 -1
  132. data/ruby-agent.gemspec +2 -0
  133. data/service_executables/VERSION +1 -1
  134. data/service_executables/linux/contrast-service +0 -0
  135. data/service_executables/mac/contrast-service +0 -0
  136. metadata +94 -57
  137. data/ext/cs__scope/cs__scope.c +0 -96
  138. data/ext/cs__scope/cs__scope.h +0 -33
  139. data/lib/contrast/agent/assess/class_reverter.rb +0 -82
  140. data/lib/contrast/agent/patching/policy/policy_unpatcher.rb +0 -28
  141. data/lib/contrast/core_extensions/module.rb +0 -42
  142. data/lib/contrast/core_extensions/object.rb +0 -27
  143. data/lib/contrast/rails_extensions/assess/action_controller_inheritance.rb +0 -48
  144. data/lib/contrast/rails_extensions/assess/active_record.rb +0 -32
  145. data/lib/contrast/rails_extensions/assess/active_record_named.rb +0 -61
  146. data/lib/contrast/rails_extensions/assess/configuration.rb +0 -26
  147. data/lib/contrast/rails_extensions/buffer.rb +0 -30
  148. data/lib/contrast/rails_extensions/rack.rb +0 -45
  149. data/lib/contrast/sinatra_extensions/assess/cookie.rb +0 -26
  150. data/lib/contrast/sinatra_extensions/inventory/sinatra_base.rb +0 -59
  151. data/lib/contrast/utils/operating_environment.rb +0 -38
  152. data/lib/contrast/utils/path_util.rb +0 -151
  153. data/lib/contrast/utils/scope_util.rb +0 -99
@@ -126,6 +126,10 @@ module Contrast
126
126
  triggers.select { |trigger| trigger.rule_id == rule_id }
127
127
  end
128
128
 
129
+ def find_source_node class_name, method_name, instance_method
130
+ sources.find { |source| source.class_name == class_name && source.method_name == method_name && source.instance_method == instance_method }
131
+ end
132
+
129
133
  def find_node rule_id, class_name, method_name, instance_method
130
134
  find_triggers_by_rule(rule_id).find do |node|
131
135
  node.class_name == class_name && node.method_name == method_name && node.instance_method == instance_method
@@ -1,6 +1,8 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
+ cs__scoped_require 'contrast/components/interface'
5
+
4
6
  module Contrast
5
7
  module Agent
6
8
  module Patching
@@ -10,7 +12,10 @@ module Contrast
10
12
  #
11
13
  # @abstract
12
14
  class PolicyNode
13
- attr_accessor :class_name, :instance_method, :method_name, :method_visibility
15
+ include Contrast::Components::Interface
16
+ access_component :scope
17
+
18
+ attr_accessor :class_name, :instance_method, :method_name, :method_scope, :method_visibility
14
19
  attr_reader :properties
15
20
 
16
21
  def node_class
@@ -25,6 +30,7 @@ module Contrast
25
30
  @class_name = policy_hash[JSON_CLASS_NAME]
26
31
  @instance_method = policy_hash[JSON_INSTANCE_METHOD]
27
32
  @method_name = policy_hash[JSON_METHOD_NAME]
33
+ @method_scope = policy_hash[JSON_METHOD_SCOPE]
28
34
  @method_visibility = policy_hash[JSON_METHOD_VISIBILITY]
29
35
  @properties = policy_hash[JSON_PROPERTIES]
30
36
  symbolize
@@ -42,6 +48,11 @@ module Contrast
42
48
  raise(ArgumentError, "#{ node_class } #{ id } did not have a proper method name. Unable to create.") unless method_name
43
49
  raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_name value. Unable to create.") unless method_name.is_a?(Symbol)
44
50
  raise(ArgumentError, "#{ node_class } #{ id } has a non symbol @method_visibility value. Unable to create.") unless method_visibility.is_a?(Symbol)
51
+ unless method_scope.nil? || Contrast::Agent::Scope.valid_scope?(method_scope)
52
+ raise(ArgumentError, "#{ node_class } #{ id } requires an undefined scope. Unable to create.")
53
+ end
54
+
55
+ nil
45
56
  end
46
57
 
47
58
  # just turns this into a ruby-ism
@@ -64,6 +75,7 @@ module Contrast
64
75
  def symbolize
65
76
  @method_name = @method_name.to_sym if @method_name
66
77
  @method_visibility = @method_visibility.to_sym if @method_visibility
78
+ @method_scope = @method_scope.to_sym if @method_scope
67
79
  end
68
80
 
69
81
  # The keys used to read from policy.json to create the individual
@@ -71,6 +83,7 @@ module Contrast
71
83
  JSON_CLASS_NAME = 'class_name'
72
84
  JSON_INSTANCE_METHOD = 'instance_method'
73
85
  JSON_METHOD_NAME = 'method_name'
86
+ JSON_METHOD_SCOPE = 'scope'
74
87
  JSON_METHOD_VISIBILITY = 'method_visibility'
75
88
  JSON_PROPERTIES = 'properties'
76
89
  end
@@ -1,7 +1,7 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- cs__scoped_require 'contrast/core_extensions/module'
4
+ cs__scoped_require 'contrast/extensions/ruby_core/module'
5
5
  cs__scoped_require 'contrast/agent/patching/policy/policy_node'
6
6
 
7
7
  module Contrast
@@ -20,6 +20,7 @@ module Contrast
20
20
  JSON_APPLICATOR_METHOD = 'applicator_method'
21
21
  JSON_REQUIRED_PROPS = 'required_properties'
22
22
  JSON_OPTIONAL_PROPS = 'optional_properties'
23
+ JSON_SCOPE = 'scope'
23
24
  JSON_ON_EXCEPTION = 'on_exception'
24
25
 
25
26
  attr_reader :rule_id
@@ -4,12 +4,12 @@
4
4
  cs__scoped_require 'contrast/agent/patching/policy/policy'
5
5
 
6
6
  # classes required by patches in the policy
7
- cs__scoped_require 'contrast/core_extensions/protect/applies_command_injection_rule'
8
- cs__scoped_require 'contrast/core_extensions/protect/applies_deserialization_rule'
9
- cs__scoped_require 'contrast/core_extensions/protect/applies_no_sqli_rule'
10
- cs__scoped_require 'contrast/core_extensions/protect/applies_path_traversal_rule'
11
- cs__scoped_require 'contrast/core_extensions/protect/applies_sqli_rule'
12
- cs__scoped_require 'contrast/core_extensions/protect/applies_xxe_rule'
7
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_command_injection_rule'
8
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_deserialization_rule'
9
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_no_sqli_rule'
10
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_path_traversal_rule'
11
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_sqli_rule'
12
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_xxe_rule'
13
13
  cs__scoped_require 'contrast/agent/protect/policy/trigger_node'
14
14
 
15
15
  module Contrast
@@ -14,7 +14,7 @@ module Contrast
14
14
  class Base
15
15
  include Contrast::Components::Interface
16
16
 
17
- access_component :logging, :analysis, :settings
17
+ access_component :logging, :analysis, :settings, :scope
18
18
 
19
19
  UNKNOWN_USER_INPUT = Contrast::Api::Dtm::UserInput.new.tap do |user_input|
20
20
  user_input.input_type = :UNKNOWN
@@ -90,7 +90,9 @@ module Contrast
90
90
  # @raise [Contrast::SecurityException] if attack detected while in
91
91
  # block mode.
92
92
  def check_command_scope gadget_command
93
- return unless deserializing?
93
+ # If we're within 'deserialization scope', then we've got a
94
+ # deserialization method in our call stack.
95
+ return unless in_deserialization_scope?
94
96
 
95
97
  context = Contrast::Agent::REQUEST_TRACKER.current
96
98
  kwargs = { COMMAND_SCOPE: true }
@@ -100,30 +102,6 @@ module Contrast
100
102
  raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
101
103
  end
102
104
 
103
- # I don't know a better way to do this without introducing another
104
- # scope.
105
- # The policy files are designed around using the Module names, not
106
- # the file names, but stack is built using filenames. These are the
107
- # files and methods we patch for this rule.
108
- #
109
- # There's not real test for this since I can't figure out how to get
110
- # a command to execute from within the .load methods
111
- # Assuming someone else does, this should just work (tested with
112
- # Screener and the combination "%w[erb.rb `result']")
113
- DESERIALIZER_STACK = [
114
- %w[/psych.rb: `load'],
115
- %w[/marshal.rb: `load']
116
- ].cs__freeze
117
- # We're considered deserializing if the call stack includes a
118
- # reference to the file in which a serializer is defined and
119
- # the method of that serializer responsible for deserializing.
120
- # @return [Boolean] if the caller indicates deserialization.
121
- def deserializing?
122
- Kernel.caller.product(DESERIALIZER_STACK).find do |(frame, (class_signature_form, method_signature_form))|
123
- frame.end_with?(method_signature_form) && frame.include?(class_signature_form)
124
- end
125
- end
126
-
127
105
  protected
128
106
 
129
107
  # Build the RaspRuleSample for the detected Deserialization attack.
@@ -1,7 +1,7 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- cs__scoped_require 'contrast/core_extensions/protect/applies_sqli_rule'
4
+ cs__scoped_require 'contrast/extensions/ruby_core/protect/applies_sqli_rule'
5
5
 
6
6
  module Contrast
7
7
  module Agent
@@ -1,24 +1,30 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- cs__scoped_require 'contrast/utils/operating_environment'
4
+ cs__scoped_require 'contrast/utils/job_servers_running'
5
5
 
6
6
  module Contrast
7
7
  module Agent
8
8
  # A Railtie to allow for the automatic hooking of the Agent into a Rails
9
9
  # application.
10
10
  class Railtie < Rails::Railtie
11
+ include Contrast::Components::Interface
12
+ access_component :agent, :app_context, :logging
13
+
11
14
  initializer 'Contrast Ruby Agent Initializer' do |app|
12
15
  if defined?(Rails) && defined?(Rails.logger)
13
16
  Rails.logger.debug('In railtie ::')
14
17
  Rails.logger.debug(app.middleware.inspect)
15
18
  end
16
19
 
17
- if Contrast::Utils::OperatingEnvironment.unsupported?
18
- Rails.logger.debug('Detected a non-webserver context, skipping Contrast middleware insertion.')
20
+ # TODO: RUBY-564 This logic is not specific to Rails and should be used more broadly
21
+ # with all web frameworks. Move this check to be a part of our new initialization
22
+ # routine.
23
+ if APP_CONTEXT.instrument_middleware_stack?
24
+ AGENT.insert_middleware(app)
19
25
  else
20
- # Keep our middleware at the outermost layer of the onion
21
- app.middleware.insert_before 0, Contrast::Agent::Middleware
26
+ Rails.logger.debug('Detected a running job server, skipping Contrast middleware insertion.')
27
+ logger.debug(nil, "Disabling Contrast for process #{ Process.pid }")
22
28
  end
23
29
  end
24
30
 
@@ -265,17 +265,7 @@ module Contrast
265
265
  http_request.version = '1.1' # currently not in rack request; hard-coding
266
266
  http_request.method = Contrast::Utils::StringUtils.force_utf8(request_method)
267
267
  http_request.raw = Contrast::Utils::StringUtils.force_utf8(@rack_request.path_info)
268
- http_request.parsed_connection = true
269
- http_request.uri = Contrast::Utils::StringUtils.force_utf8(path)
270
- http_request.normalized_uri = Contrast::Utils::StringUtils.force_utf8(normalized_uri)
271
- http_context = if http_request.uri == Contrast::Utils::ObjectShare::SLASH
272
- Contrast::Utils::ObjectShare::SLASH
273
- else
274
- http_request.uri.split(Contrast::Utils::ObjectShare::SLASH).first
275
- end
276
- http_request.context = Contrast::Utils::StringUtils.force_utf8(http_context)
277
- http_request.path = Contrast::Utils::StringUtils.force_utf8(http_request.uri)
278
- http_request.query_string = Contrast::Utils::StringUtils.force_utf8(query_string)
268
+ http_request.parsed_connection = false
279
269
  end
280
270
 
281
271
  def append_params http_request
@@ -395,10 +385,6 @@ module Contrast
395
385
  @_content_type ||= @rack_request.content_type
396
386
  end
397
387
 
398
- def path
399
- @_path ||= @rack_request.path
400
- end
401
-
402
388
  def request_cookies
403
389
  @_request_cookies ||= @rack_request.cookies
404
390
  end
@@ -407,10 +393,6 @@ module Contrast
407
393
  @_parameters ||= with_contrast_scope { normalize_params(@rack_request.params) }
408
394
  end
409
395
 
410
- def base_url
411
- @_base_url ||= @rack_request.base_url
412
- end
413
-
414
396
  # End of Rack Request memoized values
415
397
 
416
398
  def file_names
@@ -59,7 +59,7 @@ module Contrast
59
59
 
60
60
  @sample_response &&= ASSESS.scan_response?
61
61
 
62
- append_route_coverage(Contrast::Utils::PathUtil.get_route(@request))
62
+ append_route_coverage(Contrast::Agent.framework_manager.get_route_dtm(@request))
63
63
  end
64
64
  end
65
65
 
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
- # frozen_string_literal: false
2
+ # frozen_string_literal: true
3
3
 
4
4
  # intentional -- we're using a << operator here
5
5
 
@@ -18,8 +18,8 @@ module Contrast
18
18
  include Contrast::Components::Interface
19
19
  access_component :logging, :scope
20
20
 
21
- SELF_DEFINITION = 'def self.'.cs__freeze
22
- DEFINITION = 'def '.cs__freeze
21
+ SELF_DEFINITION = 'def self.'
22
+ DEFINITION = 'def '
23
23
 
24
24
  class << self
25
25
  def rewrite_class module_data, redo_rewrite = false
@@ -95,6 +95,7 @@ module Contrast
95
95
  return nil if location.empty? || location[0].empty? || location[0].include?('eval')
96
96
  return nil if opener.written_from_location?(location)
97
97
 
98
+ opener.written_from_location!(location)
98
99
  opener.source_code(location, method_name)
99
100
  rescue SyntaxError
100
101
  logger.debug(nil, "SyntaxError: Can't parse method source from #{ clazz }##{ method_name }")
@@ -1,28 +1,125 @@
1
1
  # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
- # Scope lets us disable Contrast for certain code calls. We need to do this so
5
- # that we don't propagate through our own code.
6
- #
7
- # Think logging: If you have
8
- # something like "The source was '" + source + "'", and source is tracked,
9
- # you'll trigger propagation with the + method. This in turn would cause
10
- # propagation if you log there "The target ''" + target + "' was propagated'"
11
- # Which would then cause another propagation with the '+' method, forever.
12
- #
13
- # Instead, we should say "If I'm already doing Contrast things, don't track
14
- # this"
15
- #
16
- # NOTE: DO NOT DO ANYTHING IN THIS CLASS THAT COULD RESULT IN A MONKEY PATCHED
17
- # ACTION. WE CAN'T ENTER SCOPE BEFORE WE HAVE A SCOPE! YOU HAVE BEEN WARNED!!!
18
4
  module Contrast
19
5
  module Agent
6
+ # Scope lets us disable Contrast for certain code calls. We need to do this so
7
+ # that we don't propagate through our own code.
8
+ #
9
+ # Think logging: If you have
10
+ # something like "The source was '" + source + "'", and source is tracked,
11
+ # you'll trigger propagation with the + method. This in turn would cause
12
+ # propagation if you log there "The target ''" + target + "' was propagated'"
13
+ # Which would then cause another propagation with the '+' method, forever.
14
+ #
15
+ # Instead, we should say "If I'm already doing Contrast things, don't track
16
+ # this"
20
17
  class Scope
21
- # At first, all scopes are 0, meaning we're not in Contrast land
22
- # NOTE: DO NOT DO ANYTHING IN THIS CLASS THAT COULD RESULT IN A MONKEY
23
- # PATCHED ACTION. WE CAN'T ENTER SCOPE BEFORE WE HAVE A SCOPE! YOU HAVE
24
- # BEEN WARNED!!! (again)
18
+ # The following %i[] list is the authoritative list
19
+ # of scopes. If you define a new symbol here, you'll
20
+ # get scope methods:
21
+ # %i[monkey] ->
22
+ # enter_monkey_scope!
23
+ # exit_monkey_scope!
24
+ # in_monkey_scope?
25
+ # with_monkey_scope { special_monkey_function }
26
+ SCOPE_LIST = %i[contrast deserialization].cs__freeze
27
+
28
+ iv_list = SCOPE_LIST.map { |name| :"@#{ name }_scope" }
29
+ define_method 'initialize' do
30
+ iv_list.each do |iv_sym|
31
+ instance_variable_set(iv_sym, 0)
32
+ end
33
+ end
34
+
35
+ SCOPE_LIST.each do |name|
36
+ iv_sym = :"@#{ name }_scope"
37
+
38
+ define_method "in_#{ name }_scope?" do
39
+ instance_variable_get(iv_sym).positive?
40
+ end
41
+
42
+ enter_method_sym = :"enter_#{ name }_scope!"
43
+ define_method enter_method_sym do
44
+ level = instance_variable_get(iv_sym)
45
+ instance_variable_set(iv_sym, level + 1)
46
+ end
47
+
48
+ exit_method_sym = :"exit_#{ name }_scope!"
49
+ define_method exit_method_sym do
50
+ # by design, can go below zero.
51
+ # every exit/enter pair (regardless of series)
52
+ # should cancel each other out.
53
+ #
54
+ # so we prefer this sequence:
55
+ # scope = 0
56
+ # exit = -1
57
+ # enter = 0
58
+ # enter = 1
59
+ # exit = 0
60
+ # scope = 0
61
+ #
62
+ # over this sequence:
63
+ # scope = 0
64
+ # exit = 0
65
+ # enter = 1
66
+ # enter = 2
67
+ # exit = 1
68
+ # scope = 1
69
+ level = instance_variable_get(iv_sym)
70
+ instance_variable_set(iv_sym, level - 1)
71
+ end
72
+
73
+ define_method "with_#{ name }_scope" do |*_args, &block|
74
+ begin
75
+ send enter_method_sym
76
+ block.call
77
+ ensure
78
+ send exit_method_sym
79
+ end
80
+ end
81
+ end
82
+
83
+ # Dynamic versions of the above.
84
+ # These are equivalent, but they're slower and riskier.
85
+ # Prefer the static methods if you know what scope you need at the call site.
86
+ def in_scope? name
87
+ cs__class.ensure_valid_scope! name
88
+ call = with_contrast_scope { :"in_#{ name }_scope?" }
89
+ send(call)
90
+ end
91
+
92
+ def enter_scope! name
93
+ cs__class.ensure_valid_scope! name
94
+ call = with_contrast_scope { :"enter_#{ name }_scope!" }
95
+ send(call)
96
+ end
97
+
98
+ def exit_scope! name
99
+ cs__class.ensure_valid_scope! name
100
+ call = with_contrast_scope { :"exit_#{ name }_scope!" }
101
+ send(call)
102
+ end
103
+
104
+ def with_scope name, &block
105
+ cs__class.ensure_valid_scope! name
106
+ call = with_contrast_scope { :"with_#{ name }_scope" }
107
+ send(call, &block)
108
+ end
109
+
110
+ class << self
111
+ def valid_scope? scope_sym
112
+ Contrast::Agent::Scope::SCOPE_LIST.include? scope_sym
113
+ end
114
+
115
+ def ensure_valid_scope! scope_sym
116
+ unless valid_scope? scope_sym # rubocop:disable Style/GuardClause
117
+ with_contrast_scope do
118
+ raise NotImplementedError, "Scope '#{ scope_sym.inspect }' is not registered as a scope."
119
+ end
120
+ end
121
+ end
122
+ end
25
123
  end
26
124
  end
27
125
  end
28
- cs__scoped_require 'cs__scope/cs__scope'
@@ -20,8 +20,7 @@ module Contrast
20
20
  loop do
21
21
  begin
22
22
  logger.debug(nil, 'Sending Heartbeat...')
23
- # TODO: RUBY-672: Change noop to poll messages
24
- CONTRAST_SERVICE.queue_message(Contrast::Api::Dtm::Noop.new)
23
+ CONTRAST_SERVICE.queue_message(poll_message)
25
24
  end
26
25
  sleep REFRESH_INTERVAL_SEC
27
26
  end
@@ -29,6 +28,10 @@ module Contrast
29
28
  end
30
29
  alias_method :start, :updater_thread
31
30
 
31
+ def poll_message
32
+ @_poll_message ||= Contrast::Api::Dtm::Poll.new
33
+ end
34
+
32
35
  def stop
33
36
  Thread.kill(@_updater_thread) if @_updater_thread&.alive?
34
37
  end
@@ -120,16 +120,20 @@ module Contrast
120
120
  def send_inventory_message
121
121
  return unless INVENTORY.enabled?
122
122
 
123
- msg = Contrast::Api::Dtm::ApplicationUpdate.new
124
- msg.platform = Contrast::Api::Dtm::Platform.new
125
- msg.platform.major, msg.platform.minor, msg.platform.build = *Contrast::Utils::EnvironmentUtil.platform
123
+ app_update_msg = Contrast::Api::Dtm::ApplicationUpdate.new
126
124
 
127
- Contrast::Utils::EnvironmentUtil.add_library_to_app_update(msg, protobuf_format(CONFIG.root.inventory.tags))
128
- Contrast::Utils::EnvironmentUtil.scan_views(msg)
129
- Contrast::Utils::EnvironmentUtil.scan_routes(msg)
130
- Contrast::Utils::InventoryUtil.append_db_config(msg)
125
+ # TODO: RUBY-770
126
+ Contrast::Utils::EnvironmentUtil.add_library_to_app_update(app_update_msg, protobuf_format(CONFIG.root.inventory.tags))
131
127
 
132
- CONTRAST_SERVICE.queue_message msg
128
+ Contrast::Delegators::ApplicationUpdate.new(app_update_msg).instance_eval do
129
+ append_view_technology_descriptor_data(Contrast::Agent.framework_manager.find_applicable_view_technologies) if INVENTORY.enabled?
130
+ append_route_coverage_data(Contrast::Agent.framework_manager.find_route_discovery_data) if INVENTORY.enabled?
131
+ append_platform_version(Contrast::Agent.framework_manager.platform_version)
132
+ end
133
+
134
+ Contrast::Utils::InventoryUtil.append_db_config(app_update_msg)
135
+
136
+ CONTRAST_SERVICE.queue_message app_update_msg
133
137
  end
134
138
 
135
139
  def present? str