contrast-agent 6.1.0 → 6.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) 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 +12 -1
  37. data/lib/contrast/agent/middleware.rb +6 -3
  38. data/lib/contrast/agent/patching/policy/after_load_patch.rb +3 -3
  39. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +2 -2
  40. data/lib/contrast/agent/patching/policy/method_policy_extend.rb +4 -4
  41. data/lib/contrast/agent/patching/policy/patch.rb +9 -9
  42. data/lib/contrast/agent/patching/policy/patch_status.rb +10 -3
  43. data/lib/contrast/agent/patching/policy/policy.rb +13 -15
  44. data/lib/contrast/agent/patching/policy/policy_node.rb +19 -21
  45. data/lib/contrast/agent/patching/policy/trigger_node.rb +1 -1
  46. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +125 -125
  47. data/lib/contrast/agent/protect/policy/applies_no_sqli_rule.rb +2 -2
  48. data/lib/contrast/agent/protect/policy/applies_path_traversal_rule.rb +1 -1
  49. data/lib/contrast/agent/protect/policy/applies_xxe_rule.rb +1 -1
  50. data/lib/contrast/agent/protect/policy/rule_applicator.rb +4 -4
  51. data/lib/contrast/agent/protect/rule/base.rb +30 -18
  52. data/lib/contrast/agent/protect/rule/base_service.rb +31 -14
  53. data/lib/contrast/agent/protect/rule/cmd_injection.rb +16 -9
  54. data/lib/contrast/agent/protect/rule/cmdi/cmdi_input_classification.rb +3 -3
  55. data/lib/contrast/agent/protect/rule/default_scanner.rb +2 -1
  56. data/lib/contrast/agent/protect/rule/deserialization.rb +18 -7
  57. data/lib/contrast/agent/protect/rule/http_method_tampering/http_method_tampering_input_classification.rb +74 -74
  58. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +71 -53
  59. data/lib/contrast/agent/protect/rule/no_sqli/no_sqli_input_classification.rb +3 -3
  60. data/lib/contrast/agent/protect/rule/no_sqli.rb +15 -16
  61. data/lib/contrast/agent/protect/rule/path_traversal.rb +13 -3
  62. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +2 -2
  63. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +1 -1
  64. data/lib/contrast/agent/protect/rule/sqli.rb +16 -23
  65. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_input_classification.rb +61 -61
  66. data/lib/contrast/agent/protect/rule/unsafe_file_upload/unsafe_file_upload_matcher.rb +29 -29
  67. data/lib/contrast/agent/protect/rule/unsafe_file_upload.rb +32 -32
  68. data/lib/contrast/agent/protect/rule/xss.rb +17 -0
  69. data/lib/contrast/agent/protect/rule/xxe/entity_wrapper.rb +14 -13
  70. data/lib/contrast/agent/protect/rule/xxe.rb +25 -3
  71. data/lib/contrast/agent/reaction_processor.rb +1 -1
  72. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +36 -36
  73. data/lib/contrast/agent/reporting/masker/masker.rb +10 -10
  74. data/lib/contrast/agent/reporting/masker/masker_utils.rb +2 -2
  75. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +8 -10
  76. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +53 -5
  77. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +25 -19
  78. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +129 -17
  79. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +20 -21
  80. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_stack.rb +22 -0
  81. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +26 -12
  82. data/lib/contrast/agent/reporting/reporting_events/application_inventory.rb +5 -5
  83. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +7 -5
  84. data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +4 -10
  85. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +1 -1
  86. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -2
  87. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +6 -6
  88. data/lib/contrast/agent/reporting/reporting_utilities/response.rb +1 -1
  89. data/lib/contrast/agent/reporting/reporting_utilities/response_extractor.rb +1 -1
  90. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +7 -7
  91. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +15 -15
  92. data/lib/contrast/agent/reporting/settings/application_settings.rb +1 -1
  93. data/lib/contrast/agent/reporting/settings/assess.rb +5 -5
  94. data/lib/contrast/agent/reporting/settings/assess_server_feature.rb +3 -3
  95. data/lib/contrast/agent/reporting/settings/exclusions.rb +3 -3
  96. data/lib/contrast/agent/reporting/settings/protect.rb +20 -5
  97. data/lib/contrast/agent/reporting/settings/protect_server_feature.rb +5 -5
  98. data/lib/contrast/agent/reporting/settings/reaction.rb +3 -3
  99. data/lib/contrast/agent/reporting/settings/sensitive_data_masking.rb +2 -2
  100. data/lib/contrast/agent/reporting/settings/sensitive_data_masking_rule.rb +2 -2
  101. data/lib/contrast/agent/reporting/settings/server_features.rb +2 -2
  102. data/lib/contrast/agent/request.rb +2 -2
  103. data/lib/contrast/agent/request_context.rb +23 -19
  104. data/lib/contrast/agent/request_context_extend.rb +10 -23
  105. data/lib/contrast/agent/request_handler.rb +1 -1
  106. data/lib/contrast/agent/rule_set.rb +2 -2
  107. data/lib/contrast/agent/scope.rb +1 -1
  108. data/lib/contrast/agent/telemetry/base.rb +9 -5
  109. data/lib/contrast/agent/telemetry/events/exceptions/obfuscate.rb +119 -0
  110. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_base.rb +2 -2
  111. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_message_exception.rb +1 -1
  112. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exception_stack_frame.rb +1 -1
  113. data/lib/contrast/agent/telemetry/events/exceptions/telemetry_exceptions_report.rb +16 -18
  114. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +2 -2
  115. data/lib/contrast/agent/version.rb +1 -1
  116. data/lib/contrast/api/communication/messaging_queue.rb +1 -1
  117. data/lib/contrast/api/communication/service_lifecycle.rb +1 -1
  118. data/lib/contrast/api/communication/socket.rb +1 -1
  119. data/lib/contrast/api/communication/socket_client.rb +1 -1
  120. data/lib/contrast/api/communication/speedracer.rb +2 -2
  121. data/lib/contrast/api/decorators/agent_startup.rb +10 -9
  122. data/lib/contrast/api/decorators/application_settings.rb +1 -1
  123. data/lib/contrast/api/decorators/application_startup.rb +4 -4
  124. data/lib/contrast/api/decorators/response_type.rb +4 -17
  125. data/lib/contrast/components/agent.rb +1 -1
  126. data/lib/contrast/components/base.rb +1 -1
  127. data/lib/contrast/components/config.rb +6 -6
  128. data/lib/contrast/components/contrast_service.rb +4 -1
  129. data/lib/contrast/components/sampling.rb +1 -1
  130. data/lib/contrast/components/settings.rb +52 -28
  131. data/lib/contrast/config/assess_rules_configuration.rb +1 -1
  132. data/lib/contrast/config/protect_rules_configuration.rb +1 -1
  133. data/lib/contrast/config/root_configuration.rb +1 -1
  134. data/lib/contrast/configuration.rb +4 -4
  135. data/lib/contrast/extension/assess/array.rb +1 -1
  136. data/lib/contrast/extension/assess/erb.rb +1 -1
  137. data/lib/contrast/extension/assess/marshal.rb +1 -1
  138. data/lib/contrast/extension/assess/string.rb +1 -1
  139. data/lib/contrast/extension/extension.rb +2 -2
  140. data/lib/contrast/framework/base_support.rb +8 -8
  141. data/lib/contrast/framework/grape/support.rb +3 -3
  142. data/lib/contrast/framework/manager.rb +5 -5
  143. data/lib/contrast/framework/manager_extend.rb +1 -1
  144. data/lib/contrast/framework/rack/patch/session_cookie.rb +1 -1
  145. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +14 -3
  146. data/lib/contrast/framework/rails/patch/assess_configuration.rb +3 -3
  147. data/lib/contrast/framework/rails/patch/rails_application_configuration.rb +1 -1
  148. data/lib/contrast/framework/rails/patch/support.rb +1 -1
  149. data/lib/contrast/framework/rails/support.rb +2 -2
  150. data/lib/contrast/framework/sinatra/support.rb +1 -1
  151. data/lib/contrast/logger/aliased_logging.rb +29 -22
  152. data/lib/contrast/logger/cef_log.rb +14 -14
  153. data/lib/contrast/logger/format.rb +1 -1
  154. data/lib/contrast/logger/log.rb +8 -8
  155. data/lib/contrast/tasks/config.rb +12 -12
  156. data/lib/contrast/tasks/service.rb +2 -2
  157. data/lib/contrast/utils/assess/tracking_util.rb +4 -4
  158. data/lib/contrast/utils/class_util.rb +4 -4
  159. data/lib/contrast/utils/findings.rb +3 -3
  160. data/lib/contrast/utils/hash_digest.rb +6 -7
  161. data/lib/contrast/utils/head_dump_utils_extend.rb +1 -1
  162. data/lib/contrast/utils/invalid_configuration_util.rb +1 -1
  163. data/lib/contrast/utils/log_utils.rb +4 -4
  164. data/lib/contrast/utils/lru_cache.rb +1 -1
  165. data/lib/contrast/utils/metrics_hash.rb +1 -1
  166. data/lib/contrast/utils/middleware_utils.rb +5 -5
  167. data/lib/contrast/utils/net_http_base.rb +4 -4
  168. data/lib/contrast/utils/os.rb +1 -1
  169. data/lib/contrast/utils/patching/policy/patch_utils.rb +2 -2
  170. data/lib/contrast/utils/request_utils.rb +2 -2
  171. data/lib/contrast/utils/sha256_builder.rb +4 -4
  172. data/lib/contrast/utils/stack_trace_utils.rb +31 -13
  173. data/lib/contrast/utils/telemetry.rb +6 -9
  174. data/lib/contrast/utils/telemetry_client.rb +5 -5
  175. data/lib/contrast/utils/telemetry_hash.rb +1 -1
  176. data/lib/contrast/utils/telemetry_identifier.rb +2 -2
  177. data/lib/contrast/utils/timer.rb +1 -1
  178. data/resources/assess/policy.json +1 -1
  179. metadata +15 -13
@@ -18,7 +18,7 @@ module Contrast
18
18
  # @return [Contrast::Agent::Reporting::ApplicationDefendActivity]
19
19
  def convert activity_dtm
20
20
  activity = new
21
- activity.attach_data activity_dtm.results
21
+ activity.attach_data(activity_dtm.results)
22
22
  activity
23
23
  end
24
24
  end
@@ -35,11 +35,59 @@ module Contrast
35
35
  }
36
36
  end
37
37
 
38
- # @param attack_dtms [Contrast::Api::Dtm::AttackResult]
39
- # @return [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
38
+ # @param attack_dtms [Array<Contrast::Api::Dtm::AttackResult>]
40
39
  def attach_data attack_dtms
41
- attack_dtms.each do |attack|
42
- @attackers << Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.convert(attack)
40
+ attack_dtms.each do |attack_result|
41
+ attacker_activity = Contrast::Agent::Reporting::ApplicationDefendAttackerActivity.convert(attack_result)
42
+ existing_attacker_activity = attackers.find do |existing|
43
+ existing.source_forwarded_for == attacker_activity.source_forwarded_for &&
44
+ existing.source_ip == attacker_activity.source_ip
45
+ end
46
+ rule = attack_result.rule_id
47
+ if existing_attacker_activity
48
+ attach_existing(existing_attacker_activity, attacker_activity, rule)
49
+ else
50
+ attackers << attacker_activity
51
+ end
52
+ end
53
+ end
54
+
55
+ # @param existing_attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
56
+ # @param attacker_activity [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
57
+ # @param rule [String]
58
+ def attach_existing existing_attacker_activity, attacker_activity, rule
59
+ # TODO: RUBY-1663 figure out attack time mapping
60
+ new_violation = attacker_activity.protection_rules[rule]
61
+ if (previously_violated = existing_attacker_activity.protection_rules[rule])
62
+ if (new_blocked = new_violation.blocked&.samples)
63
+ unless previously_violated.blocked
64
+ previously_violated.blocked = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
65
+ end
66
+ previously_violated.blocked.samples.concat(new_blocked)
67
+ end
68
+
69
+ if (new_exploited = new_violation.exploited&.samples)
70
+ unless previously_violated.exploited
71
+ previously_violated.exploited = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
72
+ end
73
+ previously_violated.exploited.samples.concat(new_exploited)
74
+ end
75
+
76
+ if (new_ineffective = new_violation.ineffective&.samples)
77
+ unless previously_violated.ineffective
78
+ previously_violated.ineffective = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
79
+ end
80
+ previously_violated.ineffective.samples.concat(new_ineffective)
81
+ end
82
+
83
+ if (new_suspicious = new_violation.suspicious&.samples)
84
+ unless previously_violated.suspicious
85
+ previously_violated.suspicious = Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.new
86
+ end
87
+ previously_violated.suspicious.samples.concat(new_suspicious)
88
+ end
89
+ else
90
+ existing_attacker_activity.protection_rules[rule] = new_violation
43
91
  end
44
92
  end
45
93
  end
@@ -1,6 +1,8 @@
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/api/dtm.pb'
5
+ require 'contrast/api/decorators/response_type'
4
6
  require 'contrast/components/logger'
5
7
  require 'contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity'
6
8
 
@@ -9,49 +11,53 @@ module Contrast
9
11
  module Reporting
10
12
  # This is the new ApplicationDefendAttackActivity class which will include the attacks sent by the source.
11
13
  class ApplicationDefendAttackActivity
12
- attr_reader :blocked, :exploited, :ineffective, :suspicious
14
+ # @return [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
15
+ attr_accessor :blocked
16
+ # @return [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
17
+ attr_accessor :exploited
18
+ # @return [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
19
+ attr_accessor :ineffective
20
+ # @return [Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity]
21
+ attr_accessor :suspicious
13
22
 
14
23
  class << self
24
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
15
25
  def convert attack_result
16
26
  activity = new
17
- activity.attach_data attack_result
27
+ activity.attach_data(attack_result)
18
28
  activity
19
29
  end
20
30
  end
21
31
 
22
32
  def initialize
23
33
  @start_time = start_time
24
- @blocked = []
25
- @exploited = []
26
- @ineffective = []
27
- @suspicious = []
28
34
  super
29
35
  end
30
36
 
31
37
  def to_controlled_hash
32
38
  {
33
39
  startTime: @start_time,
34
- blocked: blocked.map(&:to_controlled_hash),
35
- exploited: exploited.map(&:to_controlled_hash),
36
- ineffective: ineffective.map(&:to_controlled_hash),
37
- suspicious: suspicious.map(&:to_controlled_hash)
40
+ blocked: blocked&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
41
+ exploited: exploited&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
42
+ ineffective: ineffective&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH,
43
+ suspicious: suspicious&.to_controlled_hash || Contrast::Utils::ObjectShare::EMPTY_HASH
38
44
  }
39
45
  end
40
46
 
41
- # @param attack_result [Contrast::Agent::Reporting::AttackResult]
47
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
42
48
  # @return [Contrast::Agent::Reporting::Defend::AttackSampleActivity]
43
49
  def attach_data attack_result
44
50
  attack_sample_activity =
45
51
  Contrast::Agent::Reporting::ApplicationDefendAttackSampleActivity.convert(attack_result)
46
52
  case attack_result.response
47
- when Contrast::Agent::Reporting::ResponseType::BLOCKED
48
- @blocked << attack_sample_activity
49
- when Contrast::Agent::Reporting::ResponseType::EXPLOITED
50
- @exploited << attack_sample_activity
51
- when Contrast::Agent::Reporting::ResponseType::MONITORED
52
- @ineffective << attack_sample_activity
53
- when Contrast::Agent::Reporting::ResponseType::SUSPICIOUS
54
- @suspicious << attack_sample_activity
53
+ when ::Contrast::Api::Dtm::AttackResult::ResponseType::BLOCKED
54
+ @blocked = attack_sample_activity
55
+ when ::Contrast::Api::Dtm::AttackResult::ResponseType::MONITORED
56
+ @exploited = attack_sample_activity
57
+ when ::Contrast::Api::Dtm::AttackResult::ResponseType::PROBED
58
+ @ineffective = attack_sample_activity
59
+ when ::Contrast::Api::Dtm::AttackResult::ResponseType::SUSPICIOUS
60
+ @suspicious = attack_sample_activity
55
61
  end
56
62
  end
57
63
 
@@ -1,6 +1,11 @@
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/protect/rule/cmd_injection'
5
+ require 'contrast/agent/protect/rule/deserialization'
6
+ require 'contrast/agent/protect/rule/http_method_tampering'
7
+ require 'contrast/agent/protect/rule/no_sqli'
8
+ require 'contrast/api/dtm.pb'
4
9
  require 'contrast/components/logger'
5
10
 
6
11
  module Contrast
@@ -11,10 +16,15 @@ module Contrast
11
16
  class ApplicationDefendAttackSample
12
17
  include Contrast::Agent::Reporting::InputType
13
18
 
19
+ # @return [Hash]
20
+ attr_reader :time_stamp
21
+
14
22
  class << self
15
- def convert attack_result
23
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
24
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
25
+ def convert attack_result, attack_sample
16
26
  activity = new
17
- activity.attach_data attack_result
27
+ activity.attach_data(attack_result, attack_sample)
18
28
  activity
19
29
  end
20
30
  end
@@ -28,22 +38,26 @@ module Contrast
28
38
  {
29
39
  blocked: @blocked,
30
40
  input: @input,
31
- request: @request.to_controlled_hash,
32
- stack: @stack,
33
- timeStamp: @time_stamp
41
+ details: @details,
42
+ request: @request&.to_controlled_hash,
43
+ stack: @stack&.map(&:to_controlled_hash),
44
+ timestamp: time_stamp
34
45
  }
35
46
  end
36
47
 
37
48
  # @param attack_result [Contrast::Api::Dtm::AttackResult]
38
- def attach_data attack_result
39
- rasp_rule = attack_result.samples[0]
40
- @blocked = attack_result.response == Contrast::Agent::Reporting::ResponseType::BLOCKED
41
- @input = build_input(rasp_rule)
42
- @time_stamp = build_time_stamp(rasp_rule.timestamp_ms)
49
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
50
+ def attach_data attack_result, attack_sample
51
+ @blocked = attack_result.response == ::Contrast::Api::Dtm::AttackResult::ResponseType::BLOCKED
52
+ @input = build_input(attack_sample)
53
+ @details = build_details(attack_result.rule_id, attack_sample)
54
+ @time_stamp = build_time_stamp(attack_sample.timestamp_ms)
43
55
  @request = FindingRequest.convert(Contrast::Agent::REQUEST_TRACKER.current&.request)
44
- @stack = Contrast::Utils::StackTraceUtils.build_protect_stack_array
56
+ @stack = Contrast::Utils::StackTraceUtils.build_protect_report_stack_array
45
57
  end
46
58
 
59
+ # @param start [Integer]
60
+ # @return [Hash]
47
61
  def build_time_stamp start
48
62
  {
49
63
  start: start,
@@ -51,19 +65,117 @@ module Contrast
51
65
  }
52
66
  end
53
67
 
54
- def build_input rasp_rule
55
- user_input = rasp_rule.user_input
68
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
69
+ def build_input attack_sample
70
+ user_input = attack_sample.user_input
56
71
  {
57
- details: Contrast::Api::Dtm::RaspRuleSample.to_controlled_hash(rasp_rule),
58
72
  documentPath: user_input.path,
59
- documentType: user_input.document_type,
73
+ documentType: build_document_type(user_input.document_type),
60
74
  filters: user_input.matcher_ids,
61
75
  name: user_input.key,
62
- time: rasp_rule.timestamp_ms,
63
- type: user_input.input_type,
76
+ time: attack_sample.timestamp_ms,
77
+ type: build_input_type(user_input.input_type),
64
78
  value: user_input.value
65
79
  }
66
80
  end
81
+
82
+ # Convert the document type api enum to a String constant TeamServer can understand.
83
+ #
84
+ # @param type_enum [::Contrast::Api::Dtm::HttpRequest::DocumentType]
85
+ # @return [String]
86
+ def build_document_type type_enum
87
+ case type_enum
88
+ when ::Contrast::Api::Dtm::HttpRequest::DocumentType::JSON
89
+ 'JSON'
90
+ when ::Contrast::Api::Dtm::HttpRequest::DocumentType::XML
91
+ 'XML'
92
+ else
93
+ 'NORMAL'
94
+ end
95
+ end
96
+
97
+ # Convert the input type api enum to a String constant TeamServer can understand.
98
+ #
99
+ # @param type_enum [when ::Contrast::Api::Dtm::UserInput::InputType]
100
+ # @return [String]
101
+ def build_input_type type_enum
102
+ case type_enum
103
+ when ::Contrast::Api::Dtm::UserInput::InputType::UNDEFINED_TYPE
104
+ 'UNDEFINED_TYPE'
105
+ when ::Contrast::Api::Dtm::UserInput::InputType::BODY
106
+ 'BODY'
107
+ when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_NAME
108
+ 'COOKIE_NAME'
109
+ when ::Contrast::Api::Dtm::UserInput::InputType::COOKIE_VALUE
110
+ 'COOKIE_VALUE'
111
+ when ::Contrast::Api::Dtm::UserInput::InputType::HEADER
112
+ 'HEADER'
113
+ when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_NAME
114
+ 'PARAMETER_NAME'
115
+ when ::Contrast::Api::Dtm::UserInput::InputType::PARAMETER_VALUE
116
+ 'PARAMETER_VALUE'
117
+ when ::Contrast::Api::Dtm::UserInput::InputType::QUERYSTRING
118
+ 'QUERYSTRING'
119
+ when ::Contrast::Api::Dtm::UserInput::InputType::URI
120
+ 'URI'
121
+ when ::Contrast::Api::Dtm::UserInput::InputType::SOCKET
122
+ 'SOCKET'
123
+ when ::Contrast::Api::Dtm::UserInput::InputType::JSON_VALUE
124
+ 'JSON_VALUE'
125
+ when ::Contrast::Api::Dtm::UserInput::InputType::JSON_ARRAYED_VALUE
126
+ 'JSON_ARRAYED_VALUE'
127
+ when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_CONTENT_TYPE
128
+ 'MULTIPART_CONTENT_TYPE'
129
+ when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_VALUE
130
+ 'MULTIPART_VALUE'
131
+ when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_FIELD_NAME
132
+ 'MULTIPART_FIELD_NAME'
133
+ when ::Contrast::Api::Dtm::UserInput::InputType::MULTIPART_NAME
134
+ 'MULTIPART_NAME'
135
+ when ::Contrast::Api::Dtm::UserInput::InputType::XML_VALUE
136
+ 'XML_VALUE'
137
+ when ::Contrast::Api::Dtm::UserInput::InputType::DWR_VALUE
138
+ 'DWR_VALUE'
139
+ when ::Contrast::Api::Dtm::UserInput::InputType::METHOD
140
+ 'METHOD'
141
+ when ::Contrast::Api::Dtm::UserInput::InputType::REQUEST
142
+ 'REQUEST'
143
+ when ::Contrast::Api::Dtm::UserInput::InputType::URL_PARAMETER
144
+ 'URL_PARAMETER'
145
+ else # ::Contrast::Api::Dtm::UserInput::InputType::UNKNOWN
146
+ 'UNKNOWN'
147
+ end
148
+ end
149
+
150
+ # This is a temporary method to facilitate the translation of API details to Reporting details. As we move off
151
+ # of protobuf, this responsibility should shift from this class to the individual rules, as they do today when
152
+ # they populate their details in Contrast::Api::Dtm::RaspRuleSample
153
+ #
154
+ # @param rule_id [String]
155
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
156
+ # @return [Hash] the details for this specific rule
157
+ def build_details rule_id, attack_sample
158
+ case rule_id
159
+ when Contrast::Agent::Protect::Rule::CmdInjection::NAME
160
+ Contrast::Agent::Protect::Rule::CmdInjection.extract_details(attack_sample)
161
+ when Contrast::Agent::Protect::Rule::Deserialization::NAME
162
+ Contrast::Agent::Protect::Rule::Deserialization.extract_details(attack_sample)
163
+ when Contrast::Agent::Protect::Rule::HttpMethodTampering::NAME
164
+ Contrast::Agent::Protect::Rule::HttpMethodTampering.extract_details(attack_sample)
165
+ when Contrast::Agent::Protect::Rule::NoSqli::NAME
166
+ Contrast::Agent::Protect::Rule::NoSqli.extract_details(attack_sample)
167
+ when Contrast::Agent::Protect::Rule::PathTraversal::NAME
168
+ Contrast::Agent::Protect::Rule::PathTraversal.extract_details(attack_sample)
169
+ when Contrast::Agent::Protect::Rule::Sqli::NAME
170
+ Contrast::Agent::Protect::Rule::Sqli.extract_details(attack_sample)
171
+ when Contrast::Agent::Protect::Rule::Xss::NAME
172
+ Contrast::Agent::Protect::Rule::Xss.extract_details(attack_sample)
173
+ when Contrast::Agent::Protect::Rule::Xxe::NAME
174
+ Contrast::Agent::Protect::Rule::Xxe.extract_details(attack_sample)
175
+ else # something unknown or Contrast::Agent::Protect::Rule::UnsafeFileUpload::NAME
176
+ Contrast::Utils::ObjectShare::EMPTY_HASH
177
+ end
178
+ end
67
179
  end
68
180
  end
69
181
  end
@@ -10,46 +10,45 @@ module Contrast
10
10
  # This is the new AttackerSampleActivity class which will include Sample of the given attacks in the activity
11
11
  # period.
12
12
  class ApplicationDefendAttackSampleActivity
13
+ # @return [Array<Contrast::Agent::Reporting::ApplicationDefendAttackSample>]
14
+ attr_reader :samples
15
+ # @return [Hash<Integer,Integer>] map of time from start in seconds to number of attacks in that second
16
+ attr_reader :time_map
17
+
13
18
  class << self
14
- def convert attack_samples
19
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
20
+ def convert attack_result
15
21
  activity = new
16
- activity.attach_data attack_samples
22
+ activity.attach_data(attack_result)
17
23
  activity
18
24
  end
19
25
  end
20
26
 
21
27
  def initialize
22
28
  @samples = []
23
- @start_time = (Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0)
29
+ @start_time = (Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0) / 1000
24
30
  @event_type = :application_defend_attack_sample_activity
31
+ @time_map = Hash.new { |h, k| h[k] = 0 }
25
32
  super
26
33
  end
27
34
 
28
35
  def to_controlled_hash
29
36
  {
30
- attackTimeMap: attack_time_map,
31
- samples: @samples.map(&:to_controlled_hash),
37
+ attackTimeMap: time_map,
38
+ samples: samples.map(&:to_controlled_hash),
32
39
  startTime: @start_time,
33
40
  total: 1 # there will only ever be 1 attack sample, until batching is done
34
41
  }
35
42
  end
36
43
 
37
- # @param inventory_dtm [Contrast::Api::Dtm::ApplicationUpdate]
38
- # @return [Contrast::Agent::Reporting::ApplicationInventory]
39
- # TODO: RUBY-9999 update to handle batching
40
- def attach_data attack_sample
41
- @samples << Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_sample)
42
- end
43
-
44
- def attack_time_map
45
- {
46
- second: duration,
47
- count: 1 # there will only ever be 1 attack sample, until batching is done
48
- }
49
- end
50
-
51
- def duration
52
- Contrast::Utils::Timer.now_ms - @start_time
44
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
45
+ def attach_data attack_result
46
+ attack_result.samples.each do |attack_sample|
47
+ converted = Contrast::Agent::Reporting::ApplicationDefendAttackSample.convert(attack_result, attack_sample)
48
+ samples << converted
49
+ attack_second = converted.time_stamp[:elapsed] / 1000
50
+ time_map[attack_second] += 1
51
+ end
53
52
  end
54
53
  end
55
54
  end
@@ -0,0 +1,22 @@
1
+ # Copyright (c) 2022 Contrast Security, Inc. See https://www.contrastsecurity.com/enduser-terms-0317a for more details.
2
+ # frozen_string_literal: true
3
+
4
+ module Contrast
5
+ module Agent
6
+ module Reporting
7
+ # This is the new ApplicationDefendAttackSample class which includes a samples of an attack for the given rule of
8
+ # the given result observed in the activity period.
9
+ class ApplicationDefendAttackSampleStack
10
+ # @return [String]
11
+ attr_accessor :file_name
12
+ # @return [String]
13
+ attr_accessor :method_name
14
+
15
+ # @return [Hash]
16
+ def to_controlled_hash
17
+ { fileName: @file_name, methodName: @method_name }.compact
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -10,19 +10,33 @@ module Contrast
10
10
  # This is the new AttackerActivity class which will includes the attacker information discovered during this
11
11
  # activity period.
12
12
  class ApplicationDefendAttackerActivity
13
- attr_reader :attackers
13
+ # @return [Hash<String,Contrast::Agent::Reporting::ApplicationDefendAttackActivity>] map of rule-id to violated
14
+ # samples for that rule
15
+ attr_reader :protection_rules
16
+ # @return [String, nil] the IP address of the request from which the attack originated; used to identify unique
17
+ # attackers
18
+ attr_reader :source_ip
19
+ # @return [String, nil] the X-Forwarded-For Header of the request from which the attack originated; used to
20
+ # identify unique attackers
21
+ attr_reader :source_forwarded_for
14
22
 
15
23
  class << self
24
+ # @param attack_result_dtm [Contrast::Api::Dtm::AttackResult]
25
+ # @return [Contrast::Agent::Reporting::ApplicationDefendAttackerActivity]
16
26
  def convert attack_result_dtm
17
27
  activity = new
18
- activity.attach_data attack_result_dtm
28
+ activity.attach_data(attack_result_dtm)
19
29
  activity
20
30
  end
21
31
  end
22
32
 
23
33
  def initialize
24
34
  @protection_rules = {}
25
- @request = Contrast::Agent::REQUEST_TRACKER.current.activity.http_request
35
+ req = Contrast::Agent::REQUEST_TRACKER.current.activity.http_request
36
+ if req
37
+ @source_ip = req.sender.ip
38
+ @source_forwarded_for = req.request_headers['X-Forwarded-For']
39
+ end
26
40
  @event_type = :application_defend_attacker_activity
27
41
  super
28
42
  end
@@ -31,24 +45,24 @@ module Contrast
31
45
  {
32
46
  protectionRules: process_protection_rules,
33
47
  source: {
34
- ip: @request.sender.ip,
35
- xForwardedFor: @request.request_headers['X-Forwarded-For']
48
+ ip: source_ip,
49
+ xForwardedFor: source_forwarded_for
36
50
  }
37
51
  }
38
52
  end
39
53
 
40
- # @param attack_result [Contrast::Agent::Reporting::AttackResult]
54
+ # @param attack_result [Contrast::Api::Dtm::AttackResult]
41
55
  def attach_data attack_result
42
- attack_activity = Contrast::Agent::Reporting::ApplicationDefendAttackActivity.convert(attack_result)
43
- @protection_rules[attack_result.rule_id] = attack_activity
56
+ @protection_rules[attack_result.rule_id] =
57
+ Contrast::Agent::Reporting::ApplicationDefendAttackActivity.convert(attack_result)
44
58
  end
45
59
 
46
60
  def process_protection_rules
47
- arr = []
48
- @protection_rules.each_pair do |k, v|
49
- arr << { k => v&.to_controlled_hash }
61
+ hsh = {}
62
+ @protection_rules.each_pair do |rule_id, attack_activity|
63
+ hsh[rule_id] = attack_activity.to_controlled_hash
50
64
  end
51
- arr
65
+ hsh
52
66
  end
53
67
  end
54
68
  end
@@ -20,15 +20,11 @@ module Contrast
20
20
  class << self
21
21
  def convert app_update_dtm
22
22
  app_inventory = new
23
- app_inventory.attach_data app_update_dtm
23
+ app_inventory.attach_data(app_update_dtm)
24
24
  app_inventory
25
25
  end
26
26
  end
27
27
 
28
- def file_name
29
- 'applications-inventory'
30
- end
31
-
32
28
  def initialize
33
29
  @routes = []
34
30
  @event_type = :application_inventory
@@ -37,6 +33,10 @@ module Contrast
37
33
  super
38
34
  end
39
35
 
36
+ def file_name
37
+ 'applications-inventory'
38
+ end
39
+
40
40
  def to_controlled_hash
41
41
  {
42
42
  session_id: @agent_session_id_value,
@@ -4,6 +4,7 @@
4
4
  require 'contrast/components/logger'
5
5
  require 'contrast/agent/reporting/reporting_events/application_reporting_event'
6
6
  require 'contrast/agent/reporting/reporting_events/architecture_component'
7
+ require 'contrast/utils/object_share'
7
8
 
8
9
  module Contrast
9
10
  module Agent
@@ -14,21 +15,22 @@ module Contrast
14
15
  class ApplicationInventoryActivity < Contrast::Agent::Reporting::ApplicationReportingEvent
15
16
  # return [Contrast::Agent::Reporting::ArchitectureComponent]
16
17
  attr_reader :components
17
- # @ return [String, nil] - User-Agent Header value
18
- attr_reader :browser
18
+ # @ return [Array<String>, nil] - User-Agent Header value
19
+ attr_reader :browsers
19
20
 
20
21
  class << self
21
22
  # @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
22
23
  # @return [Contrast::Agent::Reporting::ApplicationInventoryActivity]
23
24
  def convert activity_dtm
24
25
  inventory = new
25
- inventory.attach_data activity_dtm
26
+ inventory.attach_data(activity_dtm)
26
27
  inventory
27
28
  end
28
29
  end
29
30
 
30
31
  def initialize
31
32
  @event_type = :application_inventory_activity
33
+ @browsers = []
32
34
  @components = []
33
35
  super
34
36
  end
@@ -36,7 +38,7 @@ module Contrast
36
38
  def to_controlled_hash
37
39
  {
38
40
  activityDuration: duration,
39
- browser: browser,
41
+ browsers: browsers,
40
42
  components: components.map(&:to_controlled_hash)
41
43
  }
42
44
  end
@@ -46,7 +48,7 @@ module Contrast
46
48
  @components << Contrast::Agent::Reporting::ArchitectureComponent.convert(architecture)
47
49
  end
48
50
  request_headers = activity.http_request&.request_headers
49
- @browser = request_headers['USER_AGENT'] if request_headers
51
+ @browsers << request_headers['USER_AGENT'] if request_headers
50
52
  end
51
53
 
52
54
  def duration
@@ -26,23 +26,17 @@ module Contrast
26
26
  # TeamServer to process the JSON form of this message.
27
27
  #
28
28
  # @return [Hash]
29
- # @raise [ArgumentError]
30
29
  def to_controlled_hash
31
30
  app_config = ::Contrast::CONFIG.root.application
32
- hsh = {
31
+ {
33
32
  code: app_config.code,
34
33
  group: app_config.group,
35
34
  instrumentation: Contrast::Agent::Reporting::ApplicationStartupInstrumentation.new.to_controlled_hash,
36
35
  metadata: app_config.metadata,
36
+ session_id: ::Contrast::CONFIG.session_id,
37
+ session_metadata: ::Contrast::CONFIG.session_metadata,
37
38
  tags: app_config.tags
38
- }
39
- if (session_id = ::Contrast::CONFIG.session_id)
40
- hsh[:session_id] = session_id
41
- end
42
- if (session_metadata = ::Contrast::CONFIG.session_metadata)
43
- hsh[:session_metadata] = session_metadata
44
- end
45
- hsh
39
+ }.compact
46
40
  end
47
41
  end
48
42
  end
@@ -21,7 +21,7 @@ module Contrast
21
21
  # @return [Contrast::Agent::Reporting::DiscoveredRoute]
22
22
  def convert dtm
23
23
  discovered_route = new
24
- discovered_route.attach_data dtm
24
+ discovered_route.attach_data(dtm)
25
25
  discovered_route
26
26
  end
27
27
  end
@@ -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