contrast-agent 3.13.2 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (250) hide show
  1. checksums.yaml +4 -4
  2. data/exe/contrast_service +1 -7
  3. data/ext/cs__assess_active_record_named/cs__active_record_named.c +8 -7
  4. data/ext/cs__assess_array/cs__assess_array.c +6 -5
  5. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +5 -5
  6. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +2 -1
  7. data/ext/cs__assess_hash/cs__assess_hash.c +18 -17
  8. data/ext/cs__assess_hash/cs__assess_hash.h +2 -1
  9. data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -8
  10. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +18 -16
  11. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.h +1 -0
  12. data/ext/cs__assess_module/cs__assess_module.c +6 -6
  13. data/ext/cs__assess_regexp/cs__assess_regexp.c +4 -4
  14. data/ext/cs__assess_string/cs__assess_string.c +31 -16
  15. data/ext/cs__assess_string/cs__assess_string.h +6 -1
  16. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +4 -2
  17. data/ext/cs__assess_yield_track/cs__assess_yield_track.c +2 -2
  18. data/ext/cs__common/cs__common.c +48 -39
  19. data/ext/cs__common/cs__common.h +16 -21
  20. data/ext/cs__contrast_patch/cs__contrast_patch.c +27 -25
  21. data/ext/cs__contrast_patch/cs__contrast_patch.h +5 -7
  22. data/ext/cs__protect_kernel/cs__protect_kernel.c +11 -12
  23. data/ext/cs__protect_kernel/cs__protect_kernel.h +2 -2
  24. data/lib/contrast-agent.rb +1 -1
  25. data/lib/contrast.rb +13 -23
  26. data/lib/contrast/agent.rb +39 -47
  27. data/lib/contrast/agent/assess.rb +12 -12
  28. data/lib/contrast/agent/assess/contrast_event.rb +151 -85
  29. data/lib/contrast/agent/assess/events/event_factory.rb +2 -2
  30. data/lib/contrast/agent/assess/events/source_event.rb +3 -3
  31. data/lib/contrast/agent/assess/finalizers/freeze.rb +15 -0
  32. data/lib/contrast/agent/assess/finalizers/hash.rb +97 -0
  33. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +11 -4
  34. data/lib/contrast/agent/assess/policy/patcher.rb +6 -6
  35. data/lib/contrast/agent/assess/policy/policy.rb +9 -11
  36. data/lib/contrast/agent/assess/policy/policy_node.rb +17 -12
  37. data/lib/contrast/agent/assess/policy/policy_scanner.rb +21 -5
  38. data/lib/contrast/agent/assess/policy/preshift.rb +13 -7
  39. data/lib/contrast/agent/assess/policy/propagation_method.rb +64 -44
  40. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -2
  41. data/lib/contrast/agent/assess/policy/propagator.rb +18 -18
  42. data/lib/contrast/agent/assess/policy/propagator/append.rb +8 -5
  43. data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
  44. data/lib/contrast/agent/assess/policy/propagator/center.rb +9 -5
  45. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  46. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +6 -4
  47. data/lib/contrast/agent/assess/policy/propagator/insert.rb +7 -4
  48. data/lib/contrast/agent/assess/policy/propagator/keep.rb +4 -1
  49. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +7 -9
  50. data/lib/contrast/agent/assess/policy/propagator/next.rb +7 -5
  51. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +13 -5
  52. data/lib/contrast/agent/assess/policy/propagator/remove.rb +8 -4
  53. data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -2
  54. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +7 -5
  55. data/lib/contrast/agent/assess/policy/propagator/select.rb +13 -7
  56. data/lib/contrast/agent/assess/policy/propagator/splat.rb +10 -9
  57. data/lib/contrast/agent/assess/policy/propagator/split.rb +27 -22
  58. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +52 -35
  59. data/lib/contrast/agent/assess/policy/propagator/trim.rb +11 -5
  60. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +5 -5
  61. data/lib/contrast/agent/assess/policy/source_method.rb +90 -72
  62. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +1 -1
  63. data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +1 -1
  64. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -12
  65. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +2 -2
  66. data/lib/contrast/agent/assess/policy/trigger_method.rb +81 -33
  67. data/lib/contrast/agent/assess/policy/trigger_node.rb +41 -46
  68. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +2 -1
  69. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +2 -2
  70. data/lib/contrast/agent/assess/properties.rb +15 -5
  71. data/lib/contrast/agent/assess/property/evented.rb +7 -20
  72. data/lib/contrast/agent/assess/property/tagged.rb +13 -7
  73. data/lib/contrast/agent/assess/property/updated.rb +131 -0
  74. data/lib/contrast/agent/assess/rule.rb +2 -2
  75. data/lib/contrast/agent/assess/rule/base.rb +3 -4
  76. data/lib/contrast/agent/assess/rule/provider.rb +3 -3
  77. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +58 -5
  78. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +24 -9
  79. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +85 -16
  80. data/lib/contrast/agent/assess/tag.rb +1 -1
  81. data/lib/contrast/agent/assess/tracker.rb +66 -0
  82. data/lib/contrast/agent/at_exit_hook.rb +6 -6
  83. data/lib/contrast/agent/class_reopener.rb +14 -11
  84. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +1 -1
  85. data/lib/contrast/agent/deadzone/policy/policy.rb +2 -2
  86. data/lib/contrast/agent/disable_reaction.rb +1 -1
  87. data/lib/contrast/agent/exclusion_matcher.rb +1 -1
  88. data/lib/contrast/agent/inventory.rb +15 -0
  89. data/lib/contrast/agent/inventory/dependencies.rb +50 -0
  90. data/lib/contrast/agent/inventory/dependency_analysis.rb +37 -0
  91. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +104 -0
  92. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +38 -0
  93. data/lib/contrast/agent/inventory/policy/datastores.rb +2 -2
  94. data/lib/contrast/agent/inventory/policy/policy.rb +3 -3
  95. data/lib/contrast/agent/inventory/policy/trigger_node.rb +1 -1
  96. data/lib/contrast/agent/middleware.rb +33 -34
  97. data/lib/contrast/agent/patching/policy/after_load_patch.rb +9 -9
  98. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +23 -22
  99. data/lib/contrast/agent/patching/policy/module_policy.rb +11 -11
  100. data/lib/contrast/agent/patching/policy/patch.rb +15 -15
  101. data/lib/contrast/agent/patching/policy/patcher.rb +43 -44
  102. data/lib/contrast/agent/patching/policy/policy.rb +23 -12
  103. data/lib/contrast/agent/patching/policy/policy_node.rb +1 -1
  104. data/lib/contrast/agent/patching/policy/trigger_node.rb +2 -2
  105. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +6 -8
  106. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +2 -2
  107. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
  108. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +7 -6
  109. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +2 -2
  110. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +4 -4
  111. data/lib/contrast/agent/protect/policy/policy.rb +8 -8
  112. data/lib/contrast/agent/protect/policy/rule_applicator.rb +1 -1
  113. data/lib/contrast/agent/protect/policy/trigger_node.rb +1 -1
  114. data/lib/contrast/agent/protect/rule.rb +18 -18
  115. data/lib/contrast/agent/protect/rule/base.rb +4 -3
  116. data/lib/contrast/agent/protect/rule/base_service.rb +1 -1
  117. data/lib/contrast/agent/protect/rule/cmd_injection.rb +5 -5
  118. data/lib/contrast/agent/protect/rule/deserialization.rb +1 -1
  119. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +1 -1
  120. data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -1
  121. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +1 -0
  122. data/lib/contrast/agent/protect/rule/path_traversal.rb +4 -5
  123. data/lib/contrast/agent/protect/rule/sqli.rb +2 -2
  124. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +1 -1
  125. data/lib/contrast/agent/protect/rule/xss.rb +1 -1
  126. data/lib/contrast/agent/protect/rule/xxe.rb +3 -5
  127. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +2 -2
  128. data/lib/contrast/agent/railtie.rb +1 -1
  129. data/lib/contrast/agent/reaction_processor.rb +2 -2
  130. data/lib/contrast/agent/request.rb +45 -43
  131. data/lib/contrast/agent/request_context.rb +10 -6
  132. data/lib/contrast/agent/request_handler.rb +1 -1
  133. data/lib/contrast/agent/response.rb +23 -12
  134. data/lib/contrast/agent/rewriter.rb +6 -9
  135. data/lib/contrast/agent/service_heartbeat.rb +2 -2
  136. data/lib/contrast/agent/static_analysis.rb +9 -9
  137. data/lib/contrast/agent/thread.rb +1 -1
  138. data/lib/contrast/agent/thread_watcher.rb +2 -2
  139. data/lib/contrast/agent/tracepoint_hook.rb +2 -2
  140. data/lib/contrast/agent/version.rb +1 -1
  141. data/lib/contrast/api.rb +4 -4
  142. data/lib/contrast/api/communication.rb +9 -9
  143. data/lib/contrast/api/communication/messaging_queue.rb +3 -6
  144. data/lib/contrast/api/communication/response_processor.rb +1 -1
  145. data/lib/contrast/api/communication/socket_client.rb +41 -6
  146. data/lib/contrast/api/communication/speedracer.rb +1 -1
  147. data/lib/contrast/api/communication/tcp_socket.rb +1 -1
  148. data/lib/contrast/api/communication/unix_socket.rb +1 -1
  149. data/lib/contrast/api/decorators.rb +17 -14
  150. data/lib/contrast/api/decorators/address.rb +20 -20
  151. data/lib/contrast/api/decorators/application_settings.rb +3 -2
  152. data/lib/contrast/api/decorators/application_update.rb +7 -8
  153. data/lib/contrast/api/decorators/http_request.rb +13 -12
  154. data/lib/contrast/api/decorators/input_analysis.rb +3 -2
  155. data/lib/contrast/api/decorators/library.rb +53 -0
  156. data/lib/contrast/api/decorators/library_usage_update.rb +30 -0
  157. data/lib/contrast/api/decorators/message.rb +4 -2
  158. data/lib/contrast/api/decorators/rasp_rule_sample.rb +2 -1
  159. data/lib/contrast/api/decorators/route_coverage.rb +3 -2
  160. data/lib/contrast/api/decorators/server_features.rb +3 -2
  161. data/lib/contrast/api/decorators/trace_event.rb +28 -25
  162. data/lib/contrast/api/decorators/trace_event_object.rb +6 -5
  163. data/lib/contrast/api/decorators/trace_event_signature.rb +5 -4
  164. data/lib/contrast/api/decorators/trace_taint_range.rb +4 -3
  165. data/lib/contrast/api/decorators/user_input.rb +4 -4
  166. data/lib/contrast/common_agent_configuration.rb +2 -2
  167. data/lib/contrast/components/agent.rb +8 -7
  168. data/lib/contrast/components/app_context.rb +50 -39
  169. data/lib/contrast/components/config.rb +32 -50
  170. data/lib/contrast/components/contrast_service.rb +10 -10
  171. data/lib/contrast/components/interface.rb +39 -17
  172. data/lib/contrast/components/inventory.rb +6 -1
  173. data/lib/contrast/components/logger.rb +1 -1
  174. data/lib/contrast/components/scope.rb +3 -3
  175. data/lib/contrast/components/settings.rb +20 -23
  176. data/lib/contrast/config.rb +18 -18
  177. data/lib/contrast/config/application_configuration.rb +5 -2
  178. data/lib/contrast/config/base_configuration.rb +2 -2
  179. data/lib/contrast/config/inventory_configuration.rb +2 -2
  180. data/lib/contrast/config/protect_rule_configuration.rb +1 -1
  181. data/lib/contrast/config/service_configuration.rb +8 -0
  182. data/lib/contrast/configuration.rb +93 -52
  183. data/lib/contrast/extension/assess.rb +21 -22
  184. data/lib/contrast/extension/assess/array.rb +18 -11
  185. data/lib/contrast/extension/assess/erb.rb +11 -3
  186. data/lib/contrast/extension/assess/eval_trigger.rb +7 -7
  187. data/lib/contrast/extension/assess/exec_trigger.rb +2 -2
  188. data/lib/contrast/extension/assess/fiber.rb +14 -14
  189. data/lib/contrast/extension/assess/hash.rb +7 -6
  190. data/lib/contrast/extension/assess/kernel.rb +34 -28
  191. data/lib/contrast/extension/assess/marshal.rb +67 -0
  192. data/lib/contrast/extension/assess/regexp.rb +10 -9
  193. data/lib/contrast/extension/assess/string.rb +23 -23
  194. data/lib/contrast/extension/inventory.rb +4 -4
  195. data/lib/contrast/extension/kernel.rb +1 -1
  196. data/lib/contrast/extension/module.rb +1 -1
  197. data/lib/contrast/extension/protect.rb +3 -3
  198. data/lib/contrast/extension/protect/kernel.rb +4 -4
  199. data/lib/contrast/extension/protect/psych.rb +2 -2
  200. data/lib/contrast/framework/base_support.rb +1 -1
  201. data/lib/contrast/framework/manager.rb +10 -11
  202. data/lib/contrast/framework/rack/patch/session_cookie.rb +23 -29
  203. data/lib/contrast/framework/rack/patch/support.rb +1 -1
  204. data/lib/contrast/framework/rack/support.rb +2 -2
  205. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +13 -13
  206. data/lib/contrast/framework/rails/patch/assess_configuration.rb +7 -13
  207. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +11 -11
  208. data/lib/contrast/framework/rails/patch/support.rb +4 -4
  209. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +11 -11
  210. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +12 -12
  211. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +4 -4
  212. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +12 -12
  213. data/lib/contrast/framework/rails/support.rb +67 -14
  214. data/lib/contrast/framework/sinatra/patch/base.rb +12 -12
  215. data/lib/contrast/framework/sinatra/patch/support.rb +1 -1
  216. data/lib/contrast/framework/sinatra/support.rb +6 -6
  217. data/lib/contrast/funchook/funchook.rb +1 -1
  218. data/lib/contrast/logger/application.rb +13 -5
  219. data/lib/contrast/logger/format.rb +22 -9
  220. data/lib/contrast/logger/log.rb +17 -10
  221. data/lib/contrast/logger/request.rb +30 -0
  222. data/lib/contrast/tasks/config.rb +1 -1
  223. data/lib/contrast/tasks/service.rb +2 -2
  224. data/lib/contrast/utils/assess/sampling_util.rb +2 -2
  225. data/lib/contrast/utils/assess/tracking_util.rb +49 -4
  226. data/lib/contrast/utils/class_util.rb +2 -2
  227. data/lib/contrast/utils/duck_utils.rb +0 -10
  228. data/lib/contrast/utils/env_configuration_item.rb +2 -1
  229. data/lib/contrast/utils/hash_digest.rb +2 -1
  230. data/lib/contrast/utils/heap_dump_util.rb +2 -2
  231. data/lib/contrast/utils/invalid_configuration_util.rb +21 -22
  232. data/lib/contrast/utils/inventory_util.rb +3 -10
  233. data/lib/contrast/utils/io_util.rb +1 -1
  234. data/lib/contrast/utils/os.rb +1 -1
  235. data/lib/contrast/utils/ruby_ast_rewriter.rb +1 -1
  236. data/lib/contrast/utils/sha256_builder.rb +2 -14
  237. data/lib/contrast/utils/stack_trace_utils.rb +2 -2
  238. data/lib/contrast/utils/string_utils.rb +11 -6
  239. data/resources/assess/policy.json +31 -22
  240. data/ruby-agent.gemspec +21 -19
  241. data/service_executables/VERSION +1 -1
  242. data/service_executables/linux/contrast-service +0 -0
  243. data/service_executables/mac/contrast-service +0 -0
  244. metadata +73 -30
  245. data/lib/contrast/agent/assess/insulator.rb +0 -49
  246. data/lib/contrast/agent/require_state.rb +0 -61
  247. data/lib/contrast/extension/assess/assess_extension.rb +0 -147
  248. data/lib/contrast/utils/boolean_util.rb +0 -30
  249. data/lib/contrast/utils/freeze_util.rb +0 -32
  250. data/lib/contrast/utils/gemfile_reader.rb +0 -193
@@ -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/agent/assess/policy/source_method'
4
+ require 'contrast/agent/assess/policy/source_method'
5
5
 
6
6
  module Contrast
7
7
  module Agent
@@ -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/agent/assess/policy/source_validation/cross_site_validator'
4
+ require 'contrast/agent/assess/policy/source_validation/cross_site_validator'
5
5
 
6
6
  module Contrast
7
7
  module Agent
@@ -25,24 +25,26 @@ module Contrast
25
25
  TEMPLATE_PROPAGATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
26
26
 
27
27
  def xss_tilt_trigger context, trigger_node, _source, object, ret, *args
28
- scope = args[0]
28
+ properties = Contrast::Agent::Assess::Tracker.properties(ret)
29
+ return unless properties
29
30
 
31
+ scope = args[0]
30
32
  erb_template_prerender = object.instance_variable_get(:@data)
31
33
  interpolated_inputs = []
32
34
  handle_binding_variables(scope, erb_template_prerender, ret, interpolated_inputs)
33
-
34
35
  handle_local_variables(args, erb_template_prerender, ret, interpolated_inputs)
35
-
36
+ properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
36
37
  unless interpolated_inputs.empty?
38
+ current_event = properties.event
37
39
  interpolated_inputs.each do |input|
38
- input.cs__properties.events.each do |event|
39
- ret.cs__properties.events << event
40
- end
40
+ input_properties = Contrast::Agent::Assess::Tracker.properties(input)
41
+ next unless input_properties&.event
42
+
43
+ current_event.parent_events << input_properties.event
41
44
  end
42
- ret.cs__properties.build_event(TEMPLATE_PROPAGATION_NODE, ret, erb_template_prerender, ret, interpolated_inputs)
43
45
  end
44
46
 
45
- if ret.cs__tracked?
47
+ if Contrast::Agent::Assess::Tracker.tracked?(ret)
46
48
  Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(context, trigger_node, ret, erb_template_prerender, ret, interpolated_inputs)
47
49
  end
48
50
 
@@ -52,32 +54,34 @@ module Contrast
52
54
  private
53
55
 
54
56
  def handle_binding_variables scope, erb_template_prerender, ret, interpolated_inputs
57
+ properties = Contrast::Agent::Assess::Tracker.properties(ret)
55
58
  binding_variables = scope.instance_variables
56
59
 
57
60
  binding_variables.each do |bound_variable_sym|
58
61
  bound_variable_value = scope.instance_variable_get(bound_variable_sym)
59
62
 
60
- next unless bound_variable_value.cs__respond_to?(:cs__tracked?) && bound_variable_value.cs__tracked?
63
+ next unless Contrast::Agent::Assess::Tracker.tracked?(bound_variable_value)
61
64
  next unless erb_template_prerender.include?(bound_variable_sym.to_s)
62
65
 
63
66
  start_index = ret.index(bound_variable_value)
64
67
  next if start_index.nil?
65
68
 
66
- ret.cs__copy_from(bound_variable_value, start_index)
69
+ properties.copy_from(bound_variable_value, ret, start_index)
67
70
  interpolated_inputs << bound_variable_sym
68
71
  end
69
72
  end
70
73
 
71
74
  def handle_local_variables args, erb_template_prerender, ret, interpolated_inputs
75
+ properties = Contrast::Agent::Assess::Tracker.properties(ret)
72
76
  locals = args[1]
73
77
  locals.each do |local_name, local_value|
74
- next unless local_value.cs__respond_to?(:cs__tracked?) && local_value.cs__tracked?
78
+ next unless Contrast::Agent::Assess::Tracker.tracked?(local_value)
75
79
  next unless erb_template_prerender.include?(local_name.to_s)
76
80
 
77
81
  start_index = ret.index(local_value)
78
82
  next if start_index.nil?
79
83
 
80
- ret.cs__copy_from(local_value, start_index)
84
+ properties.copy_from(local_value, ret, start_index)
81
85
  interpolated_inputs << local_name
82
86
  end
83
87
  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/components/interface'
4
+ require 'contrast/components/interface'
5
5
 
6
6
  module Contrast
7
7
  module Agent
@@ -39,7 +39,7 @@ module Contrast
39
39
  def process context, trigger_node, object, ret, *args
40
40
  args.each do |arg|
41
41
  next unless arg.cs__is_a?(String) || arg.cs__is_a?(Symbol)
42
- next unless arg.cs__tracked?
42
+ next unless Contrast::Agent::Assess::Tracker.tracked?(arg)
43
43
  next unless trigger_node.violated?(arg)
44
44
 
45
45
  Contrast::Agent::Assess::Policy::TriggerMethod.build_finding(
@@ -1,11 +1,11 @@
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/object_share'
5
- cs__scoped_require 'contrast/utils/sha256_builder'
6
- cs__scoped_require 'contrast/agent/assess/events/event_factory'
7
- cs__scoped_require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
8
- cs__scoped_require 'contrast/components/interface'
4
+ require 'contrast/agent/assess/events/event_factory'
5
+ require 'contrast/agent/assess/policy/trigger_validation/trigger_validation'
6
+ require 'contrast/components/interface'
7
+ require 'contrast/utils/object_share'
8
+ require 'contrast/utils/sha256_builder'
9
9
 
10
10
  module Contrast
11
11
  module Agent
@@ -20,10 +20,31 @@ module Contrast
20
20
  include Contrast::Components::Interface
21
21
  access_component :analysis, :logging
22
22
 
23
+ # The level of TeamServer compliance our traces meet when in the
24
+ # abnormal condition of being dataflow rules without routes
25
+ MINIMUM_FINDING_VERSION = 3
23
26
  # The level of TeamServer compliance our traces meet
24
- CURRENT_FINDING_VERSION = 2
27
+ CURRENT_FINDING_VERSION = 4
25
28
 
26
29
  class << self
30
+ # Append the given finding to the given context to be reported when
31
+ # the Context's activity is sent to the Service or, in the absence
32
+ # of that Context, generate an Activity and queue it manually
33
+ # @param finding [Contrast::Api::Dtm::Finding]
34
+ def report_finding finding
35
+ context = Contrast::Agent::REQUEST_TRACKER.current
36
+ if context
37
+ context.activity.findings << finding
38
+ else
39
+ activity = Contrast::Api::Dtm::Activity.new
40
+ activity.findings << finding
41
+
42
+ Contrast::Agent.messaging_queue.send_event_eventually(activity)
43
+ end
44
+ logger.debug('Finding reported',
45
+ rule: finding.rule_id)
46
+ end
47
+
27
48
  # This is called from within our woven proc. It will be called as if it
28
49
  # were inline in the Rack application.
29
50
  #
@@ -66,8 +87,8 @@ module Contrast
66
87
  # This converts the source of the finding, and the events leading
67
88
  # up to it into a Finding
68
89
  #
69
- # @param context [Contrast::Utils::ThreadTracker] the current request
70
- # context
90
+ # @param context [Contrast::Agent::RequestContext] the current
91
+ # request context
71
92
  # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
72
93
  # the node to direct applying this trigger event
73
94
  # @param source [Object] the source of the Trigger Event
@@ -89,18 +110,17 @@ module Contrast
89
110
 
90
111
  finding = Contrast::Api::Dtm::Finding.new
91
112
  finding.rule_id = Contrast::Utils::StringUtils.protobuf_safe_string(trigger_node.rule_id)
92
- finding.version = CURRENT_FINDING_VERSION
93
-
94
113
  build_from_source(finding, source)
95
114
  trigger_event = Contrast::Agent::Assess::Events::EventFactory.build(trigger_node, source, object, ret, args).to_dtm_event
96
115
  finding.events << trigger_event
97
116
  build_hash(finding, source)
98
117
  finding.routes << context.route if context.route
99
- context.activity.findings << finding
118
+ finding.version = determine_compliance_version(finding)
100
119
  logger.trace('Finding created',
101
120
  node_id: trigger_node.id,
102
121
  source_id: source.__id__,
103
122
  rule: trigger_node.rule_id)
123
+ report_finding(finding)
104
124
  rescue StandardError => e
105
125
  logger.error('Unable to build a finding', e, rule: trigger_node.rule_id, node_id: trigger_node.id)
106
126
  end
@@ -110,8 +130,8 @@ module Contrast
110
130
  # This is our method that actually checks the taint on the object
111
131
  # our trigger_node targets.
112
132
  #
113
- # @param context [Contrast::Utils::ThreadTracker] the current request
114
- # context
133
+ # @param context [Contrast::Agent::RequestContext] the current
134
+ # request context
115
135
  # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
116
136
  # the node to direct applying this trigger event
117
137
  # @param source [Object] the source of the Trigger Event
@@ -179,8 +199,8 @@ module Contrast
179
199
  # This is our method that actually checks the taint on the object
180
200
  # our trigger_node targets for our Regexp based rules.
181
201
  #
182
- # @param context [Contrast::Utils::ThreadTracker] the current request
183
- # context
202
+ # @param context [Contrast::Agent::RequestContext] the current
203
+ # request context
184
204
  # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
185
205
  # the node to direct applying this trigger event
186
206
  # @param source [Object] the source of the Trigger Event
@@ -199,8 +219,8 @@ module Contrast
199
219
  # This is our method that actually checks the taint on the object
200
220
  # our trigger_node targets for our Dataflow based rules.
201
221
  #
202
- # @param context [Contrast::Utils::ThreadTracker] the current request
203
- # context
222
+ # @param context [Contrast::Agent::RequestContext] the current
223
+ # request context
204
224
  # @param trigger_node [Contrast::Agent::Assess::Policy::TriggerNode]
205
225
  # the node to direct applying this trigger event
206
226
  # @param source [Object] the source of the Trigger Event
@@ -211,8 +231,8 @@ module Contrast
211
231
  def apply_dataflow_rule context, trigger_node, source, object, ret, *args
212
232
  return unless source
213
233
 
214
- if Contrast::Utils::DuckUtils.quacks_to?(source, :cs__properties)
215
- return unless source.cs__tracked?
234
+ if Contrast::Agent::Assess::Tracker.trackable?(source)
235
+ return unless Contrast::Agent::Assess::Tracker.tracked?(source)
216
236
  return unless trigger_node.violated?(source)
217
237
 
218
238
  build_finding(context, trigger_node, source, object, ret, *args)
@@ -226,30 +246,27 @@ module Contrast
226
246
  apply_dataflow_rule(context, trigger_node, value, object, ret, *args)
227
247
  end
228
248
  else
229
- logger.warn('Trigger source is of unknown type. Unable to inspect.',
230
- node_id: trigger_node.id,
231
- source_id: source.__id__,
232
- source_type: source.cs__class.to_s)
249
+ logger.debug('Trigger source is untrackable. Unable to inspect.',
250
+ node_id: trigger_node.id,
251
+ source_id: source.__id__,
252
+ source_type: source.cs__class.to_s,
253
+ frozen: source.cs__frozen?)
233
254
  logger.trace(source.to_s[0..99])
234
255
  end
235
256
  end
236
257
 
237
258
  def build_from_source finding, source
238
259
  return unless source
239
- return unless Contrast::Utils::DuckUtils.quacks_to?(
240
- source,
241
- :cs__properties)
242
- return unless source.cs__properties
260
+ return unless Contrast::Agent::Assess::Tracker.trackable?(source)
243
261
 
244
- # events could technically be nil, but we would have failed
245
- # the rule check before getting here. not worth the nil check
246
- source.cs__properties.events.each do |event|
247
- finding.events << event.to_dtm_event
248
- end
262
+ properties = Contrast::Agent::Assess::Tracker.properties(source)
263
+ return unless properties
264
+
265
+ build_events finding, properties.event if properties.event
249
266
 
250
267
  # Google::Protobuf::Map doesn't support merge!, so we have to do this
251
268
  # long form
252
- source_props = source.cs__properties.properties
269
+ source_props = properties.properties
253
270
  return unless source_props
254
271
 
255
272
  source_props.each_pair do |key, value|
@@ -258,11 +275,42 @@ module Contrast
258
275
  end
259
276
  end
260
277
 
278
+ def build_events finding, event
279
+ return unless event
280
+
281
+ event.parent_events&.each do |parent_event|
282
+ build_events(finding, parent_event)
283
+ end
284
+ # events could technically be nil, but we would have failed
285
+ # the rule check before getting here. not worth the nil check
286
+ finding.events << event.to_dtm_event
287
+ end
288
+
261
289
  def build_hash finding, source
262
290
  hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source)
263
291
  finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash_code)
264
292
  finding.preflight = Contrast::Utils::PreflightUtil.create_preflight(finding)
265
293
  end
294
+
295
+ # Because our APIs are not versioned, TeamServer relies on a field,
296
+ # version, to tell it what, if any, validation it can preform on
297
+ # the findings we send it. Examine the finding and determine the
298
+ # level to which it conforms.
299
+ #
300
+ # @param finding [Contrast::Api::Dtm::Finding]
301
+ # @return [int] the version of compliance
302
+ def determine_compliance_version finding
303
+ return MINIMUM_FINDING_VERSION unless finding
304
+ # as routes are the only variable between findings, in the case
305
+ # where we couldn't determine one, any finding with a route is at
306
+ # maximum compliance
307
+ return CURRENT_FINDING_VERSION if finding.routes.any?
308
+ # any finding without events is not of a dataflow type and
309
+ # therefore at maximum compliance
310
+ return CURRENT_FINDING_VERSION unless finding.events.any?
311
+
312
+ MINIMUM_FINDING_VERSION
313
+ end
266
314
  end
267
315
  end
268
316
  end
@@ -1,9 +1,9 @@
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/agent/assess/policy/trigger/reflected_xss'
5
- cs__scoped_require 'contrast/agent/assess/policy/trigger/xpath'
6
- cs__scoped_require 'contrast/api/decorators/trace_taint_range_tags'
4
+ require 'contrast/agent/assess/policy/trigger/reflected_xss'
5
+ require 'contrast/agent/assess/policy/trigger/xpath'
6
+ require 'contrast/api/decorators/trace_taint_range_tags'
7
7
 
8
8
  module Contrast
9
9
  module Agent
@@ -100,16 +100,17 @@ module Contrast
100
100
  # if the source isn't tracked, there can't be a violation
101
101
  # this condition may not hold true forever, but for now it's
102
102
  # a nice optimization
103
- return false unless source.cs__tracked?
103
+ return false unless Contrast::Agent::Assess::Tracker.tracked?(source)
104
104
 
105
+ properties = Contrast::Agent::Assess::Tracker.properties(source)
105
106
  # find the ranges that violate the rule (untrusted, etc)
106
- vulnerable_ranges = find_ranges_by_all_tags(Contrast::Utils::StringUtils.ret_length(source), source.cs__properties, required_tags)
107
+ vulnerable_ranges = ranges_with_all_tags(Contrast::Utils::StringUtils.ret_length(source), properties, required_tags)
107
108
  # if there aren't any vulnerable ranges, nope out
108
109
  return false if vulnerable_ranges.empty?
109
110
 
110
111
  # find the ranges that are exempt from the rule
111
112
  # (validated, sanitized, etc)
112
- secure_ranges = find_ranges_by_any_tag(source.cs__properties, disallowed_tags)
113
+ secure_ranges = ranges_with_any_tag(properties, disallowed_tags)
113
114
  # if there are vulnerable ranges and no secure, report
114
115
  return true if secure_ranges.empty?
115
116
 
@@ -178,68 +179,62 @@ module Contrast
178
179
  # @param length [Integer] the length of the object which may have the
179
180
  # given tags -- used as the maximum index to search for all of the
180
181
  # tags.
181
- # @param cs__properties [Contrast::Agent::Assess::Properties] the
182
+ # @param properties [Contrast::Agent::Assess::Properties] the
182
183
  # properties to check for the tags
183
- # @param tags [Set<String>] the list of tags on which to match
184
+ # @param required_tags [Set<String>] the list of tags on which to match
184
185
  # @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
185
186
  # by the given conditions
186
- def find_ranges_by_all_tags length, cs__properties, tags
187
- # if there aren't any all_tags or tags, break early
188
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless cs__properties.tracked?
189
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
190
-
191
- # :zap: faster to treat all as any if there's only one tag
192
- return find_ranges_by_any_tag(cs__properties, tags) if tags.length == 1
187
+ def ranges_with_all_tags length, properties, required_tags
188
+ # if there are no tags, not required tags, or the tags don't have
189
+ # all the required tags, we can just return here.
190
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
191
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags&.any?
192
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless required_tags.all? { |tag| properties.tag_keys.include?(tag) }
193
193
 
194
194
  ranges = []
195
- # TODO: RUBY-946 clean this up, perhaps with
196
- # tags.each { |tag| applicable << cs__properties.fetch_tag(tag) }
197
- # return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless applicable.length == tags.length
198
- # ...
195
+ chunking = false
199
196
  # find all the indicies on the source that have all the given tags
200
197
  (0..length).each do |idx|
201
- tags_at = cs__properties.tags_at(idx)
202
- ranges << idx if tags.all? do |tag|
203
- found = false
204
- tags_at.each do |tag_at|
205
- found = tag_at.label == tag
206
- break if found
207
- end
208
- found
198
+ tags_at = properties.tags_at(idx).to_a
199
+ # only those that have all the required tags in the tags_at
200
+ # satisfy the requirement
201
+ satisfied = tags_at.any? && required_tags.all? { |tag| tags_at.any? { |found| found.label == tag } }
202
+ # if this range matches all the required tags and we're already
203
+ # chunking, meaning the previous range matched, do nothing
204
+ next if satisfied && chunking
205
+
206
+ # if we are satisfied and we were not chunking, this represents
207
+ # the start of the next range, so create a new entry.
208
+ if satisfied
209
+ ranges << Contrast::Agent::Assess::Tag.new('required', 0, idx)
210
+ chunking = true
211
+ # if we are chunking and not satisfied, this represents the end
212
+ # of the range, meaning the last index is what satisfied the
213
+ # range. Because the range is exclusive end, we can just use this
214
+ # index.
215
+ elsif chunking
216
+ ranges[-1]&.update_end(idx)
217
+ chunking = false
209
218
  end
210
219
  end
211
- # break early if no indicies satisfy all the tags
212
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY if ranges.empty?
213
-
214
- # chunk all the adjacent ranges
215
- chunked = ranges.chunk_while { |i, j| i + 1 == j }
216
- tag_ranges = []
217
- # and convert them into Tags
218
- chunked.each do |join|
219
- start = join[0]
220
- stop = join[-1]
221
- # add the 1 to account for end index being exclusive
222
- tag_length = stop - start + 1
223
- tag_ranges = Contrast::Utils::TagUtil.ordered_merge(tag_ranges, Tag.new(tag_length, start))
224
- end
225
- tag_ranges
220
+ ranges
226
221
  end
227
222
 
228
223
  # Find the ranges that satisfy any of the given tags.
229
224
  #
230
- # @param cs__properties [Contrast::Agent::Assess::Properties] the
225
+ # @param properties [Contrast::Agent::Assess::Properties] the
231
226
  # properties to check for the tags
232
227
  # @param tags [Set<String>] the list of tags on which to match
233
228
  # @return [Array<Contrast::Agent::Assess::Tag>] the ranges satisfied
234
229
  # by the given conditions
235
- def find_ranges_by_any_tag cs__properties, tags
230
+ def ranges_with_any_tag properties, tags
236
231
  # if there aren't any all_tags or tags, break early
237
- return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless cs__properties.tracked?
232
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless properties.tracked?
238
233
  return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless tags&.any?
239
234
 
240
235
  ranges = []
241
236
  tags.each do |desired|
242
- found = cs__properties.fetch_tag(desired)
237
+ found = properties.fetch_tag(desired)
243
238
  next unless found
244
239
 
245
240
  # we need to dup here so that we don't change the tags if target is