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,13 +17,6 @@ module Contrast
17
17
  class CacheControl < HeaderRule
18
18
  include BodyRule
19
19
  include Framework::RailsSupport
20
-
21
- def rule_id
22
- 'cache-controls-missing'
23
- end
24
-
25
- protected
26
-
27
20
  HEADER_KEYS = %w[Cache-Control].cs__freeze
28
21
  ACCEPTED_VALUES = [/no-store/, /no-cache/].cs__freeze
29
22
  DEFAULT_SAFE = false
@@ -31,6 +24,12 @@ module Contrast
31
24
  HEAD_TAG = /<head>/i.cs__freeze
32
25
  NAME = 'cache-control'
33
26
 
27
+ def rule_id
28
+ 'cache-controls-missing'
29
+ end
30
+
31
+ protected
32
+
34
33
  # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
35
34
  #
36
35
  # @param response [Contrast::Agent::Response] the response of the application
@@ -62,7 +61,7 @@ module Contrast
62
61
  return false if meta_tags(response).empty?
63
62
 
64
63
  meta_tags(response).each do |tag|
65
- return true if meta_cache_tag? tag[HTML_PROP]
64
+ return true if meta_cache_tag?(tag[HTML_PROP])
66
65
  end
67
66
 
68
67
  false
@@ -92,7 +91,7 @@ module Contrast
92
91
  # @return [Hash, nil] the evidence hash or nil
93
92
  def tag_evidence response
94
93
  meta_tags(response).each do |tag|
95
- return evidence(META_TYPE, PRAGMA, tag[HTML_PROP]) if meta_cache_tag? tag[HTML_PROP]
94
+ return evidence(META_TYPE, PRAGMA, tag[HTML_PROP]) if meta_cache_tag?(tag[HTML_PROP])
96
95
  end
97
96
  end
98
97
 
@@ -11,13 +11,13 @@ module Contrast
11
11
  module Response
12
12
  # These rules check the content of the HTTP Response to determine if the headers contains the required header
13
13
  class ClickJacking < HeaderRule
14
- def rule_id
15
- 'clickjacking-control-missing'
16
- end
17
-
18
14
  HEADER_KEYS = %w[X-Frame-Options].cs__freeze
19
15
  ACCEPTED_VALUES = [/^deny/i, /^sameorigin/i].cs__freeze
20
16
  DEFAULT_SAFE = false
17
+
18
+ def rule_id
19
+ 'clickjacking-control-missing'
20
+ end
21
21
  end
22
22
  end
23
23
  end
@@ -12,12 +12,6 @@ module Contrast
12
12
  module Response
13
13
  # These rules check that the HTTP Headers include CSP header types
14
14
  class CspHeaderInsecure < HeaderRule
15
- def rule_id
16
- 'csp-header-insecure'
17
- end
18
-
19
- protected
20
-
21
15
  HEADER_KEYS = %w[Content-Security-Policy X-Content-Security-Policy X-Webkit-CSP].cs__freeze
22
16
  DEFAULT_SAFE = false
23
17
  SETTINGS = %w[
@@ -28,6 +22,12 @@ module Contrast
28
22
  ASTERISK_REGEXP = /[*]/.cs__freeze
29
23
  SAFE_REFLECTED_XSS = /1/.cs__freeze
30
24
 
25
+ def rule_id
26
+ 'csp-header-insecure'
27
+ end
28
+
29
+ protected
30
+
31
31
  # Determine if the Response violates the Rule or not. If it does, return the evidence that proves it so.
32
32
  #
33
33
  # @param response [Contrast::Agent::Response] the response of the application
@@ -11,13 +11,13 @@ module Contrast
11
11
  module Response
12
12
  # These rules check that the HTTP Headers include CSP header types
13
13
  class CspHeaderMissing < HeaderRule
14
- def rule_id
15
- 'csp-header-missing'
16
- end
17
-
18
14
  HEADER_KEYS = %w[Content-Security-Policy X-Content-Security-Policy X-Webkit-CSP].cs__freeze
19
15
  ACCEPTED_VALUES = [/(.)/].cs__freeze
20
16
  DEFAULT_SAFE = false
17
+
18
+ def rule_id
19
+ 'csp-header-missing'
20
+ end
21
21
  end
22
22
  end
23
23
  end
@@ -12,16 +12,16 @@ module Contrast
12
12
  # This rule checks if the HTTP Headers include HSTS header and ensures that the max-age value
13
13
  # is set to a value greater than 0.
14
14
  class HSTSHeader < HeaderRule
15
+ HEADER_KEYS = %w[Strict-Transport-Security].cs__freeze
16
+ ACCEPTED_VALUES = [/max-age=(\.)?\d+(\.\d*)?/].cs__freeze
17
+ DEFAULT_SAFE = true
18
+
15
19
  def rule_id
16
20
  'hsts-header-missing'
17
21
  end
18
22
 
19
23
  protected
20
24
 
21
- HEADER_KEYS = %w[Strict-Transport-Security].cs__freeze
22
- ACCEPTED_VALUES = [/max-age=(\.)?\d+(\.\d*)?/].cs__freeze
23
- DEFAULT_SAFE = true
24
-
25
25
  def evidence data
26
26
  # get only the value of the max-age property
27
27
  val = data&.split('=')&.last
@@ -11,13 +11,13 @@ module Contrast
11
11
  module Response
12
12
  # These rules check the content of the HTTP Response to determine if the response contains the needed header
13
13
  class XContentType < HeaderRule
14
- def rule_id
15
- 'xcontenttype-header-missing'
16
- end
17
-
18
14
  HEADER_KEYS = %w[X-Content-Type-Options].cs__freeze
19
15
  ACCEPTED_VALUES = [/^nosniff/i].cs__freeze
20
16
  DEFAULT_SAFE = false
17
+
18
+ def rule_id
19
+ 'xcontenttype-header-missing'
20
+ end
21
21
  end
22
22
  end
23
23
  end
@@ -13,15 +13,14 @@ module Contrast
13
13
  # These rules check the content of the HTTP Response to determine if the response contains the needed header
14
14
  class XXssProtection < HeaderRule
15
15
  include Framework::RailsSupport
16
+ HEADER_KEYS = %w[X-XSS-Protection].cs__freeze
17
+ ACCEPTED_VALUES = [/^1/].cs__freeze
18
+ DEFAULT_SAFE = true
16
19
 
17
20
  def rule_id
18
21
  'xxssprotection-header-disabled'
19
22
  end
20
23
 
21
- HEADER_KEYS = %w[X-XSS-Protection].cs__freeze
22
- ACCEPTED_VALUES = [/^1/].cs__freeze
23
- DEFAULT_SAFE = true
24
-
25
24
  protected
26
25
 
27
26
  def analyze_response? response
@@ -12,6 +12,19 @@ module Contrast
12
12
  :start_idx, # start of range
13
13
  :end_idx # end of range (calculated from start + length), exclusive
14
14
 
15
+ # Update range should be how start and end index of tags are changed,
16
+ # as it includes validation
17
+ #
18
+ # Note that we allow start_idx == end_idx b/c this is how we determine
19
+ # if a tag range is 'covered' in trigger detection
20
+ ERROR_NEGATIVE_START = 'Unable to set start idx negative'
21
+ BELOW = 'BELOW'
22
+ ERROR_END_BEFORE_START = 'Unable to set start idx after end idx'
23
+ LOW_SPAN = 'LOW_SPAN'
24
+ WITHIN = 'WITHIN'
25
+ WITHOUT = 'WITHOUT'
26
+ HIGH_SPAN = 'HIGH_SPAN'
27
+ ABOVE = 'ABOVE'
15
28
  # Initialize a new tag
16
29
  #
17
30
  # @param label [String] the label of the tag
@@ -109,13 +122,6 @@ module Contrast
109
122
  end
110
123
  alias_method :to_s, :str_val
111
124
 
112
- BELOW = 'BELOW'
113
- LOW_SPAN = 'LOW_SPAN'
114
- WITHIN = 'WITHIN'
115
- WITHOUT = 'WITHOUT'
116
- HIGH_SPAN = 'HIGH_SPAN'
117
- ABOVE = 'ABOVE'
118
-
119
125
  # The tag is ______ the range
120
126
  # rrrrrrr == self.range, the range of the tag
121
127
  def compare_range start, stop
@@ -154,13 +160,6 @@ module Contrast
154
160
 
155
161
  private
156
162
 
157
- # Update range should be how start and end index of tags are changed,
158
- # as it includes validation
159
- #
160
- # Note that we allow start_idx == end_idx b/c this is how we determine
161
- # if a tag range is 'covered' in trigger detection
162
- ERROR_NEGATIVE_START = 'Unable to set start idx negative'
163
- ERROR_END_BEFORE_START = 'Unable to set start idx after end idx'
164
163
  def update_range start_idx, end_idx
165
164
  raise(ArgumentError, ERROR_NEGATIVE_START) if start_idx.negative?
166
165
  raise(ArgumentError, ERROR_END_BEFORE_START) if end_idx < start_idx
@@ -31,7 +31,19 @@ module Contrast
31
31
  context = Contrast::Agent::REQUEST_TRACKER.current
32
32
  return unless context
33
33
 
34
- Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
34
+ Contrast::Agent.reporter.send_event_immediately(context.observed_library_usage)
35
+
36
+ if Contrast::Agent::Reporter.enabled?
37
+ [
38
+ context.new_observed_route,
39
+ Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.server_activity),
40
+ Contrast::Agent::Reporting::DtmMessage.dtm_to_event(context.activity)
41
+ ].each do |event|
42
+ Contrast::Agent.reporter&.send_event_immediately(event)
43
+ end
44
+ else
45
+ Contrast::Agent.messaging_queue&.send_event_immediately(context.activity)
46
+ end
35
47
  end
36
48
  end
37
49
  end
@@ -74,7 +74,7 @@ module Contrast
74
74
  # reporting.
75
75
  #
76
76
  # @param hash_or_str [Hash, String]
77
- # @return [Contrast::Api::Dtm::ArchitectureComponent]
77
+ # @return [Array<Contrast::Api::Dtm::ArchitectureComponent>, nil]
78
78
  def build_from_db_config hash_or_str
79
79
  return unless hash_or_str
80
80
 
@@ -89,7 +89,7 @@ module Contrast
89
89
  # understandable by TeamServer.
90
90
  #
91
91
  # @param hash [Hash] the information used to open a database connection
92
- # @return [Contrast::Api::Dtm::ArchitectureComponent]
92
+ # @return [Array<Contrast::Api::Dtm::ArchitectureComponent>]
93
93
  def build_from_db_hash hash
94
94
  ac = Contrast::Api::Dtm::ArchitectureComponent.build_database
95
95
  ac.vendor = hash[:adapter] || hash[ADAPTER] || Contrast::Utils::ObjectShare::EMPTY_STRING
@@ -122,9 +122,11 @@ module Contrast
122
122
  # mysql+mysqlconnector://scott:tiger@localhost/foo # pragma: allowlist secret
123
123
  #
124
124
  # @param str [String] the DB connection string
125
- # @return [Contrast::Api::Dtm::ArchitectureComponent]
125
+ # @return [Array<Contrast::Api::Dtm::ArchitectureComponent>, nil]
126
126
  def build_from_db_string str
127
127
  adapter, hosts, database = split_connection_str(str)
128
+ return unless adapter && hosts && database
129
+
128
130
  acs = []
129
131
  hosts.split(Contrast::Utils::ObjectShare::COMMA).map do |s|
130
132
  host, port = s.split(Contrast::Utils::ObjectShare::COLON)
@@ -143,13 +145,19 @@ module Contrast
143
145
  # generation of a Contrast::Api::Dtm::ArchitectureComponent
144
146
  #
145
147
  # @param str [String] the DB connection string
146
- # @return [Array<String>] the adapter, hosts, and database
148
+ # @return [Array<String>, nil] the adapter, hosts, and database
147
149
  def split_connection_str str
148
150
  adapter, str = str.split(Contrast::Utils::ObjectShare::COLON_SLASH_SLASH)
151
+ return unless str
152
+
149
153
  _auth, str = str.split(Contrast::Utils::ObjectShare::AT)
154
+ return unless str
155
+
150
156
  # Not currently used
151
157
  # user, pass = auth.split(Contrast::Utils::ObjectShare::COLON)
152
158
  hosts, db_and_options = str.split(Contrast::Utils::ObjectShare::SLASH)
159
+ return unless db_and_options
160
+
153
161
  hosts << LOCALHOST if hosts.empty?
154
162
  database, _options = db_and_options.split(Contrast::Utils::ObjectShare::QUESTION_MARK)
155
163
 
@@ -1,6 +1,7 @@
1
1
  # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
2
  # frozen_string_literal: true
3
3
 
4
+ require 'contrast/agent/reporting/reporting_events/library_usage_observation'
4
5
  require 'contrast/agent/inventory/dependencies'
5
6
  require 'contrast/components/logger'
6
7
  require 'contrast/utils/object_share'
@@ -73,10 +74,11 @@ module Contrast
73
74
  #
74
75
  # TODO: RUBY-1355
75
76
  # TODO: RUBY-1438 -- change to just use EventMessage
76
- # @param activity [Contrast::Api::Dtm::Activity] the message to which to append the usage data
77
- def generate_library_usage activity
77
+ # @param observed_library_usage [Contrast::Agent::Reporting::ObservedLibraryUsage] the message to
78
+ # which to append the usage data
79
+ def generate_library_usage observed_library_usage
78
80
  return unless enabled?
79
- return unless activity
81
+ return unless observed_library_usage
80
82
 
81
83
  # Disconnect gemdigest_cache and replace it with an empty one; synch so new libs cannot be added between the
82
84
  # assignment and the replace
@@ -86,8 +88,10 @@ module Contrast
86
88
  hold
87
89
  end
88
90
  gem_spec_digest_to_files.each_pair do |digest, files|
89
- usage = Contrast::Api::Dtm::LibraryUsageUpdate.build(digest, files)
90
- activity.library_usages[usage.hash_code] = usage
91
+ usage = Contrast::Agent::Reporting::LibraryUsageObservation.new(digest, files)
92
+ next if usage.names.empty?
93
+
94
+ observed_library_usage.observations << usage
91
95
  end
92
96
  rescue StandardError => e
93
97
  logger.error('Unable to generate library usage.', e)
@@ -48,6 +48,9 @@ module Contrast
48
48
  return
49
49
  end
50
50
  agent_startup_routine
51
+ rescue StandardError => e
52
+ logger.error('Unable to initialize the agent. Disabling permanently.', e)
53
+ ::Contrast::AGENT.disable! # ensure the agent is disabled (probably redundant)
51
54
  end
52
55
 
53
56
  # This is where we're hooked into the middleware stack. If the agent is enabled, we're ready to do some
@@ -145,7 +148,7 @@ module Contrast
145
148
  request_handler.ruleset.prefilter
146
149
  end
147
150
  rescue StandardError => e
148
- raise e if security_exception?(e)
151
+ raise(e) if security_exception?(e)
149
152
 
150
153
  logger.error('Unable to execute agent pre_call', e)
151
154
  end
@@ -164,7 +167,7 @@ module Contrast
164
167
  # Build and report all collected findings prior response
165
168
  Contrast::Agent::FINDINGS.report_collected_findings unless Contrast::Agent::FINDINGS.collection.empty?
166
169
  # All protect rules, which are trigger but require response to be reported
167
- Contrast::Agent::EXPLOITS.report_recorded_exploits context unless Contrast::Agent::EXPLOITS.collection.empty?
170
+ Contrast::Agent::EXPLOITS.report_recorded_exploits(context) unless Contrast::Agent::EXPLOITS.collection.empty?
168
171
 
169
172
  if Contrast::Agent.framework_manager.streaming?(env)
170
173
  context.reset_activity
@@ -177,7 +180,7 @@ module Contrast
177
180
  end
178
181
  # unsuccessful attack
179
182
  rescue StandardError => e
180
- raise e if security_exception?(e)
183
+ raise(e) if security_exception?(e)
181
184
 
182
185
  logger.error('Unable to execute agent post_call', e)
183
186
  end
@@ -47,7 +47,7 @@ module Contrast
47
47
  return true unless target_defined? # bc no methods are loaded
48
48
  return false unless method_to_instrument
49
49
 
50
- !module_lookup.instance_methods.include? method_to_instrument
50
+ !module_lookup.instance_methods.include?(method_to_instrument)
51
51
  end
52
52
 
53
53
  def applies? loaded_module_name
@@ -59,7 +59,7 @@ module Contrast
59
59
  end
60
60
 
61
61
  def instrument!
62
- require instrumentation_file_path
62
+ require(instrumentation_file_path)
63
63
 
64
64
  if instrumenting_module
65
65
  mod = Module.cs__const_get(instrumenting_module)
@@ -72,7 +72,7 @@ module Contrast
72
72
 
73
73
  def module_lookup
74
74
  @_module_lookup ||= begin
75
- Module.cs__const_get module_name
75
+ Module.cs__const_get(module_name)
76
76
  rescue StandardError => _e
77
77
  nil
78
78
  end
@@ -43,7 +43,7 @@ module Contrast
43
43
  ].cs__freeze
44
44
  paths.each do |p|
45
45
  path_part = "cs__assess_#{ p }"
46
- Contrast::Extension::Assess::InstrumentHelper.instrument "#{ path_part }/#{ path_part }"
46
+ Contrast::Extension::Assess::InstrumentHelper.instrument("#{ path_part }/#{ path_part }")
47
47
  end
48
48
  true
49
49
  end
@@ -73,7 +73,7 @@ module Contrast
73
73
  # handling
74
74
  def apply_require_patches!
75
75
  @_apply_require_patches ||= begin
76
- require 'contrast/extension/thread'
76
+ require('contrast/extension/thread')
77
77
  true
78
78
  rescue LoadError, StandardError => e
79
79
  logger.error('failed instrumenting apply_require_patches!', e)
@@ -38,8 +38,8 @@ module Contrast
38
38
  deadzone_node: deadzone_node
39
39
  })
40
40
 
41
- return method_policy unless check_method_policy_nodes_empty? source_node, propagation_node, trigger_node,
42
- protect_node, inventory_node, deadzone_node
41
+ return method_policy unless check_method_policy_nodes_empty?(source_node, propagation_node, trigger_node,
42
+ protect_node, inventory_node, deadzone_node)
43
43
 
44
44
  create_new_node(module_policy, method_policy) if module_policy.deadzone_nodes&.any?
45
45
  method_policy
@@ -74,9 +74,9 @@ module Contrast
74
74
  next unless node.method_name.nil?
75
75
 
76
76
  klass = Module.cs__const_get(node.class_name)
77
- next unless it_defined? klass, method_policy.method_name
77
+ next unless it_defined?(klass, method_policy.method_name)
78
78
 
79
- new_node = set_new_node method_policy, klass, node
79
+ new_node = set_new_node(method_policy, klass, node)
80
80
  method_policy.instance_variable_set(:@method_visibility, new_node.method_visibility)
81
81
  method_policy.instance_variable_set(:@deadzone_node, node)
82
82
  module_policy.deadzone_nodes << new_node
@@ -49,11 +49,11 @@ module Contrast
49
49
  ].cs__freeze
50
50
 
51
51
  def enter_method_scope! method_policy
52
- contrast_enter_method_scopes! method_policy.scopes_to_enter
52
+ contrast_enter_method_scopes!(method_policy.scopes_to_enter)
53
53
  end
54
54
 
55
55
  def exit_method_scope! method_policy
56
- contrast_exit_method_scopes! method_policy.scopes_to_exit
56
+ contrast_exit_method_scopes!(method_policy.scopes_to_exit)
57
57
  end
58
58
 
59
59
  # @param mod [Module] the module in which the patch should be
@@ -122,7 +122,7 @@ module Contrast
122
122
  underlying_method_name = underlying_method_name(method_name, impl)
123
123
 
124
124
  target_module = Module.cs__const_get(target_module_name)
125
- target_module = target_module.cs__singleton_class if %i[prepend_singleton alias_singleton].include? impl
125
+ target_module = target_module.cs__singleton_class if %i[prepend_singleton alias_singleton].include?(impl)
126
126
 
127
127
  visibility = if target_module.private_instance_methods(false).include?(method_name)
128
128
  :private
@@ -131,14 +131,14 @@ module Contrast
131
131
  elsif target_module.public_instance_methods(false).include?(method_name)
132
132
  :public
133
133
  else
134
- raise NoMethodError,
135
- <<~ERR
134
+ raise(NoMethodError,
135
+ <<~ERR)
136
136
  Tried to register a C-defined #{ impl } patch for \
137
137
  #{ target_module_name }##{ method_name }, but can't find :#{ method_name }.
138
138
  ERR
139
139
  end
140
140
 
141
- reflect_implementation impl, target_module, unbound_method, visibility
141
+ reflect_implementation(impl, target_module, unbound_method, visibility)
142
142
  # Ougai::Logger.create_item_with_2args calls Hash#[]=, so we
143
143
  # can't invoke this logging method or we'll seg fault as we'd
144
144
  # change the method definition mid-call
@@ -166,7 +166,7 @@ module Contrast
166
166
  when :alias_instance, :alias_singleton
167
167
  # Core to patching. Ignore define method usage cop.
168
168
  # rubocop:disable Performance/Kernel/DefineMethod
169
- unless target_module.instance_methods(false).include? underlying_method_name
169
+ unless target_module.instance_methods(false).include?(underlying_method_name)
170
170
  # alias_method may be private
171
171
  target_module.send(:alias_method, underlying_method_name, method_name)
172
172
  target_module.send(:define_method, method_name, unbound_method.bind(target_module))
@@ -178,7 +178,7 @@ module Contrast
178
178
  prepending_module.send(visibility, method_name)
179
179
 
180
180
  # This prepends to the singleton class (it patches a class method)
181
- target_module.prepend prepending_module
181
+ target_module.prepend(prepending_module)
182
182
  # rubocop:enable Performance/Kernel/DefineMethod
183
183
  end
184
184
  end
@@ -204,7 +204,7 @@ module Contrast
204
204
  end
205
205
 
206
206
  def underlying_method_name method_name, impl
207
- return method_name.to_sym if %i[prepend_instance prepend_singleton].include? impl
207
+ return method_name.to_sym if %i[prepend_instance prepend_singleton].include?(impl)
208
208
 
209
209
  build_unbound_method_name(method_name).to_sym
210
210
  end
@@ -99,10 +99,17 @@ module Contrast
99
99
  :CONTRAST_PATCH_POLICY_STATUS
100
100
  end
101
101
 
102
+ # @param mod [Module or Class] the entity on which the patch has been placed
103
+ # @param method [Symbol] the method being patched
104
+ # @param ret [Contrast::Agent::Patching::Policy::MethodPolicy] the policy of the patched method
102
105
  def update_holder mod, method, ret
103
- mod.instance_variable_set(method_info_key, {}) unless mod.instance_variable_defined?(method_info_key)
104
- holder = mod.instance_variable_get(method_info_key)
105
- holder[method] = ret
106
+ unless mod.instance_variable_defined?(method_info_key) &&
107
+ (holder = mod.instance_variable_get(method_info_key))
108
+
109
+ holder = {}
110
+ mod.instance_variable_set(method_info_key, holder)
111
+ end
112
+ holder[method] = ret # rubocop:disable Lint/UselessSetterCall
106
113
  end
107
114
 
108
115
  def find_info_for candidates, method
@@ -20,6 +20,19 @@ module Contrast
20
20
  class Policy
21
21
  include Singleton
22
22
  include Contrast::Components::Logger::InstanceMethods
23
+ SOURCES_KEY = 'sources'
24
+ PROPAGATION_KEY = 'propagators'
25
+ RULES_KEY = 'rules'
26
+ TRIGGERS_KEY = 'triggers'
27
+ def initialize
28
+ @sources = []
29
+ @propagators = []
30
+ @triggers = []
31
+ @providers = {}
32
+
33
+ json = Contrast::Utils::ResourceLoader.load(cs__class.policy_json)
34
+ from_hash_string(json)
35
+ end
23
36
 
24
37
  # Indicates the folder in `resources` where this policy lives.
25
38
  def self.policy_folder
@@ -38,25 +51,10 @@ module Contrast
38
51
 
39
52
  attr_reader :sources, :propagators, :triggers, :providers
40
53
 
41
- SOURCES_KEY = 'sources'
42
- PROPAGATION_KEY = 'propagators'
43
- RULES_KEY = 'rules'
44
- TRIGGERS_KEY = 'triggers'
45
-
46
54
  def self.policy_json
47
55
  File.join(policy_folder, 'policy.json').cs__freeze
48
56
  end
49
57
 
50
- def initialize
51
- @sources = []
52
- @propagators = []
53
- @triggers = []
54
- @providers = {}
55
-
56
- json = Contrast::Utils::ResourceLoader.load(cs__class.policy_json)
57
- from_hash_string(json)
58
- end
59
-
60
58
  # Our policy for patching rules is a 'dope ass' JSON file. Rather than hard code in a bunch of things to
61
59
  # monkey patch, we let the JSON file define the conditions in which modifications are applied. This let's us
62
60
  # be flexible and extensible.
@@ -14,6 +14,23 @@ module Contrast
14
14
  # @abstract
15
15
  class PolicyNode
16
16
  include Contrast::Components::Scope::InstanceMethods
17
+ JSON_METHOD_VISIBILITY = 'method_visibility'
18
+ JSON_PROPERTIES = 'properties'
19
+ JSON_METHOD_NAME = 'method_name'
20
+ JSON_METHOD_SCOPE = 'scope'
21
+ # The keys used to read from policy.json to create the individual
22
+ # policy nodes. These are common across node types
23
+ JSON_CLASS_NAME = 'class_name'
24
+ JSON_INSTANCE_METHOD = 'instance_method'
25
+ def initialize policy_hash = {}
26
+ @class_name = policy_hash[JSON_CLASS_NAME]
27
+ @instance_method = policy_hash[JSON_INSTANCE_METHOD]
28
+ @method_name = policy_hash[JSON_METHOD_NAME]
29
+ @method_scope = policy_hash[JSON_METHOD_SCOPE]
30
+ @method_visibility = policy_hash[JSON_METHOD_VISIBILITY]
31
+ @properties = policy_hash[JSON_PROPERTIES]
32
+ symbolize
33
+ end
17
34
 
18
35
  # Name of the class in which the method is being invoked.
19
36
  attr_accessor :class_name
@@ -31,21 +48,11 @@ module Contrast
31
48
  attr_reader :method_scope
32
49
 
33
50
  def node_class
34
- raise NoMethodError, 'specify the type of the feature for which this node patches'
51
+ raise(NoMethodError, 'specify the type of the feature for which this node patches')
35
52
  end
36
53
 
37
54
  def feature
38
- raise NoMethodError, 'specify the name of the feature for which this node patches'
39
- end
40
-
41
- def initialize policy_hash = {}
42
- @class_name = policy_hash[JSON_CLASS_NAME]
43
- @instance_method = policy_hash[JSON_INSTANCE_METHOD]
44
- @method_name = policy_hash[JSON_METHOD_NAME]
45
- @method_scope = policy_hash[JSON_METHOD_SCOPE]
46
- @method_visibility = policy_hash[JSON_METHOD_VISIBILITY]
47
- @properties = policy_hash[JSON_PROPERTIES]
48
- symbolize
55
+ raise(NoMethodError, 'specify the name of the feature for which this node patches')
49
56
  end
50
57
 
51
58
  def id
@@ -91,15 +98,6 @@ module Contrast
91
98
  @method_visibility = @method_visibility.to_sym if @method_visibility
92
99
  @method_scope = @method_scope.to_sym if @method_scope
93
100
  end
94
-
95
- # The keys used to read from policy.json to create the individual
96
- # policy nodes. These are common across node types
97
- JSON_CLASS_NAME = 'class_name'
98
- JSON_INSTANCE_METHOD = 'instance_method'
99
- JSON_METHOD_NAME = 'method_name'
100
- JSON_METHOD_SCOPE = 'scope'
101
- JSON_METHOD_VISIBILITY = 'method_visibility'
102
- JSON_PROPERTIES = 'properties'
103
101
  end
104
102
  end
105
103
  end
@@ -24,6 +24,7 @@ module Contrast
24
24
  attr_reader :applicator, :applicator_method, :on_exception, :optional_properties, :required_properties,
25
25
  :rule_id
26
26
 
27
+ NODE = 'Trigger'
27
28
  def initialize trigger_hash = {}, rule_hash = {}
28
29
  super(trigger_hash)
29
30
  @rule_id = rule_hash[JSON_NAME]
@@ -36,7 +37,6 @@ module Contrast
36
37
  @applicator_method = (trigger_hash[JSON_APPLICATOR_METHOD] || rule_hash[JSON_APPLICATOR_METHOD]).to_sym
37
38
  end
38
39
 
39
- NODE = 'Trigger'
40
40
  def node_class
41
41
  NODE
42
42
  end