contrast-agent 6.1.0 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/.simplecov +1 -1
  3. data/Rakefile +1 -1
  4. data/ext/build_funchook.rb +3 -3
  5. data/ext/cs__assess_basic_object/cs__assess_basic_object.c +5 -1
  6. data/ext/extconf_common.rb +1 -1
  7. data/lib/contrast/agent/assess/finalizers/hash.rb +2 -2
  8. data/lib/contrast/agent/assess/policy/policy.rb +9 -10
  9. data/lib/contrast/agent/assess/policy/policy_node.rb +9 -10
  10. data/lib/contrast/agent/assess/policy/propagation_method.rb +3 -3
  11. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -3
  12. data/lib/contrast/agent/assess/policy/propagator/base.rb +1 -1
  13. data/lib/contrast/agent/assess/policy/propagator/buffer.rb +2 -1
  14. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +1 -1
  15. data/lib/contrast/agent/assess/policy/propagator/splat.rb +1 -1
  16. data/lib/contrast/agent/assess/policy/propagator/split.rb +2 -2
  17. data/lib/contrast/agent/assess/policy/propagator/trim.rb +1 -1
  18. data/lib/contrast/agent/assess/policy/source_node.rb +1 -1
  19. data/lib/contrast/agent/assess/policy/trigger_method.rb +7 -7
  20. data/lib/contrast/agent/assess/policy/trigger_node.rb +16 -16
  21. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +1 -1
  22. data/lib/contrast/agent/assess/property/evented.rb +2 -2
  23. data/lib/contrast/agent/assess/property/tagged.rb +2 -2
  24. data/lib/contrast/agent/assess/rule/provider/hardcoded_key.rb +6 -8
  25. data/lib/contrast/agent/assess/rule/provider/hardcoded_password.rb +6 -7
  26. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +5 -5
  27. data/lib/contrast/agent/assess/rule/response/base_rule.rb +2 -3
  28. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +8 -9
  29. data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +4 -4
  30. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +6 -6
  31. data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +4 -4
  32. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +4 -4
  33. data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +4 -4
  34. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +3 -4
  35. data/lib/contrast/agent/assess/tag.rb +13 -14
  36. data/lib/contrast/agent/at_exit_hook.rb +13 -1
  37. data/lib/contrast/agent/inventory/database_config.rb +12 -4
  38. data/lib/contrast/agent/inventory/dependency_usage_analysis.rb +9 -5
  39. data/lib/contrast/agent/middleware.rb +6 -3
  40. data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -3
  41. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +2 -2
  42. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +4 -4
  43. data/lib/contrast/agent/patching/policy/patch.rb +9 -9
  44. data/lib/contrast/agent/patching/policy/patch_status.rb +10 -3
  45. data/lib/contrast/agent/patching/policy/policy.rb +13 -15
  46. data/lib/contrast/agent/patching/policy/policy_node.rb +19 -21
  47. data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
  48. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +125 -125
  49. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
  50. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -1
  51. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  52. data/lib/contrast/agent/protect/policy/rule_applicator.rb +4 -4
  53. data/lib/contrast/agent/protect/rule/base.rb +30 -18
  54. data/lib/contrast/agent/protect/rule/base_service.rb +31 -14
  55. data/lib/contrast/agent/protect/rule/cmd_injection.rb +16 -9
  56. data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +3 -3
  57. data/lib/contrast/agent/protect/rule/default_scanner.rb +2 -1
  58. data/lib/contrast/agent/protect/rule/deserialization.rb +18 -7
  59. data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +74 -74
  60. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +71 -53
  61. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +3 -3
  62. data/lib/contrast/agent/protect/rule/no_sqli.rb +15 -16
  63. data/lib/contrast/agent/protect/rule/path_traversal.rb +13 -3
  64. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +2 -2
  65. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +1 -1
  66. data/lib/contrast/agent/protect/rule/sqli.rb +16 -23
  67. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +61 -61
  68. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +29 -29
  69. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +32 -32
  70. data/lib/contrast/agent/protect/rule/xss.rb +21 -0
  71. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +14 -13
  72. data/lib/contrast/agent/protect/rule/xxe.rb +25 -3
  73. data/lib/contrast/agent/reaction_processor.rb +1 -1
  74. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +36 -36
  75. data/lib/contrast/agent/reporting/masker/masker.rb +10 -10
  76. data/lib/contrast/agent/reporting/masker/masker_utils.rb +2 -2
  77. data/lib/contrast/agent/reporting/reporter.rb +2 -11
  78. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +8 -10
  79. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +53 -5
  80. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +25 -19
  81. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +129 -17
  82. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +20 -21
  83. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_stack.rb +22 -0
  84. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +26 -12
  85. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +7 -22
  86. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +7 -5
  87. data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +4 -10
  88. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +75 -15
  89. data/lib/contrast/agent/reporting/reporting_events/finding.rb +2 -2
  90. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +5 -19
  91. data/lib/contrast/agent/reporting/reporting_events/observed_library_usage.rb +6 -22
  92. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +1 -1
  93. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +2 -3
  94. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +1 -3
  95. data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +9 -0
  96. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -2
  97. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -10
  98. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -2
  99. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +6 -7
  100. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +61 -3
  101. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +33 -11
  102. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +8 -8
  103. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +64 -32
  104. data/lib/contrast/agent/reporting/settings/application_settings.rb +8 -23
  105. data/lib/contrast/agent/reporting/settings/assess.rb +5 -5
  106. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +28 -34
  107. data/lib/contrast/agent/reporting/settings/bot_blocker.rb +68 -0
  108. data/lib/contrast/agent/reporting/settings/code_exclusion.rb +27 -0
  109. data/lib/contrast/agent/reporting/settings/exclusion_base.rb +33 -0
  110. data/lib/contrast/agent/reporting/settings/exclusions.rb +39 -57
  111. data/lib/contrast/agent/reporting/settings/helpers.rb +56 -0
  112. data/lib/contrast/agent/reporting/settings/input_exclusion.rb +37 -0
  113. data/lib/contrast/agent/reporting/settings/ip_filter.rb +35 -0
  114. data/lib/contrast/agent/reporting/settings/keyword.rb +74 -0
  115. data/lib/contrast/agent/reporting/settings/log_enhancer.rb +65 -0
  116. data/lib/contrast/agent/reporting/settings/protect.rb +22 -5
  117. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +62 -115
  118. data/lib/contrast/agent/reporting/settings/reaction.rb +13 -4
  119. data/lib/contrast/agent/reporting/settings/rule_definition.rb +63 -0
  120. data/lib/contrast/agent/reporting/settings/sampling.rb +10 -0
  121. data/lib/contrast/agent/reporting/settings/sanitizer.rb +38 -0
  122. data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +11 -3
  123. data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +9 -2
  124. data/lib/contrast/agent/reporting/settings/server_features.rb +10 -2
  125. data/lib/contrast/agent/reporting/settings/syslog.rb +176 -0
  126. data/lib/contrast/agent/reporting/settings/url_exclusion.rb +42 -0
  127. data/lib/contrast/agent/reporting/settings/validator.rb +17 -0
  128. data/lib/contrast/agent/request.rb +2 -2
  129. data/lib/contrast/agent/request_context.rb +27 -19
  130. data/lib/contrast/agent/request_context_extend.rb +10 -23
  131. data/lib/contrast/agent/request_handler.rb +9 -5
  132. data/lib/contrast/agent/rule_set.rb +2 -2
  133. data/lib/contrast/agent/scope.rb +1 -1
  134. data/lib/contrast/agent/static_analysis.rb +4 -8
  135. data/lib/contrast/agent/telemetry/base.rb +9 -5
  136. data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +119 -0
  137. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -2
  138. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +1 -1
  139. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +1 -1
  140. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +16 -18
  141. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +2 -2
  142. data/lib/contrast/agent/thread_watcher.rb +4 -5
  143. data/lib/contrast/agent/version.rb +1 -1
  144. data/lib/contrast/agent.rb +1 -3
  145. data/lib/contrast/api/communication/messaging_queue.rb +1 -1
  146. data/lib/contrast/api/communication/service_lifecycle.rb +1 -1
  147. data/lib/contrast/api/communication/socket.rb +1 -1
  148. data/lib/contrast/api/communication/socket_client.rb +1 -1
  149. data/lib/contrast/api/communication/speedracer.rb +2 -2
  150. data/lib/contrast/api/decorators/agent_startup.rb +10 -9
  151. data/lib/contrast/api/decorators/application_settings.rb +1 -1
  152. data/lib/contrast/api/decorators/application_startup.rb +4 -4
  153. data/lib/contrast/api/decorators/application_update.rb +0 -8
  154. data/lib/contrast/api/decorators/response_type.rb +4 -17
  155. data/lib/contrast/api/decorators.rb +0 -1
  156. data/lib/contrast/components/agent.rb +1 -1
  157. data/lib/contrast/components/base.rb +1 -1
  158. data/lib/contrast/components/config.rb +6 -6
  159. data/lib/contrast/components/contrast_service.rb +4 -1
  160. data/lib/contrast/components/sampling.rb +1 -1
  161. data/lib/contrast/components/settings.rb +52 -28
  162. data/lib/contrast/config/assess_rules_configuration.rb +1 -1
  163. data/lib/contrast/config/protect_rules_configuration.rb +1 -1
  164. data/lib/contrast/config/root_configuration.rb +1 -1
  165. data/lib/contrast/configuration.rb +4 -4
  166. data/lib/contrast/extension/assess/array.rb +1 -1
  167. data/lib/contrast/extension/assess/erb.rb +1 -1
  168. data/lib/contrast/extension/assess/marshal.rb +1 -1
  169. data/lib/contrast/extension/assess/string.rb +1 -1
  170. data/lib/contrast/extension/extension.rb +2 -2
  171. data/lib/contrast/framework/base_support.rb +13 -12
  172. data/lib/contrast/framework/grape/support.rb +9 -9
  173. data/lib/contrast/framework/manager.rb +7 -9
  174. data/lib/contrast/framework/manager_extend.rb +2 -1
  175. data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
  176. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +15 -3
  177. data/lib/contrast/framework/rails/patch/assess_configuration.rb +3 -3
  178. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  179. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  180. data/lib/contrast/framework/rails/support.rb +11 -4
  181. data/lib/contrast/framework/sinatra/support.rb +4 -3
  182. data/lib/contrast/logger/aliased_logging.rb +27 -15
  183. data/lib/contrast/logger/cef_log.rb +14 -14
  184. data/lib/contrast/logger/format.rb +1 -1
  185. data/lib/contrast/logger/log.rb +8 -8
  186. data/lib/contrast/tasks/config.rb +12 -12
  187. data/lib/contrast/tasks/service.rb +2 -2
  188. data/lib/contrast/utils/assess/tracking_util.rb +4 -4
  189. data/lib/contrast/utils/class_util.rb +4 -4
  190. data/lib/contrast/utils/findings.rb +3 -3
  191. data/lib/contrast/utils/hash_digest.rb +6 -7
  192. data/lib/contrast/utils/head_dump_utils_extend.rb +1 -1
  193. data/lib/contrast/utils/invalid_configuration_util.rb +1 -1
  194. data/lib/contrast/utils/log_utils.rb +4 -4
  195. data/lib/contrast/utils/lru_cache.rb +1 -1
  196. data/lib/contrast/utils/metrics_hash.rb +1 -1
  197. data/lib/contrast/utils/middleware_utils.rb +5 -5
  198. data/lib/contrast/utils/net_http_base.rb +4 -4
  199. data/lib/contrast/utils/os.rb +1 -1
  200. data/lib/contrast/utils/patching/policy/patch_utils.rb +2 -2
  201. data/lib/contrast/utils/request_utils.rb +2 -2
  202. data/lib/contrast/utils/response_utils.rb +14 -1
  203. data/lib/contrast/utils/sha256_builder.rb +4 -4
  204. data/lib/contrast/utils/stack_trace_utils.rb +31 -13
  205. data/lib/contrast/utils/telemetry.rb +15 -9
  206. data/lib/contrast/utils/telemetry_client.rb +5 -5
  207. data/lib/contrast/utils/telemetry_hash.rb +37 -13
  208. data/lib/contrast/utils/telemetry_identifier.rb +10 -2
  209. data/lib/contrast/utils/thread_tracker.rb +26 -9
  210. data/lib/contrast/utils/timer.rb +7 -2
  211. data/lib/contrast.rb +1 -3
  212. data/resources/assess/policy.json +1 -1
  213. metadata +28 -14
  214. data/lib/contrast/api/decorators/library_usage_update.rb +0 -31
@@ -17,15 +17,84 @@ module Contrast
17
17
  # recorded.
18
18
  class DiscoveredRoute < Contrast::Agent::Reporting::ObservedRoute
19
19
  class << self
20
- # @param dtm [Contrast::Api::Dtm::RouteCoverage]
20
+ # @param obj [Regexp, Object]
21
+ # @return [String]
22
+ def source_or_string obj
23
+ if obj.cs__is_a?(Regexp)
24
+ obj.source
25
+ elsif obj.cs__respond_to?(:safe_string)
26
+ obj.safe_string
27
+ else
28
+ obj.to_s
29
+ end
30
+ end
31
+
32
+ # Convert ActionDispatch::Journey::Route to Contrast::Agent::Reporting::DiscoveredRoute
33
+ #
34
+ # @param journey_obj [ActionDispatch::Journey::Route] a rails route
35
+ # @param url [String, nil] use url from string instead of journey object.
36
+ # @return [Contrast::Agent::Reporting::DiscoveredRoute]
37
+ def from_action_dispatch_journey journey_obj, url = nil
38
+ msg = new
39
+ msg.signature = "#{ journey_obj.defaults[:controller] }##{ journey_obj.defaults[:action] }"
40
+
41
+ verb = source_or_string(journey_obj.verb)
42
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(verb)
43
+
44
+ url ||= source_or_string(journey_obj.path.spec)
45
+ msg.url = Contrast::Utils::StringUtils.force_utf8(url)
46
+ msg
47
+ end
48
+
49
+ # Convert Grape route data to discovered route.
50
+ #
51
+ # @param controller [::Grape::API] the route's final controller.
52
+ # @param method [String] GET, PUT, POST, etc...
53
+ # @param url [String, nil] use url from string instead matched pattern.
54
+ # @param pattern [String, Grape::Router::Route] the pattern that was matched in routing.
21
55
  # @return [Contrast::Agent::Reporting::DiscoveredRoute]
22
- def convert dtm
23
- discovered_route = new
24
- discovered_route.attach_data dtm
25
- discovered_route
56
+ def from_grape_controller controller, method, pattern, url = nil
57
+ if pattern.cs__is_a?(Grape::Router::Route)
58
+ safe_pattern = pattern.pattern&.path&.to_s
59
+ safe_url = source_or_string(url || safe_pattern)
60
+ else
61
+ safe_pattern = source_or_string(pattern)
62
+ safe_url = source_or_string(url || pattern)
63
+ end
64
+
65
+ msg = new
66
+ msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
67
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
68
+ msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
69
+ msg
70
+ end
71
+
72
+ # Convert Sinatra route data to discovered route.
73
+ #
74
+ # @param controller [::Sinatra::Base] the route's final controller.
75
+ # @param method [String] GET, PUT, POST, etc...
76
+ # @param pattern [::Mustermann::Sinatra] the pattern that was matched in routing.
77
+ # @param url [String, nil] use url from string instead matched pattern.
78
+ # @return [Contrast::Agent::Reporting::DiscoveredRoute]
79
+ def from_sinatra_route controller, method, pattern, url = nil
80
+ safe_pattern = source_or_string(pattern)
81
+ safe_url = source_or_string(url || pattern)
82
+
83
+ msg = new
84
+ msg.signature = "#{ controller }##{ method } #{ safe_pattern }"
85
+ msg.verb = Contrast::Utils::StringUtils.force_utf8(method)
86
+ msg.url = Contrast::Utils::StringUtils.force_utf8(safe_url)
87
+ msg
26
88
  end
27
89
  end
28
90
 
91
+ # @return [String] the controller, method, and pattern of this route
92
+ attr_accessor :signature
93
+ # @return [String] the url (or url pattern) used to access this route
94
+ attr_accessor :url
95
+ # @return [String, nil] the HTTP verb used to access this route or nil for any verb
96
+ attr_accessor :verb
97
+
29
98
  def initialize
30
99
  super
31
100
  @event_type = :discovered_route
@@ -34,18 +103,9 @@ module Contrast
34
103
  @url = Contrast::Utils::ObjectShare::EMPTY_STRING
35
104
  end
36
105
 
37
- # @param dtm[Contrast::Api::Dtm::RouteCoverage]
38
- def attach_data dtm
39
- @signature = dtm.route
40
- @verb = dtm.verb
41
- @url = dtm.url
42
- end
43
-
44
106
  def to_controlled_hash
45
107
  validate
46
- dr_hash = { session_id: @agent_session_id_value, signature: @signature, verb: @verb, url: @url }
47
- dr_hash.delete(:verb) unless @verb
48
- dr_hash
108
+ { session_id: ::Contrast::ASSESS.session_id, signature: @signature, verb: @verb, url: @url }.compact
49
109
  end
50
110
 
51
111
  def validate
@@ -135,7 +135,7 @@ module Contrast
135
135
  created: created,
136
136
  hash: hash_code.to_s,
137
137
  ruleId: rule_id,
138
- session_id: @agent_session_id_value.to_s,
138
+ session_id: ::Contrast::ASSESS.session_id,
139
139
  version: 4
140
140
  }
141
141
  hsh[:events] = events.map(&:to_controlled_hash) if event_based?
@@ -152,7 +152,7 @@ module Contrast
152
152
  # @raise [ArgumentError]
153
153
  def validate
154
154
  raise(ArgumentError, "#{ self } did not have a proper rule. Unable to continue.") unless @rule_id
155
- unless @agent_session_id_value
155
+ unless ::Contrast::ASSESS.session_id
156
156
  raise(ArgumentError, "#{ self } did not have a proper session id. Unable to continue.")
157
157
  end
158
158
  if event_based? && events.empty?
@@ -11,25 +11,11 @@ module Contrast
11
11
  # @param [Array<String>] List of file paths that have been loaded out of or executed by the library
12
12
  attr_reader :names
13
13
 
14
- class << self
15
- # Convert a DTM for SpeedRacer to an Event for TeamServer.
16
- #
17
- # @param usage_dtm [Contrast::Api::Dtm::LibraryUsageUpdate]
18
- # @return [Contrast::Agent::Reporting::LibraryUsageObservation]
19
- def convert usage_dtm
20
- observation = new
21
- observation.attach_data(usage_dtm)
22
- observation
23
- end
24
- end
25
-
26
- def initialize
27
- @names = []
28
- end
29
-
30
- def attach_data usage_dtm
31
- @id = usage_dtm.hash_code
32
- @names = usage_dtm.class_names.keys
14
+ # @param id [String] Sha256Sum of library as identified by the agent
15
+ # @param class_names [Array<String>] List of file paths that have been loaded out of or executed by the library
16
+ def initialize id, class_names
17
+ @id = id
18
+ @names = class_names
33
19
  end
34
20
 
35
21
  def to_controlled_hash
@@ -9,23 +9,11 @@ module Contrast
9
9
  module Reporting
10
10
  # List of libraries that have been observed to have something loaded or executed.
11
11
  #
12
- # @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
13
- # - Hash of LibraryUsageObservations
14
12
  class ObservedLibraryUsage < Contrast::Agent::Reporting::ApplicationReportingEvent
13
+ # @attr_reader observations - Array[Contrast::Agent::Reporting::LibraryUsageObservation]
14
+ # - Hash of LibraryUsageObservations
15
15
  attr_reader :observations
16
16
 
17
- class << self
18
- # Convert a Hash of LibraryUsageUpdate DTMs for SpeedRacer to an Event for TeamServer.
19
- #
20
- # @param usages_dtm_hash Hash[Contrast::Api::Dtm::LibraryUsageUpdate]
21
- # @return [Contrast::Agent::Reporting::ObservedLibraryUsage]
22
- def convert usages_dtm_hash
23
- report = new
24
- report.attach_data(usages_dtm_hash)
25
- report
26
- end
27
- end
28
-
29
17
  def initialize
30
18
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.library_usage
31
19
  @observations = []
@@ -41,17 +29,13 @@ module Contrast
41
29
  { observations: @observations.map(&:to_controlled_hash) }
42
30
  end
43
31
 
44
- def attach_data usages_dtm_hash
45
- usages_dtm_hash.each do |_key, value|
46
- next unless value.class_names.any?
47
-
48
- @observations << Contrast::Agent::Reporting::LibraryUsageObservation.convert(value)
49
- end
50
- end
51
-
52
32
  def validate
53
33
  raise(ArgumentError, "#{ self } did not have observations. Unable to continue.") if observations.empty?
54
34
  end
35
+
36
+ def clear
37
+ @observations = []
38
+ end
55
39
  end
56
40
  end
57
41
  end
@@ -47,7 +47,7 @@ module Contrast
47
47
  def to_controlled_hash
48
48
  validate
49
49
  rc_hash = {
50
- session_id: @agent_session_id_value,
50
+ session_id: ::Contrast::ASSESS.session_id,
51
51
  sources: @sources.map(&:to_controlled_hash),
52
52
  signature: @signature,
53
53
  verb: @verb,
@@ -30,7 +30,6 @@ module Contrast
30
30
  @app_name = ::Contrast::APP_CONTEXT.app_name
31
31
  @app_version = ::Contrast::APP_CONTEXT.app_version
32
32
  @routes = []
33
- @agent_session_id_value = ::Contrast::ASSESS.session_id
34
33
  end
35
34
 
36
35
  # Convert the instance variables on the class, and other information, into the identifiers required for
@@ -48,7 +47,7 @@ module Contrast
48
47
  data: '',
49
48
  key: 0,
50
49
  routes: @routes,
51
- session_id: @agent_session_id_value
50
+ session_id: ::Contrast::ASSESS.session_id
52
51
  }
53
52
  end
54
53
 
@@ -60,7 +59,7 @@ module Contrast
60
59
  unless @app_language
61
60
  raise(ArgumentError, "#{ cs__class } did not have a proper application language. Unable to continue.")
62
61
  end
63
- unless @agent_session_id_value
62
+ unless ::Contrast::ASSESS.session_id
64
63
  raise(ArgumentError, "#{ cs__class } did not have a proper session id. Unable to continue.")
65
64
  end
66
65
 
@@ -20,9 +20,7 @@ module Contrast
20
20
  attr_reader :event_method
21
21
 
22
22
  def initialize
23
- @agent_session_id_value = ::Contrast::ASSESS.session_id
24
- @event_endpoint ||= nil
25
- @event_method ||= :POST
23
+ @event_method ||= :POST # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
26
24
  end
27
25
 
28
26
  # Some reports require specific additional headers to be used. To that end, we'll attach them here, letting
@@ -32,6 +32,15 @@ module Contrast
32
32
  @count = 0
33
33
  end
34
34
 
35
+ # Parse the given controller and route from a Rack based application framework in order to create an instance
36
+ # of this class
37
+ #
38
+ # @param final_controller [Grape::API, Sinatra::Base] the controller responsible for the definition of the
39
+ # entrypoint of the route actively being executed
40
+ # @param method [String] the HTTP request method of the route actively being executed
41
+ # @param route_pattern [Grape::Router::Route, Mustermann::Sinatra] the pattern to which the url maps
42
+ # @param url [String] the literal url of the route actively being executed
43
+ # @return [Contrast::Agent::Reporting::RouteCoverage]
35
44
  def attach_rack_based_data final_controller, method, route_pattern, url = nil
36
45
  if route_pattern.cs__is_a?(Grape::Router::Route)
37
46
  safe_pattern = route_pattern.pattern&.path&.to_s
@@ -16,7 +16,7 @@ module Contrast
16
16
  attr_reader :path_for_requests, :path_for_responses
17
17
 
18
18
  def initialize
19
- generate_paths if enabled? && Contrast::CONTRAST_SERVICE.use_agent_communication?
19
+ generate_paths if enabled?
20
20
  end
21
21
 
22
22
  # This method will be handling the auditing of the requests and responses we send to SpeedRacer. If the audit
@@ -45,7 +45,6 @@ module Contrast
45
45
  # @param data[String] String representation if the logged data
46
46
  def log_data type, file_name, data = nil
47
47
  return unless enabled?
48
- return unless Contrast::CONTRAST_SERVICE.use_agent_communication?
49
48
 
50
49
  logger.debug('logging to file', file_name: file_name) # TODO: RUBY-99999 DO NOT COMMIT THIS
51
50
  write_to_file(type, file_name, data)
@@ -21,15 +21,6 @@ module Contrast
21
21
  dtm.cs__is_a?(Contrast::Api::Dtm::ServerActivity)
22
22
  end
23
23
 
24
- # Checks if the message is a Hash of Contrast::Api::Dtm::LibraryUsageUpdate class
25
- #
26
- # @param message [Protobuf::Field::FieldHash<String,::Contrast::Api::Dtm::LibraryUsageUpdate>]
27
- # @return [Boolean]
28
- def library_usage? message
29
- message.cs__is_a?(Protobuf::Field::FieldHash) &&
30
- message.values[0].cs__is_a?(Contrast::Api::Dtm::LibraryUsageUpdate)
31
- end
32
-
33
24
  # Checks if the message is of Contrast::Api::Dtm::ApplicationUpdate class
34
25
  #
35
26
  # @param dtm [Contrast::Api::Dtm::ApplicationUpdate,Object]
@@ -61,7 +52,6 @@ module Contrast
61
52
  return Contrast::Agent::Reporting::ServerActivity.new if server_activity?(dtm)
62
53
 
63
54
  # For the others, we convert them.
64
- return Contrast::Agent::Reporting::ObservedLibraryUsage.convert(dtm) if library_usage?(dtm)
65
55
  return Contrast::Agent::Reporting::ApplicationUpdate.convert(dtm) if application_update?(dtm)
66
56
  return Contrast::Agent::Reporting::Finding.convert(dtm) if finding?(dtm)
67
57
  return Contrast::Agent::Reporting::ApplicationActivity.convert(dtm) if activity?(dtm)
@@ -13,11 +13,10 @@ module Contrast
13
13
  attr_reader :app_name, :api_key, :agent_version, :app_language, :app_path, :app_version, :authorization,
14
14
  :server_name, :server_path, :server_type, :content_type, :encoding
15
15
 
16
+ include Contrast::Utils::ObjectShare
16
17
  ENCODING = 'base64'
17
18
  CONTENT_TYPE = 'application/json'
18
19
 
19
- include Contrast::Utils::ObjectShare
20
-
21
20
  def initialize
22
21
  @app_name = Base64.strict_encode64(Contrast::APP_CONTEXT.app_name)
23
22
  @api_key = Contrast::API.api_key
@@ -21,8 +21,13 @@ module Contrast
21
21
 
22
22
  include Contrast::Agent::Reporting::Endpoints
23
23
  include Contrast::Agent::Reporting::ReporterClientUtils
24
- SERVICE_NAME = 'Reporter'
25
24
  include Contrast::Components::Logger::InstanceMethods
25
+ SERVICE_NAME = 'Reporter'
26
+ def initialize
27
+ @headers = Contrast::Agent::Reporting::Headers.new
28
+ super()
29
+ end
30
+
26
31
  # This method initializes the Net::HTTP client we'll need. it will validate
27
32
  # the connection and make the first request. If connection is valid and response
28
33
  # is available then the open connection is returned.
@@ -33,11 +38,6 @@ module Contrast
33
38
  super(SERVICE_NAME, Contrast::API.api_url, use_proxy: true, use_custom_cert: true)
34
39
  end
35
40
 
36
- def initialize
37
- @headers = Contrast::Agent::Reporting::Headers.new
38
- super()
39
- end
40
-
41
41
  # Start the client for first time and sent startup event
42
42
  #
43
43
  # @param connection [Net::HTTP] open connection
@@ -56,7 +56,6 @@ module Contrast
56
56
  # @param send_immediately [Boolean] flag for the logger
57
57
  # @return response [Net::HTTP::Response, nil] response from TS if no response
58
58
  def send_event event, connection, send_immediately: false
59
- return unless Contrast::Agent::Reporter.enabled?
60
59
  return unless connection
61
60
 
62
61
  log_send_event(event) if send_immediately
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'contrast/agent/reporting/settings/application_settings'
5
5
  require 'contrast/agent/reporting/settings/server_features'
6
+ require 'contrast/agent/reporting/settings/reaction'
6
7
 
7
8
  module Contrast
8
9
  module Agent
@@ -18,22 +19,79 @@ module Contrast
18
19
 
19
20
  # All of the feature server_features
20
21
  #
21
- # @return server_features [Contrast::Agent::Reporting::Settings::FeatureSettings, nil]
22
+ # @return [Contrast::Agent::Reporting::Settings::FeatureSettings, nil]
22
23
  attr_accessor :server_features
23
24
 
25
+ # Success boolean message value
26
+ #
27
+ # @return [Boolean]
28
+ attr_accessor :success
29
+
30
+ # Message with reasons for success or fail.
31
+ #
32
+ # @return [Array<String>] Messages received from TS.
33
+ attr_accessor :messages
34
+
24
35
  class << self
25
- def application_response
36
+ # All of the settings from TeamServer that apply at the application level.
37
+ #
38
+ # @return response [Contrast::Agent::Reporting::Response]
39
+ def build_application_response
26
40
  res = new
27
41
  res.application_settings = Contrast::Agent::Reporting::Settings::ApplicationSettings.new
28
42
  res
29
43
  end
30
44
 
31
- def server_response
45
+ # All of the settings from TeamServer that apply at the server level.
46
+ #
47
+ # @return response [Contrast::Agent::Reporting::Response]
48
+ def build_server_response
32
49
  res = new
33
50
  res.server_features = Contrast::Agent::Reporting::Settings::ServerFeatures.new
34
51
  res
35
52
  end
36
53
  end
54
+
55
+ # Reaction the agent should take based on a state in TS.
56
+ # This is moved one level up because the responses we
57
+ # receive for feature and settings from TS have different
58
+ # place to store these reactions:
59
+ #
60
+ # body.reactions vs body.settings.reactions
61
+ #
62
+ # @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
63
+ def reactions
64
+ @_reactions ||= []
65
+ end
66
+
67
+ # Set the reaction
68
+ #
69
+ # @param reaction_array [Array<Reaction>] {
70
+ # level [String] The level at which the agent should log this reaction.
71
+ # [ERROR, WARN, INFO, DEBUG, TRACE]
72
+ # message [String] A message to log when receiving this reaction.
73
+ # operation [String] What to do in response to this reaction.[NOOP, DISABLE] }
74
+ # @return [Array<Contrast::Agent::Reporting::Settings::Reaction>]
75
+ def reactions= reaction_array
76
+ return unless reaction_array.is_a?(Array)
77
+
78
+ reaction_array.each do |r|
79
+ reactions << Contrast::Agent::Reporting::Settings::Reaction.new(r[:level], r[:operation], r[:message])
80
+ end
81
+ end
82
+
83
+ # This method is used only for testing with golden files.
84
+ def to_controlled_hash
85
+ {
86
+ success: success,
87
+ messages: messages,
88
+ features: server_features.nil? ? nil : server_features.to_controlled_hash,
89
+ settings: application_settings.nil? ? nil : application_settings.to_controlled_hash,
90
+ logLevel: server_features&.log_level,
91
+ logFile: server_features&.log_file,
92
+ reactions: server_features.nil? ? nil : reactions.map(&:to_controlled_hash)
93
+ }.compact
94
+ end
37
95
  end
38
96
  end
39
97
  end
@@ -24,7 +24,6 @@ module Contrast
24
24
  protect = response_data[:settings][:defend]
25
25
  return unless protect
26
26
 
27
- # TODO: RUBY-1636 should this be `:rules` or `:protectionRules`
28
27
  res.application_settings.protect.protection_rules = protect[:protectionRules]
29
28
  res.application_settings.protect.virtual_patches = protect[:virtualPatches]
30
29
  end
@@ -40,10 +39,14 @@ module Contrast
40
39
  res.application_settings.exclusions.url_exclusions = exclusions[:urlExceptions]
41
40
  end
42
41
 
42
+ # The responses we receive for feature and settings from TS have different
43
+ # place to store these reactions: body.reactions vs body.settings.reactions.
44
+ #
43
45
  # @param response_data [Hash]
44
46
  # @param res [Contrast::Agent::Reporting::Response]
45
47
  def extract_reactions response_data, res
46
- res.application_settings.reactions = response_data[:settings][:reactions]
48
+ res.reactions = response_data[:settings][:reactions] if response_data[:settings]
49
+ res.reactions = response_data[:reactions] if response_data[:features]
47
50
  end
48
51
 
49
52
  # @param response_data [Hash]
@@ -65,10 +68,17 @@ module Contrast
65
68
  return unless protect
66
69
 
67
70
  res.server_features.protect.enabled = protect[:enabled]
68
- res.server_features.protect.bot_blocker = protect[:'bot-blocker']
69
- # TODO: RUBY-1636 should this be `:rules` or `:protectionRules`
70
- # process the botBlockers field
71
- res.server_features.protect.syslog = protect[:syslog]
71
+ res.server_features.protect.bot_blocker.enable = protect[:'bot-blocker']
72
+ res.server_features.protect.bot_blocker.bots = protect[:botBlockers]
73
+ extract_syslog(response_data, res)
74
+ end
75
+
76
+ # @param response_data [Hash]
77
+ # @param res [Contrast::Agent::Reporting::Response]
78
+ def extract_syslog response_data, res
79
+ return unless (syslog = response_data[:features][:defend][:syslog])
80
+
81
+ res.server_features.protect.syslog.assign_array(syslog)
72
82
  end
73
83
 
74
84
  # @param response_data [Hash]
@@ -78,21 +88,33 @@ module Contrast
78
88
  return unless protect
79
89
 
80
90
  res.server_features.protect.ip_allowlist = protect[:ipAllowlist]
81
- res.server_features.protect.ip_denylist = protect[:ipDenyList]
82
- res.server_features.protect.log_enchancers = protect[:logEnhancers]
91
+ res.server_features.protect.ip_denylist = protect[:ipDenylist]
92
+ res.server_features.protect.log_enhancers = protect[:logEnhancers]
83
93
  res.server_features.protect.rule_definition_list = protect[:ruleDefinitionList]
84
94
  end
85
95
 
86
96
  # Here we extract the rules and state for the sensitive data masking policy
87
- # Received from TS.
97
+ # received from TS.
88
98
  #
89
99
  # @param response_data [Hash]
90
100
  # @param res [Contrast::Agent::Reporting::Response]
91
101
  def extract_sensitive_data_policy response_data, res
92
- sensitive_data = response_data[:settings][:sensitive_data_masking_policy]
102
+ return unless (sensitive_data = response_data[:settings][:sensitive_data_masking_policy])
103
+
93
104
  res.application_settings.sensitive_data_masking.mask_http_body = sensitive_data[:mask_http_body]
94
105
  res.application_settings.sensitive_data_masking.mask_attack_vector = sensitive_data[:mask_attack_vector]
95
- res.application_settings.sensitive_data_masking.build_rules_form_settings sensitive_data[:rules]
106
+ res.application_settings.sensitive_data_masking.build_rules_form_settings(sensitive_data[:rules])
107
+ end
108
+
109
+ # Here we extract the log settings received from TS.
110
+ #
111
+ # @param response_data [Hash]
112
+ # @param res [Contrast::Agent::Reporting::Response]
113
+ def extract_log_settings response_data, res
114
+ return unless (log_level = response_data[:logLevel])
115
+
116
+ res.server_features.log_level = log_level
117
+ res.server_features.log_file = response_data[:logFile] if response_data[:logFile]
96
118
  end
97
119
  end
98
120
  end
@@ -78,19 +78,19 @@ module Contrast
78
78
  def handle_error response
79
79
  case response&.code
80
80
  when ERROR_CODES[:message_not_sent]
81
- handle_response_errors response, UNSUCCESSFULLY_RECEIVED_MSG, mode.running
81
+ handle_response_errors(response, UNSUCCESSFULLY_RECEIVED_MSG, mode.running)
82
82
  when ERROR_CODES[:access_forbidden]
83
- handle_response_errors response, FORBIDDEN_MSG, mode.running
83
+ handle_response_errors(response, FORBIDDEN_MSG, mode.running)
84
84
  when ERROR_CODES[:access_forbidden_no_action]
85
- handle_response_errors response, FORBIDDEN_NO_ACTION_MSG, mode.running
85
+ handle_response_errors(response, FORBIDDEN_NO_ACTION_MSG, mode.running)
86
86
  when ERROR_CODES[:application_do_not_exist]
87
- handle_response_errors response, APP_NON_EXISTENT_MSG, mode.disabled
87
+ handle_response_errors(response, APP_NON_EXISTENT_MSG, mode.disabled)
88
88
  when ERROR_CODES[:unprocessable_entity]
89
- handle_response_errors response, UNPROCESSABLE_ENTITY_MSG, mode.disabled
89
+ handle_response_errors(response, UNPROCESSABLE_ENTITY_MSG, mode.disabled)
90
90
  when ERROR_CODES[:too_many_requests]
91
- handle_response_errors response, RETRY_AFTER_MSG, mode.resending
91
+ handle_response_errors(response, RETRY_AFTER_MSG, mode.resending)
92
92
  else
93
- logger.debug('Response Error code could not be processed')
93
+ logger.error('Response Error code could not be processed')
94
94
  end
95
95
  end
96
96
 
@@ -102,7 +102,7 @@ module Contrast
102
102
  # @param error_message [String, nil] Error message if any received.
103
103
  def suspend_reporting message, timeout, error_message
104
104
  @_timeout = timeout || Contrast::Agent::Reporting::ResponseHandler::TIMEOUT
105
- log_debug_msg message, timeout: @_timeout, error_message: error_message || 'none'
105
+ log_debug_msg(message, timeout: @_timeout, error_message: error_message || 'none')
106
106
  @_sleep = true
107
107
  end
108
108