contrast-agent 4.3.2 → 4.7.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 (317) hide show
  1. checksums.yaml +4 -4
  2. data/.gitmodules +1 -1
  3. data/.simplecov +1 -1
  4. data/Gemfile +1 -1
  5. data/LICENSE.txt +1 -1
  6. data/Rakefile +2 -3
  7. data/exe/contrast_service +1 -1
  8. data/ext/build_funchook.rb +4 -4
  9. data/ext/cs__assess_active_record_named/cs__active_record_named.c +1 -1
  10. data/ext/cs__assess_active_record_named/extconf.rb +1 -1
  11. data/ext/cs__assess_array/cs__assess_array.c +1 -1
  12. data/ext/cs__assess_array/extconf.rb +1 -1
  13. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +1 -1
  14. data/ext/cs__assess_basic_object/extconf.rb +1 -1
  15. data/ext/cs__assess_fiber_track/cs__assess_fiber_track.c +1 -1
  16. data/ext/cs__assess_fiber_track/extconf.rb +1 -1
  17. data/ext/cs__assess_hash/cs__assess_hash.c +4 -2
  18. data/ext/cs__assess_hash/extconf.rb +1 -1
  19. data/ext/cs__assess_kernel/cs__assess_kernel.c +1 -1
  20. data/ext/cs__assess_kernel/extconf.rb +1 -1
  21. data/ext/cs__assess_marshal_module/cs__assess_marshal_module.c +1 -1
  22. data/ext/cs__assess_marshal_module/extconf.rb +1 -1
  23. data/ext/cs__assess_module/cs__assess_module.c +1 -1
  24. data/ext/cs__assess_module/extconf.rb +1 -1
  25. data/ext/cs__assess_regexp/cs__assess_regexp.c +1 -1
  26. data/ext/cs__assess_regexp/extconf.rb +1 -1
  27. data/ext/cs__assess_string/cs__assess_string.c +1 -1
  28. data/ext/cs__assess_string/extconf.rb +1 -1
  29. data/ext/cs__assess_string_interpolation26/cs__assess_string_interpolation26.c +1 -1
  30. data/ext/cs__assess_string_interpolation26/extconf.rb +1 -1
  31. data/ext/cs__assess_yield_track/cs__assess_yield_track.c +1 -1
  32. data/ext/cs__assess_yield_track/extconf.rb +1 -1
  33. data/ext/cs__common/cs__common.c +5 -5
  34. data/ext/cs__common/cs__common.h +4 -4
  35. data/ext/cs__common/extconf.rb +1 -1
  36. data/ext/cs__contrast_patch/cs__contrast_patch.c +22 -25
  37. data/ext/cs__contrast_patch/extconf.rb +1 -1
  38. data/ext/cs__protect_kernel/cs__protect_kernel.c +1 -1
  39. data/ext/cs__protect_kernel/extconf.rb +1 -1
  40. data/ext/extconf_common.rb +2 -6
  41. data/lib/contrast-agent.rb +1 -1
  42. data/lib/contrast.rb +20 -1
  43. data/lib/contrast/agent.rb +6 -4
  44. data/lib/contrast/agent/assess.rb +2 -11
  45. data/lib/contrast/agent/assess/contrast_event.rb +54 -71
  46. data/lib/contrast/agent/assess/contrast_object.rb +7 -4
  47. data/lib/contrast/agent/assess/events/event_factory.rb +3 -2
  48. data/lib/contrast/agent/assess/events/source_event.rb +7 -2
  49. data/lib/contrast/agent/assess/finalizers/freeze.rb +1 -1
  50. data/lib/contrast/agent/assess/finalizers/hash.rb +33 -34
  51. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +34 -16
  52. data/lib/contrast/agent/assess/policy/patcher.rb +11 -18
  53. data/lib/contrast/agent/assess/policy/policy.rb +1 -1
  54. data/lib/contrast/agent/assess/policy/policy_node.rb +26 -34
  55. data/lib/contrast/agent/assess/policy/policy_scanner.rb +1 -1
  56. data/lib/contrast/agent/assess/policy/preshift.rb +4 -2
  57. data/lib/contrast/agent/assess/policy/propagation_method.rb +32 -30
  58. data/lib/contrast/agent/assess/policy/propagation_node.rb +20 -9
  59. data/lib/contrast/agent/assess/policy/propagator.rb +1 -1
  60. data/lib/contrast/agent/assess/policy/propagator/append.rb +29 -14
  61. data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
  62. data/lib/contrast/agent/assess/policy/propagator/center.rb +3 -2
  63. data/lib/contrast/agent/assess/policy/propagator/custom.rb +1 -1
  64. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +22 -17
  65. data/lib/contrast/agent/assess/policy/propagator/insert.rb +4 -2
  66. data/lib/contrast/agent/assess/policy/propagator/keep.rb +1 -1
  67. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +3 -2
  68. data/lib/contrast/agent/assess/policy/propagator/next.rb +1 -1
  69. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +1 -1
  70. data/lib/contrast/agent/assess/policy/propagator/remove.rb +23 -19
  71. data/lib/contrast/agent/assess/policy/propagator/replace.rb +1 -1
  72. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +1 -1
  73. data/lib/contrast/agent/assess/policy/propagator/select.rb +3 -13
  74. data/lib/contrast/agent/assess/policy/propagator/splat.rb +24 -14
  75. data/lib/contrast/agent/assess/policy/propagator/split.rb +18 -15
  76. data/lib/contrast/agent/assess/policy/propagator/substitution.rb +32 -22
  77. data/lib/contrast/agent/assess/policy/propagator/trim.rb +64 -45
  78. data/lib/contrast/agent/assess/policy/rewriter_patch.rb +7 -4
  79. data/lib/contrast/agent/assess/policy/source_method.rb +92 -81
  80. data/lib/contrast/agent/assess/policy/source_node.rb +1 -1
  81. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +8 -6
  82. data/lib/contrast/agent/assess/policy/source_validation/source_validation.rb +2 -4
  83. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -3
  84. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +7 -8
  85. data/lib/contrast/agent/assess/policy/trigger_method.rb +109 -76
  86. data/lib/contrast/agent/assess/policy/trigger_node.rb +33 -11
  87. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +60 -0
  88. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +3 -5
  89. data/lib/contrast/agent/assess/policy/trigger_validation/trigger_validation.rb +7 -5
  90. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +4 -13
  91. data/lib/contrast/agent/assess/properties.rb +1 -3
  92. data/lib/contrast/agent/assess/property/evented.rb +9 -6
  93. data/lib/contrast/agent/assess/property/tagged.rb +38 -20
  94. data/lib/contrast/agent/assess/property/updated.rb +1 -1
  95. data/lib/contrast/agent/assess/rule/provider.rb +1 -1
  96. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +12 -6
  97. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +5 -2
  98. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +4 -6
  99. data/lib/contrast/agent/assess/tag.rb +1 -1
  100. data/lib/contrast/agent/assess/tracker.rb +2 -2
  101. data/lib/contrast/agent/at_exit_hook.rb +1 -1
  102. data/lib/contrast/agent/class_reopener.rb +4 -2
  103. data/lib/contrast/agent/deadzone/policy/deadzone_node.rb +1 -1
  104. data/lib/contrast/agent/deadzone/policy/policy.rb +7 -3
  105. data/lib/contrast/agent/disable_reaction.rb +2 -4
  106. data/lib/contrast/agent/exclusion_matcher.rb +6 -12
  107. data/lib/contrast/agent/inventory.rb +1 -2
  108. data/lib/contrast/agent/inventory/dependencies.rb +3 -1
  109. data/lib/contrast/agent/inventory/dependency_analysis.rb +1 -1
  110. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +35 -23
  111. data/lib/contrast/agent/inventory/policy/datastores.rb +1 -1
  112. data/lib/contrast/agent/inventory/policy/policy.rb +1 -1
  113. data/lib/contrast/agent/inventory/policy/trigger_node.rb +1 -1
  114. data/lib/contrast/agent/middleware.rb +111 -110
  115. data/lib/contrast/agent/module_data.rb +4 -4
  116. data/lib/contrast/agent/patching/policy/after_load_patch.rb +1 -1
  117. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +9 -4
  118. data/lib/contrast/agent/patching/policy/method_policy.rb +7 -3
  119. data/lib/contrast/agent/patching/policy/module_policy.rb +15 -8
  120. data/lib/contrast/agent/patching/policy/patch.rb +23 -29
  121. data/lib/contrast/agent/patching/policy/patch_status.rb +8 -9
  122. data/lib/contrast/agent/patching/policy/patcher.rb +72 -64
  123. data/lib/contrast/agent/patching/policy/policy.rb +14 -21
  124. data/lib/contrast/agent/patching/policy/policy_node.rb +15 -5
  125. data/lib/contrast/agent/patching/policy/trigger_node.rb +26 -10
  126. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +2 -2
  127. data/lib/contrast/agent/protect/policy/applies_deserialization_rule.rb +2 -2
  128. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
  129. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +3 -4
  130. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +2 -2
  131. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +6 -10
  132. data/lib/contrast/agent/protect/policy/policy.rb +1 -1
  133. data/lib/contrast/agent/protect/policy/rule_applicator.rb +6 -6
  134. data/lib/contrast/agent/protect/policy/trigger_node.rb +1 -1
  135. data/lib/contrast/agent/protect/rule.rb +1 -1
  136. data/lib/contrast/agent/protect/rule/base.rb +19 -33
  137. data/lib/contrast/agent/protect/rule/base_service.rb +10 -6
  138. data/lib/contrast/agent/protect/rule/cmd_injection.rb +15 -19
  139. data/lib/contrast/agent/protect/rule/default_scanner.rb +1 -1
  140. data/lib/contrast/agent/protect/rule/deserialization.rb +7 -14
  141. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +4 -15
  142. data/lib/contrast/agent/protect/rule/no_sqli.rb +7 -3
  143. data/lib/contrast/agent/protect/rule/no_sqli/mongo_no_sql_scanner.rb +2 -4
  144. data/lib/contrast/agent/protect/rule/path_traversal.rb +6 -6
  145. data/lib/contrast/agent/protect/rule/sqli.rb +19 -13
  146. data/lib/contrast/agent/protect/rule/sqli/default_sql_scanner.rb +1 -1
  147. data/lib/contrast/agent/protect/rule/sqli/mysql_sql_scanner.rb +1 -1
  148. data/lib/contrast/agent/protect/rule/sqli/postgres_sql_scanner.rb +2 -2
  149. data/lib/contrast/agent/protect/rule/sqli/sqlite_sql_scanner.rb +1 -1
  150. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +2 -2
  151. data/lib/contrast/agent/protect/rule/xss.rb +2 -2
  152. data/lib/contrast/agent/protect/rule/xxe.rb +6 -13
  153. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +2 -3
  154. data/lib/contrast/agent/railtie.rb +1 -1
  155. data/lib/contrast/agent/reaction_processor.rb +12 -11
  156. data/lib/contrast/agent/request.rb +25 -24
  157. data/lib/contrast/agent/request_context.rb +25 -23
  158. data/lib/contrast/agent/request_handler.rb +1 -1
  159. data/lib/contrast/agent/response.rb +1 -1
  160. data/lib/contrast/agent/rewriter.rb +6 -4
  161. data/lib/contrast/agent/rule_set.rb +3 -3
  162. data/lib/contrast/agent/scope.rb +1 -1
  163. data/lib/contrast/agent/service_heartbeat.rb +3 -4
  164. data/lib/contrast/agent/static_analysis.rb +1 -1
  165. data/lib/contrast/agent/thread.rb +2 -2
  166. data/lib/contrast/agent/thread_watcher.rb +21 -6
  167. data/lib/contrast/agent/tracepoint_hook.rb +2 -2
  168. data/lib/contrast/agent/version.rb +2 -2
  169. data/lib/contrast/agent/worker_thread.rb +1 -1
  170. data/lib/contrast/api.rb +1 -1
  171. data/lib/contrast/api/communication.rb +1 -1
  172. data/lib/contrast/api/communication/connection_status.rb +1 -1
  173. data/lib/contrast/api/communication/messaging_queue.rb +19 -22
  174. data/lib/contrast/api/communication/response_processor.rb +13 -8
  175. data/lib/contrast/api/communication/service_lifecycle.rb +5 -3
  176. data/lib/contrast/api/communication/socket.rb +1 -1
  177. data/lib/contrast/api/communication/socket_client.rb +30 -35
  178. data/lib/contrast/api/communication/speedracer.rb +6 -10
  179. data/lib/contrast/api/communication/tcp_socket.rb +1 -1
  180. data/lib/contrast/api/communication/unix_socket.rb +1 -1
  181. data/lib/contrast/api/decorators.rb +3 -1
  182. data/lib/contrast/api/decorators/address.rb +1 -1
  183. data/lib/contrast/api/decorators/agent_startup.rb +58 -0
  184. data/lib/contrast/api/decorators/application_settings.rb +1 -1
  185. data/lib/contrast/api/decorators/application_startup.rb +57 -0
  186. data/lib/contrast/api/decorators/application_update.rb +1 -1
  187. data/lib/contrast/api/decorators/http_request.rb +1 -1
  188. data/lib/contrast/api/decorators/input_analysis.rb +1 -1
  189. data/lib/contrast/api/decorators/instrumentation_mode.rb +37 -0
  190. data/lib/contrast/api/decorators/library.rb +9 -7
  191. data/lib/contrast/api/decorators/library_usage_update.rb +1 -1
  192. data/lib/contrast/api/decorators/message.rb +4 -4
  193. data/lib/contrast/api/decorators/rasp_rule_sample.rb +1 -1
  194. data/lib/contrast/api/decorators/route_coverage.rb +16 -6
  195. data/lib/contrast/api/decorators/server_features.rb +1 -1
  196. data/lib/contrast/api/decorators/trace_event.rb +46 -16
  197. data/lib/contrast/api/decorators/trace_event_object.rb +2 -4
  198. data/lib/contrast/api/decorators/trace_event_signature.rb +1 -1
  199. data/lib/contrast/api/decorators/trace_taint_range.rb +1 -1
  200. data/lib/contrast/api/decorators/trace_taint_range_tags.rb +2 -7
  201. data/lib/contrast/api/decorators/user_input.rb +1 -1
  202. data/lib/contrast/components/agent.rb +16 -15
  203. data/lib/contrast/components/app_context.rb +11 -29
  204. data/lib/contrast/components/assess.rb +6 -11
  205. data/lib/contrast/components/config.rb +3 -2
  206. data/lib/contrast/components/contrast_service.rb +8 -9
  207. data/lib/contrast/components/heap_dump.rb +1 -1
  208. data/lib/contrast/components/interface.rb +4 -3
  209. data/lib/contrast/components/inventory.rb +1 -1
  210. data/lib/contrast/components/logger.rb +1 -1
  211. data/lib/contrast/components/protect.rb +11 -14
  212. data/lib/contrast/components/sampling.rb +55 -7
  213. data/lib/contrast/components/scope.rb +2 -1
  214. data/lib/contrast/components/settings.rb +29 -99
  215. data/lib/contrast/config.rb +1 -1
  216. data/lib/contrast/config/agent_configuration.rb +1 -1
  217. data/lib/contrast/config/application_configuration.rb +1 -1
  218. data/lib/contrast/config/assess_configuration.rb +1 -1
  219. data/lib/contrast/config/assess_rules_configuration.rb +2 -4
  220. data/lib/contrast/config/base_configuration.rb +5 -6
  221. data/lib/contrast/config/default_value.rb +1 -1
  222. data/lib/contrast/config/exception_configuration.rb +2 -6
  223. data/lib/contrast/config/heap_dump_configuration.rb +13 -7
  224. data/lib/contrast/config/inventory_configuration.rb +1 -1
  225. data/lib/contrast/config/logger_configuration.rb +2 -6
  226. data/lib/contrast/config/protect_configuration.rb +1 -1
  227. data/lib/contrast/config/protect_rule_configuration.rb +23 -1
  228. data/lib/contrast/config/protect_rules_configuration.rb +1 -1
  229. data/lib/contrast/config/root_configuration.rb +1 -1
  230. data/lib/contrast/config/ruby_configuration.rb +1 -1
  231. data/lib/contrast/config/sampling_configuration.rb +1 -1
  232. data/lib/contrast/config/server_configuration.rb +1 -1
  233. data/lib/contrast/config/service_configuration.rb +1 -1
  234. data/lib/contrast/configuration.rb +4 -15
  235. data/lib/contrast/delegators/input_analysis.rb +12 -0
  236. data/lib/contrast/extension/assess.rb +1 -1
  237. data/lib/contrast/extension/assess/array.rb +2 -7
  238. data/lib/contrast/extension/assess/erb.rb +2 -8
  239. data/lib/contrast/extension/assess/eval_trigger.rb +3 -11
  240. data/lib/contrast/extension/assess/exec_trigger.rb +4 -14
  241. data/lib/contrast/extension/assess/fiber.rb +3 -13
  242. data/lib/contrast/extension/assess/hash.rb +1 -1
  243. data/lib/contrast/extension/assess/kernel.rb +3 -10
  244. data/lib/contrast/extension/assess/marshal.rb +3 -11
  245. data/lib/contrast/extension/assess/regexp.rb +2 -7
  246. data/lib/contrast/extension/assess/string.rb +4 -2
  247. data/lib/contrast/extension/delegator.rb +1 -1
  248. data/lib/contrast/extension/inventory.rb +1 -1
  249. data/lib/contrast/extension/kernel.rb +5 -3
  250. data/lib/contrast/extension/module.rb +1 -1
  251. data/lib/contrast/extension/protect.rb +1 -1
  252. data/lib/contrast/extension/protect/kernel.rb +1 -1
  253. data/lib/contrast/extension/protect/psych.rb +1 -1
  254. data/lib/contrast/extension/thread.rb +1 -1
  255. data/lib/contrast/framework/base_support.rb +1 -1
  256. data/lib/contrast/framework/manager.rb +14 -17
  257. data/lib/contrast/framework/platform_version.rb +1 -1
  258. data/lib/contrast/framework/rack/patch/session_cookie.rb +6 -19
  259. data/lib/contrast/framework/rack/patch/support.rb +7 -5
  260. data/lib/contrast/framework/rack/support.rb +1 -1
  261. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -1
  262. data/lib/contrast/framework/rails/patch/assess_configuration.rb +8 -3
  263. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +4 -4
  264. data/lib/contrast/framework/rails/patch/support.rb +5 -3
  265. data/lib/contrast/framework/rails/rewrite/action_controller_railties_helper_inherited.rb +5 -2
  266. data/lib/contrast/framework/rails/rewrite/active_record_attribute_methods_read.rb +3 -1
  267. data/lib/contrast/framework/rails/rewrite/active_record_named.rb +3 -1
  268. data/lib/contrast/framework/rails/rewrite/active_record_time_zone_inherited.rb +3 -1
  269. data/lib/contrast/framework/rails/support.rb +45 -46
  270. data/lib/contrast/framework/sinatra/support.rb +103 -42
  271. data/lib/contrast/funchook/funchook.rb +2 -6
  272. data/lib/contrast/logger/application.rb +13 -10
  273. data/lib/contrast/logger/format.rb +3 -6
  274. data/lib/contrast/logger/log.rb +36 -19
  275. data/lib/contrast/logger/request.rb +2 -3
  276. data/lib/contrast/logger/time.rb +1 -1
  277. data/lib/contrast/security_exception.rb +2 -2
  278. data/lib/contrast/tasks/config.rb +1 -1
  279. data/lib/contrast/tasks/service.rb +6 -2
  280. data/lib/contrast/utils/assess/sampling_util.rb +1 -1
  281. data/lib/contrast/utils/assess/tracking_util.rb +2 -3
  282. data/lib/contrast/utils/class_util.rb +18 -12
  283. data/lib/contrast/utils/duck_utils.rb +1 -1
  284. data/lib/contrast/utils/env_configuration_item.rb +1 -1
  285. data/lib/contrast/utils/hash_digest.rb +16 -24
  286. data/lib/contrast/utils/heap_dump_util.rb +104 -88
  287. data/lib/contrast/utils/invalid_configuration_util.rb +22 -13
  288. data/lib/contrast/utils/inventory_util.rb +1 -1
  289. data/lib/contrast/utils/io_util.rb +2 -2
  290. data/lib/contrast/utils/job_servers_running.rb +10 -5
  291. data/lib/contrast/utils/object_share.rb +1 -1
  292. data/lib/contrast/utils/os.rb +3 -2
  293. data/lib/contrast/utils/preflight_util.rb +1 -1
  294. data/lib/contrast/utils/resource_loader.rb +1 -1
  295. data/lib/contrast/utils/ruby_ast_rewriter.rb +3 -2
  296. data/lib/contrast/utils/sha256_builder.rb +1 -1
  297. data/lib/contrast/utils/stack_trace_utils.rb +1 -1
  298. data/lib/contrast/utils/string_utils.rb +1 -1
  299. data/lib/contrast/utils/tag_util.rb +1 -1
  300. data/lib/contrast/utils/thread_tracker.rb +1 -1
  301. data/lib/contrast/utils/timer.rb +1 -1
  302. data/resources/assess/policy.json +8 -11
  303. data/resources/deadzone/policy.json +7 -17
  304. data/ruby-agent.gemspec +66 -27
  305. data/service_executables/VERSION +1 -1
  306. data/service_executables/linux/contrast-service +0 -0
  307. data/service_executables/mac/contrast-service +0 -0
  308. data/sonar-project.properties +9 -0
  309. metadata +154 -156
  310. data/lib/contrast/agent/assess/rule.rb +0 -18
  311. data/lib/contrast/agent/assess/rule/base.rb +0 -52
  312. data/lib/contrast/agent/assess/rule/redos.rb +0 -67
  313. data/lib/contrast/agent/inventory/gemfile_digest_cache.rb +0 -38
  314. data/lib/contrast/common_agent_configuration.rb +0 -87
  315. data/lib/contrast/framework/sinatra/patch/base.rb +0 -83
  316. data/lib/contrast/framework/sinatra/patch/support.rb +0 -27
  317. data/lib/contrast/utils/prevent_serialization.rb +0 -52
@@ -1,6 +1,8 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
+ return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
5
+
4
6
  require 'contrast/components/interface'
5
7
 
6
8
  module Contrast
@@ -1,6 +1,8 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
+ return unless RUBY_VERSION < '2.6.0' # TODO: RUBY-714 remove guard w/ EOL of 2.5
5
+
4
6
  module Contrast
5
7
  module Framework
6
8
  module Rails
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/api/dtm.pb'
@@ -27,8 +27,8 @@ module Contrast
27
27
 
28
28
  def application_name
29
29
  app_class = ::Rails.application.cs__class
30
- # Rails version 6.0.0 deprecated Rails::Application#parent_name, in Rails 6.1.0 that method will be removed entirely
31
- # and instead we need to use parent_module_name
30
+ # Rails version 6.0.0 deprecated Rails::Application#parent_name, in Rails 6.1.0 that method will be removed
31
+ # entirely and instead we need to use parent_module_name
32
32
  return app_class.parent_module_name if Gem::Version.new(::Rails.version) >= RAILS_MODULE_NAME_VERSION
33
33
 
34
34
  app_class.parent_name
@@ -47,32 +47,34 @@ module Contrast
47
47
  end
48
48
 
49
49
  # Find the current route, based on the provided Request wrapper
50
+ #
50
51
  # @param request[Contrast::Agent::Request]
51
52
  # @return [Contrast::Api::Dtm::RouteCoverage]
52
53
  def current_route request
53
54
  return unless ::Rails.cs__respond_to?(:application)
54
55
 
55
- # returns array of arrays [[match_data, path_parameters, route]], sorted by
56
- # precedence
57
- # match_data: ActionDispatch::Journey::Path::Pattern::MatchData
58
- # path_parameters: hash of various things
59
- # route: ActionDispatch::Journey::Route
60
- full_routes = ::Rails.application.routes.router.send(:find_routes, request.rack_request)
61
- return if full_routes.empty?
56
+ match, _params, route, path = get_full_route(request.rack_request)
62
57
 
63
- full_route = full_routes[0]
58
+ original_url = request.rack_request.path_info
64
59
 
65
- # the route is directly implemented within the application
66
- if direct_route?(full_route)
67
- route = full_route[2] # route w/ highest precedence
68
- Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route)
69
- else
70
- engine_route(full_route, request)
60
+ # Route is either the final rails route, or a router that points to a Sinatra controller.
61
+ if Contrast::Framework::Sinatra::Support.sinatra_controller?(route.app.app)
62
+ # Create a request copied from current request, but with the base path removed from path_info.
63
+ new_req = ::ActionDispatch::Request.new(request.env)
64
+ new_req.path_info = new_req.path_info.gsub((path << match).join, '')
65
+
66
+ return Contrast::Framework::Sinatra::Support.current_route(new_req, route.app.app, original_url)
71
67
  end
68
+
69
+ Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(route, original_url)
72
70
  rescue StandardError => _e
73
71
  nil
74
72
  end
75
73
 
74
+ # Copy a request for modification.
75
+ #
76
+ # @param [::ActionDispatch::Request] original env.
77
+ # @return [::ActionDispatch::Request] a copy of original env with rails env merged.
76
78
  def retrieve_request env
77
79
  rails_env = ::Rails.application.env_config.merge(env)
78
80
  ::ActionDispatch::Request.new(rails_env || env)
@@ -87,37 +89,34 @@ module Contrast
87
89
 
88
90
  private
89
91
 
90
- # route is not mounted within an engine
91
- def direct_route? full_route
92
- full_route[2]&.app&.cs__class == ActionDispatch::Routing::RouteSet::Dispatcher ||
93
- (full_route[2].cs__class == ActionDispatch::Journey::Route && full_route[2]&.app&.cs__class == ActionDispatch::Routing::Mapper::Constraints)
92
+ # Determine if route is a Rails engine route.
93
+ #
94
+ # @param [Object] app or route that points to a ::Rails::Engine
95
+ # @return [bool] whether the router is an engine or not.
96
+ def engine_route? route
97
+ route.app.is_a?(::ActionDispatch::Routing::Mapper::Constraints) && route.app.app < ::Rails::Engine
94
98
  end
95
99
 
96
- def engine_route full_route, request
97
- engine_route = full_route[2] # supposed route - but actually an Engine mount point
98
- return unless engine_route
99
-
100
- engine_mount_name = engine_route.name
101
- return unless engine_mount_name
102
-
103
- engine_path_segments = request.rack_request.path_info.split(engine_mount_name)
104
- return if engine_path_segments.empty?
105
-
106
- path_within_engine = engine_path_segments[-1]
107
- return unless path_within_engine
108
-
109
- engine_router = engine_route.app&.app&.routes&.router
110
- return unless engine_router
111
-
112
- # Get all routes regardless of http method
113
- matching_routes = engine_router.send(:filter_routes, path_within_engine)
114
- return unless matching_routes
115
-
116
- # filter for current http method
117
- reportable_routes = engine_router.send(:match_routes, matching_routes, request.rack_request)
118
- return if reportable_routes.empty?
119
-
120
- Contrast::Api::Dtm::RouteCoverage.from_action_dispatch_journey(reportable_routes[0])
100
+ # Recursively get final route traversing engines as required.
101
+ #
102
+ # @param request [::Rack::Request] the rack request as will be handed to rails controller.
103
+ # @param top_router [::ActionDispatch::Journer::Router] the current router relative to the previous.
104
+ # @param path [Array<String>] the chunks of path that have been seen.
105
+ # @return [Array<array>] the final set of rails route classes.
106
+ def get_full_route request, top_router = ::Rails.application.routes.router, path = []
107
+ return if (route_matches = top_router.send(:find_routes, request)).empty?
108
+
109
+ match, params, route = route_matches.first
110
+
111
+ # If the current routing node points to a sub-app (::Rais::Engine), dive deeper.
112
+ # Have sub-app route the remainder of the url.
113
+ if engine_route?(route)
114
+ new_req = retrieve_request request.env
115
+ new_req.path_info = new_req.path_info.gsub(match.to_s, '')
116
+ get_full_route(new_req, route.app.app.routes.router, path << match.to_s)
117
+ else
118
+ [match, params, route, path]
119
+ end
121
120
  end
122
121
 
123
122
  # Rails engine routes need to be detected by inspecting Engine class route set
@@ -1,8 +1,7 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/framework/base_support'
5
- require 'contrast/framework/sinatra/patch/support'
6
5
 
7
6
  module Contrast
8
7
  module Framework
@@ -10,7 +9,6 @@ module Contrast
10
9
  # Used when Sinatra is present to define framework specific behavior
11
10
  class Support
12
11
  extend Contrast::Framework::BaseSupport
13
- extend Contrast::Framework::Sinatra::Patch::Support
14
12
  class << self
15
13
  def detection_class
16
14
  'Sinatra'
@@ -21,44 +19,74 @@ module Contrast
21
19
  end
22
20
 
23
21
  def application_name
24
- return unless app_class
25
-
26
- app_class.cs__class.cs__name
22
+ app_class&.cs__name
27
23
  end
28
24
 
29
25
  def application_root
30
- return unless app_class
31
-
32
- app_class.root
26
+ app_instance&.root
33
27
  end
34
28
 
35
29
  def server_type
36
30
  'sinatra'
37
31
  end
38
32
 
39
- # Iterate over every class that extends Sinatra::Base, pull out its routes
40
- # (array of arrays with Mustermann::Sinatra as [][0]) and convert them into
41
- # Contrast::Api::Dtm::RouteCoverage
33
+ # Given an object, determine if it is a Sinatra controller with routes.
34
+ #
35
+ # @param app [Object] suspected Sinatra app.
36
+ # @return [Boolean]
37
+ def sinatra_controller? app
38
+ # Sinatra is loaded?
39
+ return false unless defined?(::Sinatra) && defined?(::Sinatra::Base)
40
+ # App is a subclass of or actually is ::Sinatra::Base.
41
+ return false unless (app.cs__respond_to?(:<) && app < ::Sinatra::Base) || app == ::Sinatra::Base
42
+
43
+ # App has routes.
44
+ !app.routes.empty?
45
+ end
46
+
47
+ # Find all classes that subclass ::Sinatra::Base. Gather their routes.
48
+ #
49
+ # @return [Array<Contrast::Api::Dtm::RouteCoverage>] the routes found as Dtms.
42
50
  def collect_routes
51
+ return Contrast::Utils::ObjectShare::EMPTY_ARRAY unless defined?(::Sinatra) && defined?(::Sinatra::Base)
52
+
43
53
  routes = []
44
- controllers = sinatra_controllers
45
- controllers.each do |clazz|
46
- class_routes = sinatra_class_routes(clazz)
47
- next unless class_routes
48
-
49
- class_routes.each_pair do |method, list|
50
- # item: [ Mustermann::Sinatra, [], Proc]
51
- list.each do |item|
52
- routes << Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(clazz, method, item[0])
54
+ sinatra_controllers.each do |controller|
55
+ controller.routes.each_pair do |method, route_triplets|
56
+ # Sinatra stores its routes as a triplet: [Mustermann::Sinatra, [], Proc]
57
+ route_triplets.map(&:first).each do |route_pattern|
58
+ routes << Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(controller, method, route_pattern)
53
59
  end
54
60
  end
55
61
  end
56
62
  routes
57
63
  end
58
64
 
59
- # TODO: RUBY-763
60
- def current_route _request
61
- nil
65
+ # Given the current request return a RouteCoverage dtm.
66
+ #
67
+ # @param request [Contrast::Agent::Request] a contrast tracked request.
68
+ # @param controller [::Sinatra::Base] optionally use this controller instead of global ::Sinatra::Base.
69
+ # @return [Contrast::Api::Dtm::RouteCoverage, nil] a Dtm describing the route
70
+ # matched to the request if a match was found.
71
+ def current_route request, controller = ::Sinatra::Base, full_route = nil
72
+ return unless sinatra_controller?(controller)
73
+
74
+ method = request.env[::Rack::REQUEST_METHOD] # GET, PUT, POST, etc...
75
+
76
+ # Find route match--checking superclasses if necessary.
77
+ final_controller, route_pattern = _route_recurse(controller, method, _cleaned_route(request))
78
+ return unless !final_controller.nil? && !route_pattern.nil?
79
+
80
+ full_route ||= request.path_info
81
+
82
+ Contrast::Api::Dtm::RouteCoverage.from_sinatra_route(final_controller, method, route_pattern, full_route)
83
+ end
84
+
85
+ # Search object space for sinatra controllers--any class that subclasses ::Sinatra::Base.
86
+ #
87
+ # @return [Array<::Sinatra::Base>] sinatra controlelrs
88
+ def sinatra_controllers
89
+ [::Sinatra::Base] + ObjectSpace.each_object(Class).select { |clazz| sinatra_controller?(clazz) }
62
90
  end
63
91
 
64
92
  def retrieve_request env
@@ -67,30 +95,63 @@ module Contrast
67
95
 
68
96
  private
69
97
 
70
- def app_class
71
- return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
72
-
73
- @_app_class ||= begin
74
- sinatra_layers = ObjectSpace.each_object(::Sinatra::Base).to_a
75
- result_layer = sinatra_layers.find { |layer| layer.app.nil? }
76
- result_layer
98
+ # Given a controller and a route to match against, find the route_pattern and class that will serve the
99
+ # route. This is recursive as Sinatra's routing is recursive from subclass to super.
100
+ #
101
+ # @param controller [Sinatra::Base, #routes] a Sinatra application.
102
+ # @param method [::Rack::REQUEST_METHOD] GET, POST, PUT, etc...
103
+ # @param method [String] the relative route passed from Rack.
104
+ # @return [Array[Sinatra::Base, Mustermann::Sinatra], nil] Either the controller that
105
+ # will handle the route along with the route pattern or nil if no match.
106
+ def _route_recurse controller, method, route
107
+ return if controller.nil? || controller.cs__class == NilClass
108
+
109
+ route_patterns = controller.routes.fetch(method, []).map(&:first)
110
+ route_pattern = route_patterns&.find do |matcher|
111
+ matcher.params(route) # ::Mustermann::Sinatra match.
77
112
  end
113
+
114
+ return controller, route_pattern if route_pattern
115
+
116
+ # Check routes defined in superclass if present.
117
+ return unless controller.superclass&.instance_variable_get(:@routes)
118
+
119
+ _route_recurse(controller.superclass, method, route)
78
120
  end
79
121
 
80
- # Iterate over every class that extends Sinatra::Base, pull out its routes
81
- # (array of arrays with Mustermann::Sinatra as [][0]) and convert them into
82
- # Contrast::Api::Dtm::RouteCoverage
83
- def sinatra_controllers
84
- return [] unless defined?(::Sinatra) && defined?(::Sinatra::Base)
122
+ # Get route and do some cleanup matching that of Sinatra::Base#process_route.
123
+ #
124
+ # @param request [Contrast::Agent::Request] a contrast tracked request.
125
+ # @return [String] the extracted and cleaned relative route.
126
+ def _cleaned_route request
127
+ settings = ::Sinatra::Base.settings
128
+ route = request.env[::Rack::PATH_INFO]
129
+ return '/' if route.empty? && !settings.empty_path_info?
130
+
131
+ !settings.strict_paths? && route.end_with?('/') ? route[0..-2] : route
132
+ end
85
133
 
86
- Contrast::Utils::ClassUtil.descendants(::Sinatra::Base)
134
+ # Almost an alias to app_instance.
135
+ #
136
+ # @return [::Sinatra::Base] the current controller class as routed by Rack.
137
+ def app_class
138
+ return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
139
+
140
+ app_instance.cs__class
87
141
  end
88
142
 
89
- def sinatra_class_routes controller
90
- controller.instance_variable_get(:@routes) if controller.instance_variable_defined?(:@routes)
91
- rescue StandardError
92
- logger.trace('Sinatra controller found with no route instances', module: controller)
93
- nil
143
+ # Search the object space for the controller handling this request which will be
144
+ # the class inheriting from ::Sinatra::Base with @app=nil since it is the final servicer
145
+ # in the request/middleware chain.
146
+ #
147
+ # @return [::Sinatra::Base] the current controller as routed by Rack.
148
+ def app_instance
149
+ return unless defined?(::Sinatra) && defined?(::Sinatra::Base)
150
+
151
+ @_app_instance ||= begin
152
+ sinatra_layers = ObjectSpace.each_object(::Sinatra::Base).to_a
153
+ sinatra_layers.find { |layer| layer.app.nil? }
154
+ end
94
155
  end
95
156
  end
96
157
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/interface'
@@ -12,11 +12,7 @@ module Funchook
12
12
  ACCEPTABLE_FILES = %w[libfunchook.dylib libfunchook.so].cs__freeze
13
13
 
14
14
  # Top level agent directories that should have the funchook libraries
15
- SEARCH_DIRS = [
16
- File.join('ext'),
17
- File.join('shared_libraries'),
18
- File.join('funchook', 'src')
19
- ].cs__freeze
15
+ SEARCH_DIRS = [File.join('ext'), File.join('shared_libraries'), File.join('funchook', 'src')].cs__freeze
20
16
 
21
17
  AGENT_ROOT = File.join(__dir__, '..', '..', '..')
22
18
 
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/components/interface'
@@ -16,10 +16,8 @@ module Contrast
16
16
  def application_environment
17
17
  return unless info?
18
18
 
19
- info('Process environment information',
20
- p_id: Process.pid,
21
- pp_id: Process.ppid,
22
- agent_version: Contrast::Agent::VERSION)
19
+ info('Process environment information', p_id: Process.pid, pp_id: Process.ppid,
20
+ agent_version: Contrast::Agent::VERSION)
23
21
  ENV.each do |env_key, env_value|
24
22
  env_key = env_key.to_s
25
23
  next unless ENV_KEYS.include?(env_key) ||
@@ -35,7 +33,9 @@ module Contrast
35
33
 
36
34
  loggable = CONFIG.loggable
37
35
  info('Current configuration', configuration: loggable)
38
- env_keys = ENV.keys.select { |env_key| env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER) }
36
+ env_keys = ENV.keys.select do |env_key|
37
+ env_key&.to_s&.start_with?(Contrast::Components::Config::CONTRAST_ENV_MARKER)
38
+ end
39
39
  env_items = env_keys.map { |env_key| Contrast::Utils::EnvConfigurationItem.new(env_key, nil) }
40
40
  env_translations = env_items.each_with_object({}) do |conversion, hash|
41
41
  hash[conversion.key] = conversion.dot_path_array.join('.')
@@ -52,7 +52,10 @@ module Contrast
52
52
  end
53
53
 
54
54
  FRAMEWORKS = %w[rails sinatra grape].cs__freeze
55
- WEB_SERVERS = %w[agoo falcon hoof iodine mongrel mongrel2 passenger puma rack skinny thin trinidad unicorn webrick yarn].cs__freeze
55
+ WEB_SERVERS = %w[
56
+ agoo falcon hoof iodine mongrel mongrel2 passenger puma rack skinny thin trinidad unicorn
57
+ webrick yarn
58
+ ].cs__freeze
56
59
  LIBRARIES = %w[excon json mongo moped mysql nokogiri oga ox pg psych sqlite3 typhoeus yaml].cs__freeze
57
60
  def log_specific_libraries
58
61
  FRAMEWORKS.each(&cs__method(:log_gem_data))
@@ -67,6 +70,7 @@ module Contrast
67
70
 
68
71
  Gem.loaded_specs.each_pair do |_name, gem_spec|
69
72
  debug('Gem loaded',
73
+ # rubocop:disable Security/Module/Name -- gems builtin.
70
74
  gem_name: gem_spec.name,
71
75
  gem_version: gem_spec.version.to_s)
72
76
  end
@@ -76,9 +80,8 @@ module Contrast
76
80
  gem_spec = Gem.loaded_specs[gem_name]
77
81
  return unless gem_spec
78
82
 
79
- info('Gem loaded',
80
- gem_name: gem_spec.name,
81
- gem_version: gem_spec.version.to_s)
83
+ info('Gem loaded', gem_name: gem_spec.name, gem_version: gem_spec.version.to_s)
84
+ # rubocop:enable Security/Module/Name
82
85
  end
83
86
  end
84
87
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2020 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
1
+ # Copyright (c) 2021 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'ougai'
@@ -43,9 +43,7 @@ module Contrast
43
43
  def thread_hash
44
44
  hash = LOG_TRACKER.get(:logging_hash)
45
45
  unless hash
46
- hash = {
47
- thread_id: Thread.current.object_id
48
- }
46
+ hash = { thread_id: Thread.current.object_id }
49
47
  LOG_TRACKER.set(:logging_hash, hash)
50
48
  end
51
49
  hash
@@ -53,8 +51,7 @@ module Contrast
53
51
 
54
52
  NO_REQUEST_HASH = { request_id: -1 }.cs__freeze
55
53
  def request_hash
56
- @request_tracker_defined ||= defined?(Contrast::Agent) &&
57
- defined?(Contrast::Agent::REQUEST_TRACKER)
54
+ @request_tracker_defined ||= defined?(Contrast::Agent) && defined?(Contrast::Agent::REQUEST_TRACKER)
58
55
  return NO_REQUEST_HASH unless @request_tracker_defined
59
56
 
60
57
  Contrast::Agent::REQUEST_TRACKER&.current&.logging_hash || NO_REQUEST_HASH