contrast-agent 3.8.5 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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