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,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'
@@ -95,10 +95,8 @@ module Contrast
95
95
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
96
96
 
97
97
  incoming_properties = Contrast::Agent::Assess::Tracker.properties(incoming)
98
- parent_event = incoming_properties&.event
99
- parent_events << parent_event if parent_event
98
+ parent_events << incoming_properties&.event if incoming_properties&.event
100
99
 
101
- pattern = preshift.args[0]
102
100
  source = preshift.object
103
101
 
104
102
  # We can't efficiently find the places that things were
@@ -111,6 +109,34 @@ module Contrast
111
109
 
112
110
  # if it's just a straight insert, that we can do
113
111
  # Copy the tags from us to the return
112
+ ranges = find_string_sub_insert(properties, preshift, incoming, ret, global)
113
+
114
+ properties.delete_tags_at_ranges(ranges)
115
+ properties.shift_tags(ranges)
116
+ return unless incoming_tracked
117
+ return unless incoming_properties
118
+
119
+ tags = incoming_properties.tag_keys
120
+ ranges.each do |range|
121
+ tags.each do |tag|
122
+ properties.add_tag(tag, range)
123
+ end
124
+ end
125
+ end
126
+
127
+ # Find the points at which the new String was placed into the original
128
+ #
129
+ # @param properties [Contrast::Agent::Assess::Properties] the Properties of the ret
130
+ # @param preshift [Contrast::Agent::Assess::PreShift] the capture of the state of the code just prior to
131
+ # the invocation of the patched method
132
+ # @param incoming [String] the new String going into the substitution
133
+ # @param ret [String] the result of the substitution
134
+ # @param global [Boolean] if this was a global or single substitution
135
+ # @return [Array<Range>] the Ranges where substitution occurred
136
+ def find_string_sub_insert properties, preshift, incoming, ret, global
137
+ pattern = preshift.args[0]
138
+ source = preshift.object
139
+
114
140
  properties.copy_from(source, ret)
115
141
  # Figure out where inserts occurred
116
142
  last_idx = 0
@@ -126,17 +152,7 @@ module Contrast
126
152
  ranges << (start_index...end_index)
127
153
  break unless global
128
154
  end
129
- properties.delete_tags_at_ranges(ranges)
130
- properties.shift_tags(ranges)
131
- return unless incoming_tracked
132
- return unless incoming_properties
133
-
134
- tags = incoming_properties.tag_keys
135
- ranges.each do |range|
136
- tags.each do |tag|
137
- properties.add_tag(tag, range)
138
- end
139
- end
155
+ ranges
140
156
  end
141
157
 
142
158
  def block_sub self_tracked, source, ret
@@ -170,13 +186,7 @@ module Contrast
170
186
 
171
187
  properties = Contrast::Agent::Assess::Tracker.properties(ret)
172
188
  args = preshift.args
173
- properties.build_event(
174
- patcher,
175
- ret,
176
- preshift.object,
177
- ret,
178
- args,
179
- 2)
189
+ properties.build_event(patcher, ret, preshift.object, ret, args, 2)
180
190
  properties.event.instance_variable_set(:@_parent_events, parent_events)
181
191
  end
182
192
  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
  module Contrast
@@ -8,50 +8,24 @@ module Contrast
8
8
  module Propagator
9
9
  # This class is specifically for String#tr(_s) propagation
10
10
  #
11
- # Disclaimer: there may be a better way, but we're
12
- # in a 'get it work' state. hopefully, we'll be in
13
- # a 'get it right' state soon.
11
+ # Disclaimer: there may be a better way, but we're in a 'get it work' state. hopefully, we'll be in a 'get it
12
+ # right' state soon.
14
13
  module Trim
15
14
  class << self
16
- def tr_tagger patcher, preshift, ret, _block
15
+ # @param policy_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
16
+ # propagation event.
17
+ # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
18
+ # the invocation of the patched method.
19
+ # @param ret [nil, String] the target to which to propagate.
20
+ # @return [nil, String] ret
21
+ def tr_tagger policy_node, preshift, ret, _block
17
22
  return ret unless ret && !ret.empty?
18
23
  return ret unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
19
24
 
20
- source = preshift.object
21
- args = preshift.args
22
- properties.copy_from(source, ret)
23
- replace_string = args[1]
24
- source_chars = source.chars
25
- # if the replace string is empty, then there's a bunch of deletes. this
26
- # functions the same as the Removal propagation.
27
- if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING
28
- Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(source_chars, ret)
29
- else
30
- remove_ranges = []
31
- ret_chars = ret.chars
32
- start = nil
33
- source_chars.each_with_index do |char, idx|
34
- if ret_chars[idx] == char
35
- next unless start
25
+ properties.copy_from(preshift.object, ret)
26
+ handle_tr(policy_node, preshift, ret, properties)
36
27
 
37
- remove_ranges << (start...idx)
38
- start = nil
39
- else
40
- start ||= idx
41
- end
42
- end
43
- # account for the last char being different
44
- remove_ranges << (start...source_chars.length) if start
45
- properties.delete_tags_at_ranges(remove_ranges, false)
46
- end
47
-
48
- properties.build_event(
49
- patcher,
50
- ret,
51
- source,
52
- ret,
53
- args,
54
- 1)
28
+ properties.build_event(policy_node, ret, preshift.object, ret, preshift.args, 1)
55
29
  ret
56
30
  end
57
31
 
@@ -62,14 +36,59 @@ module Contrast
62
36
  source = preshift.object
63
37
  args = preshift.args
64
38
  properties.splat_from(source, ret)
65
- properties.build_event(
66
- patcher,
67
- ret,
68
- source,
69
- ret,
70
- args)
39
+ properties.build_event(patcher, ret, source, ret, args)
71
40
  ret
72
41
  end
42
+
43
+ private
44
+
45
+ # @param policy_node [Contrast::Agent::Assess::Policy::PropagationNode] the node that governs this
46
+ # propagation event.
47
+ # @param preshift [Contrast::Agent::Assess::PreShift] The capture of the state of the code just prior to
48
+ # the invocation of the patched method.
49
+ # @param ret [String] the target to which to propagate.
50
+ # @param properties [Contrast::Agent::Assess::Properties] the properties of the ret
51
+ def handle_tr policy_node, preshift, ret, properties
52
+ source = preshift.object
53
+ replace_string = preshift.args[1]
54
+
55
+ # if the replace string is empty, then there's a bunch of deletes. this functions the same as the
56
+ # Removal propagation.
57
+ if replace_string == Contrast::Utils::ObjectShare::EMPTY_STRING
58
+ Contrast::Agent::Assess::Policy::Propagator::Remove.handle_removal(policy_node, source, ret)
59
+ return
60
+ end
61
+
62
+ # Otherwise, we need to target each insertion point. Based on the spec for #tr & #tr_s, the find is
63
+ # treated as a regexp range, excepting the `\` character, which we'll need to escape. This converts to
64
+ # that form, wrapping the input in `[]`.
65
+ find_string = preshift.args[0]
66
+ find_string += '\\' if find_string.end_with?('\\')
67
+ find_regexp = Regexp.new("[#{ find_string }]")
68
+
69
+ # Find the first instance to be replaced. If there isn't one, than nothing changed here.
70
+ idx = source.index(find_regexp)
71
+ return unless idx
72
+
73
+ # Iterate over each change and record where it happened. B/c this is a one to one replace, the index of
74
+ # the replacement is always one; however, there may be adjacent replacements which become a single
75
+ # range.
76
+ start = idx
77
+ stop = idx + 1
78
+ remove_ranges = []
79
+ while (idx = source.index(find_regexp, idx + 1))
80
+ # If the previous range ends at this index, we can expand that range to include this index.
81
+ # Otherwise, we need to record the held range and start a new one.
82
+ if stop != idx
83
+ remove_ranges << (start...stop)
84
+ start = idx
85
+ end
86
+ stop = idx + 1
87
+ end
88
+ # Be sure to capture the last range in the holder.
89
+ remove_ranges << (start...stop)
90
+ properties.delete_tags_at_ranges(remove_ranges, false)
91
+ end
73
92
  end
74
93
  end
75
94
  end
@@ -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/agent/patching/policy/patch_status'
5
7
  require 'contrast/agent/module_data'
6
8
  require 'contrast/agent/rewriter'
@@ -41,7 +43,9 @@ module Contrast
41
43
  module_name = mod.cs__name
42
44
  return unless module_name
43
45
 
44
- if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START, Contrast::Utils::ObjectShare::ANONYMOUS_CLASS_MARKER)
46
+ if module_name.start_with?(Contrast::Utils::ObjectShare::CONTRAST_MODULE_START,
47
+ Contrast::Utils::ObjectShare::ANONYMOUS_CLASS_MARKER)
48
+
45
49
  status.no_rewrite!
46
50
  return
47
51
 
@@ -64,8 +68,7 @@ module Contrast
64
68
  # To get around this, we have those methods tell us the class
65
69
  # isn't ready
66
70
  def mid_defining? mod
67
- mod.instance_variable_defined?(:@cs__defining_class) &&
68
- mod.instance_variable_get(:@cs__defining_class)
71
+ mod.instance_variable_defined?(:@cs__defining_class) && mod.instance_variable_get(:@cs__defining_class)
69
72
  end
70
73
 
71
74
  def agent_should_rewrite?
@@ -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 'set'
@@ -11,10 +11,9 @@ module Contrast
11
11
  module Agent
12
12
  module Assess
13
13
  module Policy
14
- # This class controls the actions we take on Sources, as determined by
15
- # our Assess policy. It indicates what actions we should take in order
16
- # to mark data as User Input and treat it as untrusted, starting the
17
- # dataflows used in Assess vulnerability detection.
14
+ # This class controls the actions we take on Sources, as determined by our Assess policy. It indicates what
15
+ # actions we should take in order to mark data as User Input and treat it as untrusted, starting the dataflows
16
+ # used in Assess vulnerability detection.
18
17
  module SourceMethod
19
18
  include Contrast::Components::Interface
20
19
  access_component :analysis, :logging
@@ -27,22 +26,17 @@ module Contrast
27
26
  COOKIE_KEY_TYPE = 'COOKIE_KEY'
28
27
 
29
28
  class << self
30
- # This is called from within our woven proc. It will be called as if it
31
- # were inline in the Rack application.
29
+ # This is called from within our woven proc. It will be called as if it were inline in the Rack
30
+ # application.
32
31
  #
33
- # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy]
34
- # the policy that applies to the method being called
32
+ # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that applies to the
33
+ # method being called
35
34
  # @param object [Object] the Object on which the method was invoked
36
35
  # @param ret [Object] the Return of the invoked method
37
- # @param args [Array<Object>] the Arguments with which the method
38
- # was invoked
39
- # @return [Object, nil] the tracked Return or nil if no changes
40
- # were made
36
+ # @param args [Array<Object>] the Arguments with which the method was invoked
37
+ # @return [Object, nil] the tracked Return or nil if no changes were made
41
38
  def source_patchers method_policy, object, ret, args
42
- return if method_policy.source_node.nil?
43
-
44
- current_context = Contrast::Agent::REQUEST_TRACKER.current
45
- return unless current_context&.analyze_request? && ASSESS.enabled?
39
+ return unless analyze?(method_policy, object, ret, args)
46
40
 
47
41
  source_node = method_policy.source_node
48
42
  target = determine_target(source_node, object, ret, args)
@@ -62,29 +56,26 @@ module Contrast
62
56
  # double check that we were able to finalize the replaced return
63
57
  return unless Contrast::Agent::Assess::Tracker.trackable?(target)
64
58
  end
65
- apply_source(current_context, source_node, target, object, ret, source_node.type, nil, *args)
59
+ apply_source(Contrast::Agent::REQUEST_TRACKER.current, source_node, target, object, ret,
60
+ source_node.type, nil, *args)
66
61
  restore_frozen_state ? ret : nil
67
62
  end
68
63
 
69
64
  private
70
65
 
71
- # This is our method that actually taints the object our
72
- # source_node targets.
66
+ # This is our method that actually taints the object our source_node targets.
73
67
  #
74
- # @param context [Contrast::Utils::ThreadTracker] the current request
75
- # context
76
- # @param source_node [Contrast::Agent::Assess::Policy::SourceNode]
77
- # the node to direct applying this source event
68
+ # @param context [Contrast::Utils::ThreadTracker] the current request context
69
+ # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
70
+ # event
78
71
  # @param target [Object] the target of the Source Event
79
72
  # @param object [Object] the Object on which the method was invoked
80
73
  # @param ret [Object] the Return of the invoked method
81
- # @param source_type [String] the type of this source, from the
82
- # source_node, or a KEY_TYPE if invoked for a map
83
- # @param source_name [String, nil] the name of this source, i.e.
84
- # the key used to accessed if from a map or nil if a type like
85
- # BODY
86
- # @param args [Array<Object>] the Arguments with which the method
87
- # was invoked
74
+ # @param source_type [String] the type of this source, from the source_node, or a KEY_TYPE if invoked for a
75
+ # map
76
+ # @param source_name [String, nil] the name of this source, i.e. the key used to accessed if from a map or
77
+ # nil if a type like BODY
78
+ # @param args [Array<Object>] the Arguments with which the method was invoked
88
79
  def apply_source context, source_node, target, object, ret, source_type, source_name = nil, *args
89
80
  return unless context && source_node && target
90
81
 
@@ -95,37 +86,34 @@ module Contrast
95
86
  apply_tags(source_node, target, object, ret, source_type, source_name, *args)
96
87
  elsif Contrast::Utils::DuckUtils.iterable_hash?(target)
97
88
  apply_hash_tags(context, source_node, target, object, ret, source_type, *args)
98
- # While we don't taint arrays themselves, we may taint the things
99
- # they hold. Let's pass their keys and values back to ourselves and
100
- # try again
89
+ # While we don't taint arrays themselves, we may taint the things they hold. Let's pass their keys and
90
+ # values back to ourselves and try again
101
91
  elsif Contrast::Utils::DuckUtils.iterable_enumerable?(target)
102
- target.each { |value| apply_source(context, source_node, value, object, ret, source_type, source_name, *args) }
92
+ target.each do |value|
93
+ apply_source(context, source_node, value, object, ret, source_type, source_name, *args)
94
+ end
103
95
  end
104
96
  rescue StandardError => e
105
97
  logger.warn('Unable to apply source', e, node_id: source_node.id)
106
98
  end
107
99
 
108
- # While we don't taint hashes themselves, we may taint the things
109
- # they hold. Let's pass their keys and values back to ourselves and
110
- # try again
100
+ # While we don't taint hashes themselves, we may taint the things they hold. Let's pass their keys and
101
+ # values back to ourselves and try again
111
102
  #
112
- # @param context [Contrast::Utils::ThreadTracker] the current request
113
- # context
114
- # @param source_node [Contrast::Agent::Assess::Policy::SourceNode]
115
- # the node to direct applying this source event
103
+ # @param context [Contrast::Utils::ThreadTracker] the current request context
104
+ # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
105
+ # event
116
106
  # @param target [Object] the target of the Source Event
117
107
  # @param object [Object] the Object on which the method was invoked
118
108
  # @param ret [Object] the Return of the invoked method
119
- # @param source_type [String] the type of this source, from the
120
- # source_node, or a KEY_TYPE if invoked for a map
121
- # @param args [Array<Object>] the Arguments with which the method
122
- # was invoked
109
+ # @param source_type [String] the type of this source, from the source_node, or a KEY_TYPE if invoked for a
110
+ # map
111
+ # @param args [Array<Object>] the Arguments with which the method was invoked
123
112
  def apply_hash_tags context, source_node, target, object, ret, source_type, *args
124
113
  to_replace = []
125
114
  target.each_pair do |key, value|
126
- # We only do this for Strings b/c of the way Hash lookup works.
127
- # To replace another object would break hash lookup and,
128
- # therefore, the application
115
+ # We only do this for Strings b/c of the way Hash lookup works. To replace another object would break
116
+ # hash lookup and, therefore, the application
129
117
  if replace_hash_key?(key, target)
130
118
  key = key.dup
131
119
  to_replace << key
@@ -136,10 +124,8 @@ module Contrast
136
124
  handle_hash_key(target, to_replace)
137
125
  end
138
126
 
139
- # Given an unfrozen hash, if the key is a String, we should replace
140
- # it with one that we can finalize, allowing us to track that key.
141
- # This method handles checking if that replace can and should
142
- # occur.
127
+ # Given an unfrozen hash, if the key is a String, we should replace it with one that we can finalize,
128
+ # allowing us to track that key. This method handles checking if that replace can and should occur.
143
129
  #
144
130
  # @param key [Object] the key in the hash that may need replacing.
145
131
  # @param hash [Hash] the hash to which the key belongs.
@@ -160,9 +146,8 @@ module Contrast
160
146
  nil
161
147
  end
162
148
 
163
- # Hash is designed to keep one instance of the string key in it.
164
- # We need to remove the existing one and replace it with our new
165
- # tracked one.
149
+ # Hash is designed to keep one instance of the string key in it. We need to remove the existing one and
150
+ # replace it with our new tracked one.
166
151
  def handle_hash_key target, to_replace
167
152
  to_replace.each do |key|
168
153
  Contrast::Agent::Assess::Tracker.pre_freeze(key)
@@ -175,25 +160,19 @@ module Contrast
175
160
  def apply_tags source_node, target, object, ret, source_type, source_name, *args
176
161
  # don't apply tags if we can't track the thing
177
162
  return unless Contrast::Agent::Assess::Tracker.trackable?(target)
178
- # don't apply second source -- probably needs tuning later if we
179
- # use more than 'UNTRUSTED' in our sources
163
+ # don't apply second source -- probably needs tuning later if we use more than 'UNTRUSTED' in our sources
180
164
  return if Contrast::Agent::Assess::Tracker.tracked?(target)
181
165
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
182
166
 
183
- # otherwise for each tag this source_node applies, create a tag range
184
- # on the target object
185
- # I realize this looping is counter-intuitive from the above
186
- # message, that's why we're revisiting.
167
+ # otherwise for each tag this source_node applies, create a tag range on the target object. I realize
168
+ # this looping is counter-intuitive from the above message, that's why we're revisiting.
187
169
  source_node.tags.each do |tag|
188
170
  next unless Contrast::Agent::Assess::Policy::SourceValidation.valid?(tag, source_type, source_name)
189
171
 
190
172
  length = Contrast::Utils::StringUtils.ret_length(target)
191
173
  properties.add_tag(tag, 0...length)
192
174
  properties.add_properties(source_node.properties)
193
- logger.trace('Source detected',
194
- node_id: source_node.id,
195
- target_id: target.__id__,
196
- tag: tag)
175
+ logger.trace('Source detected', node_id: source_node.id, target_id: target.__id__, tag: tag)
197
176
  end
198
177
  # make a representation of this method that TeamServer can render
199
178
  properties.build_event(source_node, target, object, ret, args, source_type, source_name)
@@ -201,15 +180,13 @@ module Contrast
201
180
 
202
181
  # Find the name of the source
203
182
  #
204
- # @param source_node [Contrast::Agent::Assess::Policy::SourceNode]
205
- # the node to direct applying this source event
183
+ # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
184
+ # event
206
185
  # @param object [Object] the Object on which the method was invoked
207
186
  # @param ret [Object] the Return of the invoked method
208
- # @param args [Array<Object>] the Arguments with which the method
209
- # was invoked
210
- # @return [String, nil] the human readable name of the target to
211
- # which this source event applies, or nil if none provided by the
212
- # node
187
+ # @param args [Array<Object>] the Arguments with which the method was invoked
188
+ # @return [String, nil] the human readable name of the target to which this source event applies, or nil if
189
+ # none provided by the node
213
190
  def determine_source_name source_node, object, ret, *args
214
191
  return source_node.get_property('dynamic_source_name') if source_node.type == 'UNTRUSTED_DATABASE'
215
192
 
@@ -226,14 +203,50 @@ module Contrast
226
203
  end
227
204
  end
228
205
 
206
+ # Determine if we should analyze this method invocation for a Source or not. We should if we have enough
207
+ # information to build the context of this invocation, we're not disabled, and we can't immediately
208
+ # determine the invocation was done safely.
209
+ #
210
+ # @param method_policy [Contrast::Agent::Patching::Policy::MethodPolicy] the policy that applies to the
211
+ # method being called
212
+ # @param object [Object] the Object on which the method was invoked
213
+ # @param ret [Object] the Return of the invoked method
214
+ # @param args [Array<Object>] the Arguments with which the method was invoked
215
+ # @return [boolean] if the invocation of this method should be analyzed
216
+ def analyze? method_policy, object, ret, args
217
+ return false unless method_policy&.source_node
218
+ return false unless ASSESS.enabled?
219
+ return false unless Contrast::Agent::REQUEST_TRACKER.current&.analyze_request?
220
+
221
+ !safe_invocation?(method_policy.source_node, object, ret, args)
222
+ end
223
+
224
+ # Determine if the method was invoked safely.
225
+ #
226
+ # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
227
+ # event
228
+ # @param _object [Object] the Object on which the method was invoked
229
+ # @param _ret [Object] the Return of the invoked method
230
+ # @param args [Array<Object>] the Arguments with which the method was invoked
231
+ # @return [boolean] if the invocation of this method was safe
232
+ def safe_invocation? source_node, _object, _ret, args
233
+ # According the the Rack Specification https://github.com/rack/rack/blob/master/SPEC.rdoc, any header
234
+ # from the Request will start with HTTP_. As such, only Headers with that key should be considered for
235
+ # tracking, as the others have come from the Framework or Middleware stashing in the ENV. Rails, for
236
+ # instance, uses action_dispatch. to store several values. Technically, you can't call
237
+ # Rack::Request#get_header without a parameter, and that parameter should be a String, but trust no one.
238
+ source_node.id == 'Assess:Source:Rack::Request::Env#get_header' &&
239
+ args&.any? &&
240
+ !args[0].to_s.start_with?('HTTP_')
241
+ end
242
+
229
243
  # Find the literal target of the propagation
230
244
  #
231
- # @param source_node [Contrast::Agent::Assess::Policy::SourceNode]
232
- # the node to direct applying this source event
245
+ # @param source_node [Contrast::Agent::Assess::Policy::SourceNode] the node to direct applying this source
246
+ # event
233
247
  # @param object [Object] the Object on which the method was invoked
234
248
  # @param ret [Object] the Return of the invoked method
235
- # @param args [Array<Object>] the Arguments with which the method
236
- # was invoked
249
+ # @param args [Array<Object>] the Arguments with which the method was invoked
237
250
  # @return [Object] the target to which this source event applies
238
251
  def determine_target source_node, object, ret, args
239
252
  source_target = source_node.targets[0]
@@ -247,12 +260,10 @@ module Contrast
247
260
  end
248
261
  end
249
262
 
250
- # Simple helper method to flip the type from value to key when the
251
- # source is the key of a Hash
263
+ # Simple helper method to flip the type from value to key when the source is the key of a Hash
252
264
  #
253
265
  # @param source_type [String] the original value source type
254
- # @return [String] the key form of the source type, if one exists,
255
- # else the original source type
266
+ # @return [String] the key form of the source type, if one exists, else the original source type
256
267
  def key_type source_type
257
268
  case source_type
258
269
  when PARAMETER_TYPE