contrast-agent 6.6.3 → 6.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.gitmodules +0 -3
  4. data/ext/cs__scope/cs__scope.c +1 -1
  5. data/lib/contrast/agent/assess/contrast_event.rb +2 -24
  6. data/lib/contrast/agent/assess/events/source_event.rb +7 -61
  7. data/lib/contrast/agent/assess/finalizers/hash.rb +11 -0
  8. data/lib/contrast/agent/assess/policy/dynamic_source_factory.rb +0 -55
  9. data/lib/contrast/agent/assess/policy/policy_node.rb +3 -3
  10. data/lib/contrast/agent/assess/policy/policy_node_utils.rb +0 -1
  11. data/lib/contrast/agent/assess/policy/propagation_node.rb +4 -4
  12. data/lib/contrast/agent/assess/policy/source_method.rb +24 -1
  13. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +7 -5
  14. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +6 -1
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +38 -119
  16. data/lib/contrast/agent/assess/policy/trigger_node.rb +3 -3
  17. data/lib/contrast/agent/assess/property/evented.rb +2 -12
  18. data/lib/contrast/agent/assess/rule/provider/hardcoded_value_rule.rb +42 -82
  19. data/lib/contrast/agent/assess/rule/response/base_rule.rb +11 -27
  20. data/lib/contrast/agent/assess/rule/response/body_rule.rb +1 -3
  21. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +77 -62
  22. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +1 -1
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +6 -1
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +5 -5
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +1 -1
  26. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +1 -1
  27. data/lib/contrast/agent/assess/tracker.rb +1 -7
  28. data/lib/contrast/agent/at_exit_hook.rb +1 -7
  29. data/lib/contrast/agent/excluder.rb +206 -0
  30. data/lib/contrast/agent/exclusion_matcher.rb +6 -0
  31. data/lib/contrast/agent/inventory/database_config.rb +18 -23
  32. data/lib/contrast/agent/middleware.rb +0 -1
  33. data/lib/contrast/agent/protect/policy/applies_command_injection_rule.rb +4 -0
  34. data/lib/contrast/agent/protect/policy/applies_sqli_rule.rb +1 -0
  35. data/lib/contrast/agent/protect/rule/base.rb +64 -24
  36. data/lib/contrast/agent/protect/rule/base_service.rb +1 -0
  37. data/lib/contrast/agent/protect/rule/cmd_injection.rb +18 -104
  38. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +129 -0
  39. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +169 -0
  40. data/lib/contrast/agent/protect/rule/deserialization.rb +7 -5
  41. data/lib/contrast/agent/protect/rule/path_traversal.rb +9 -7
  42. data/lib/contrast/agent/protect/rule/sql_sample_builder.rb +16 -14
  43. data/lib/contrast/agent/protect/rule/sqli/sqli_base_rule.rb +51 -0
  44. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +67 -0
  45. data/lib/contrast/agent/protect/rule/sqli.rb +6 -31
  46. data/lib/contrast/agent/protect/rule/xxe.rb +11 -6
  47. data/lib/contrast/agent/protect/rule.rb +3 -1
  48. data/lib/contrast/agent/reporting/attack_result/attack_result.rb +8 -0
  49. data/lib/contrast/agent/reporting/attack_result/rasp_rule_sample.rb +91 -36
  50. data/lib/contrast/agent/reporting/attack_result/user_input.rb +11 -0
  51. data/lib/contrast/agent/reporting/details/bot_blocker_details.rb +29 -0
  52. data/lib/contrast/agent/reporting/details/cmd_injection_details.rb +30 -0
  53. data/lib/contrast/agent/reporting/details/details.rb +18 -0
  54. data/lib/contrast/agent/reporting/details/http_method_tempering_details.rb +27 -0
  55. data/lib/contrast/agent/reporting/details/ip_denylist_details.rb +27 -0
  56. data/lib/contrast/agent/reporting/details/no_sqli_details.rb +36 -0
  57. data/lib/contrast/agent/reporting/details/path_traversal_details.rb +24 -0
  58. data/lib/contrast/agent/reporting/details/path_traversal_semantic_analysis_details.rb +32 -0
  59. data/lib/contrast/agent/reporting/details/protect_rule_details.rb +17 -0
  60. data/lib/contrast/agent/reporting/details/sqli_dangerous_functions.rb +22 -0
  61. data/lib/contrast/agent/reporting/details/sqli_details.rb +36 -0
  62. data/lib/contrast/agent/reporting/details/untrusted_deserialization_details.rb +27 -0
  63. data/lib/contrast/agent/reporting/details/virtual_patch_details.rb +24 -0
  64. data/lib/contrast/agent/reporting/details/xss_details.rb +33 -0
  65. data/lib/contrast/agent/reporting/details/xss_match.rb +30 -0
  66. data/lib/contrast/agent/reporting/details/xxe_details.rb +36 -0
  67. data/lib/contrast/agent/reporting/details/xxe_match.rb +25 -0
  68. data/lib/contrast/agent/reporting/details/xxe_wrapper.rb +25 -0
  69. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +1 -1
  70. data/lib/contrast/agent/reporting/masker/masker.rb +78 -65
  71. data/lib/contrast/agent/reporting/masker/masker_utils.rb +1 -30
  72. data/lib/contrast/agent/reporting/reporter.rb +1 -2
  73. data/lib/contrast/agent/reporting/reporting_events/agent_startup.rb +2 -2
  74. data/lib/contrast/agent/reporting/reporting_events/application_activity.rb +81 -15
  75. data/lib/contrast/agent/reporting/reporting_events/application_defend_activity.rb +13 -25
  76. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_activity.rb +17 -22
  77. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample.rb +46 -125
  78. data/lib/contrast/agent/reporting/reporting_events/application_defend_attack_sample_activity.rb +5 -16
  79. data/lib/contrast/agent/reporting/reporting_events/application_defend_attacker_activity.rb +10 -18
  80. data/lib/contrast/agent/reporting/reporting_events/application_inventory_activity.rb +6 -14
  81. data/lib/contrast/agent/reporting/reporting_events/application_startup.rb +1 -1
  82. data/lib/contrast/agent/reporting/reporting_events/architecture_component.rb +7 -21
  83. data/lib/contrast/agent/reporting/reporting_events/finding.rb +19 -49
  84. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +12 -9
  85. data/lib/contrast/agent/reporting/reporting_events/finding_event_signature.rb +1 -1
  86. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +23 -21
  87. data/lib/contrast/agent/reporting/reporting_events/finding_event_stack.rb +5 -18
  88. data/lib/contrast/agent/reporting/reporting_events/finding_event_taint_range.rb +1 -0
  89. data/lib/contrast/{api/decorators/trace_taint_range_tags.rb → agent/reporting/reporting_events/finding_event_taint_range_tags.rb} +7 -6
  90. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +45 -10
  91. data/lib/contrast/agent/reporting/reporting_events/library_usage_observation.rb +1 -1
  92. data/lib/contrast/agent/reporting/reporting_events/observed_route.rb +2 -2
  93. data/lib/contrast/agent/reporting/reporting_events/preflight_message.rb +10 -14
  94. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +11 -0
  95. data/lib/contrast/agent/reporting/reporting_events/route_coverage.rb +3 -1
  96. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +11 -23
  97. data/lib/contrast/agent/reporting/reporting_events/route_discovery_observation.rb +8 -26
  98. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
  99. data/lib/contrast/agent/reporting/reporting_utilities/build_preflight.rb +4 -7
  100. data/lib/contrast/agent/reporting/reporting_utilities/headers.rb +1 -1
  101. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +2 -1
  102. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +3 -3
  103. data/lib/contrast/agent/request.rb +4 -2
  104. data/lib/contrast/agent/request_context.rb +12 -15
  105. data/lib/contrast/agent/request_context_extend.rb +67 -69
  106. data/lib/contrast/agent/request_handler.rb +1 -11
  107. data/lib/contrast/agent/response.rb +0 -18
  108. data/lib/contrast/agent/service_heartbeat.rb +1 -1
  109. data/lib/contrast/agent/telemetry/events/event.rb +1 -1
  110. data/lib/contrast/agent/telemetry/events/metric_event.rb +1 -1
  111. data/lib/contrast/agent/telemetry/events/startup_metrics_event.rb +3 -3
  112. data/lib/contrast/agent/version.rb +1 -1
  113. data/lib/contrast/api/communication/messaging_queue.rb +2 -3
  114. data/lib/contrast/api/communication/socket_client.rb +4 -4
  115. data/lib/contrast/api/communication/speedracer.rb +4 -8
  116. data/lib/contrast/api/decorators/agent_startup.rb +5 -6
  117. data/lib/contrast/api/decorators/application_settings.rb +2 -1
  118. data/lib/contrast/api/decorators/application_startup.rb +6 -6
  119. data/lib/contrast/api/decorators/message.rb +0 -4
  120. data/lib/contrast/api/decorators/rasp_rule_sample.rb +0 -6
  121. data/lib/contrast/api/decorators.rb +0 -6
  122. data/lib/contrast/api/dtm.pb.rb +0 -489
  123. data/lib/contrast/components/agent.rb +16 -12
  124. data/lib/contrast/components/api.rb +10 -10
  125. data/lib/contrast/components/app_context.rb +9 -9
  126. data/lib/contrast/components/app_context_extend.rb +1 -1
  127. data/lib/contrast/components/assess.rb +92 -38
  128. data/lib/contrast/components/assess_rules.rb +36 -0
  129. data/lib/contrast/components/config.rb +54 -12
  130. data/lib/contrast/components/contrast_service.rb +8 -8
  131. data/lib/contrast/components/heap_dump.rb +1 -1
  132. data/lib/contrast/components/protect.rb +5 -5
  133. data/lib/contrast/components/ruby_component.rb +81 -0
  134. data/lib/contrast/components/sampling.rb +1 -1
  135. data/lib/contrast/components/security_logger.rb +23 -0
  136. data/lib/contrast/components/service.rb +55 -0
  137. data/lib/contrast/components/settings.rb +12 -4
  138. data/lib/contrast/config/base_configuration.rb +1 -1
  139. data/lib/contrast/config/protect_rules_configuration.rb +17 -3
  140. data/lib/contrast/config/server_configuration.rb +1 -1
  141. data/lib/contrast/config.rb +0 -6
  142. data/lib/contrast/configuration.rb +81 -17
  143. data/lib/contrast/extension/assess/exec_trigger.rb +3 -1
  144. data/lib/contrast/extension/assess/marshal.rb +3 -2
  145. data/lib/contrast/extension/assess/string.rb +0 -1
  146. data/lib/contrast/extension/extension.rb +1 -1
  147. data/lib/contrast/framework/base_support.rb +0 -5
  148. data/lib/contrast/framework/grape/support.rb +1 -23
  149. data/lib/contrast/framework/manager.rb +0 -10
  150. data/lib/contrast/framework/rails/patch/action_controller_live_buffer.rb +1 -6
  151. data/lib/contrast/framework/rails/support.rb +5 -58
  152. data/lib/contrast/framework/sinatra/support.rb +2 -21
  153. data/lib/contrast/logger/cef_log.rb +21 -3
  154. data/lib/contrast/logger/log.rb +1 -11
  155. data/lib/contrast/tasks/config.rb +4 -2
  156. data/lib/contrast/utils/assess/event_limit_utils.rb +28 -12
  157. data/lib/contrast/utils/assess/trigger_method_utils.rb +10 -18
  158. data/lib/contrast/utils/findings.rb +6 -5
  159. data/lib/contrast/utils/hash_digest.rb +9 -24
  160. data/lib/contrast/utils/hash_digest_extend.rb +6 -6
  161. data/lib/contrast/utils/invalid_configuration_util.rb +21 -58
  162. data/lib/contrast/utils/log_utils.rb +47 -17
  163. data/lib/contrast/utils/net_http_base.rb +7 -8
  164. data/lib/contrast/utils/patching/policy/patch_utils.rb +3 -2
  165. data/lib/contrast/utils/stack_trace_utils.rb +0 -25
  166. data/lib/contrast/utils/string_utils.rb +9 -0
  167. data/lib/contrast/utils/telemetry_client.rb +13 -7
  168. data/lib/contrast.rb +5 -10
  169. metadata +39 -28
  170. data/lib/contrast/agent/reporting/reporting_events/trace_event_source.rb +0 -30
  171. data/lib/contrast/agent/reporting/reporting_utilities/dtm_message.rb +0 -43
  172. data/lib/contrast/api/decorators/activity.rb +0 -33
  173. data/lib/contrast/api/decorators/architecture_component.rb +0 -36
  174. data/lib/contrast/api/decorators/finding.rb +0 -29
  175. data/lib/contrast/api/decorators/route_coverage.rb +0 -91
  176. data/lib/contrast/api/decorators/trace_event.rb +0 -120
  177. data/lib/contrast/api/decorators/trace_event_object.rb +0 -63
  178. data/lib/contrast/api/decorators/trace_event_signature.rb +0 -69
  179. data/lib/contrast/api/decorators/trace_taint_range.rb +0 -52
  180. data/lib/contrast/config/assess_configuration.rb +0 -93
  181. data/lib/contrast/config/assess_rules_configuration.rb +0 -32
  182. data/lib/contrast/config/root_configuration.rb +0 -90
  183. data/lib/contrast/config/ruby_configuration.rb +0 -81
  184. data/lib/contrast/config/service_configuration.rb +0 -49
  185. data/lib/contrast/utils/preflight_util.rb +0 -13
@@ -2,6 +2,7 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/protect/rule/base'
5
+ require 'contrast/agent/reporting/details/untrusted_deserialization_details'
5
6
  require 'contrast/components/logger'
6
7
 
7
8
  module Contrast
@@ -68,9 +69,10 @@ module Contrast
68
69
  # Per the spec, this rule applies regardless of input. Only the mode
69
70
  # of the rule and code exclusions apply at this point.
70
71
  # @return [Boolean] should the rule apply to this call.
71
- def infilter? _context
72
+ def infilter? context
72
73
  return false unless enabled?
73
74
  return false if protect_excluded_by_code?
75
+ return false if protect_excluded_by_url?(context)
74
76
 
75
77
  true
76
78
  end
@@ -116,7 +118,7 @@ module Contrast
116
118
  ia_result = build_evaluation(gadget_command)
117
119
  result = build_attack_with_match(context, ia_result, nil, gadget_command, **kwargs)
118
120
  append_to_activity(context, result)
119
- cef_logging(result, :successful_attack, gadget_command)
121
+ cef_logging(result, :successful_attack, value: gadget_command)
120
122
  raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
121
123
  end
122
124
 
@@ -135,13 +137,13 @@ module Contrast
135
137
  # to render this attack event in TeamServer.
136
138
  def build_sample context, input_analysis_result, _candidate_string, **kwargs
137
139
  sample = build_base_sample(context, input_analysis_result)
138
- sample.untrusted_deserialization = Contrast::Api::Dtm::UntrustedDeserializationDetails.new
140
+ sample.details = Contrast::Agent::Reporting::Details::UntrustedDeserializationDetails.new
139
141
 
140
142
  deserializer = Contrast::Utils::StringUtils.protobuf_safe_string(kwargs[:GADGET_TYPE])
141
- sample.untrusted_deserialization.deserializer = deserializer
143
+ sample.details.deserializer = deserializer
142
144
 
143
145
  command = !!kwargs[:COMMAND_SCOPE]
144
- sample.untrusted_deserialization.command = command
146
+ sample.details.cmd = command
145
147
 
146
148
  sample
147
149
  end
@@ -3,6 +3,8 @@
3
3
 
4
4
  require 'contrast/agent/protect/rule/base_service'
5
5
  require 'contrast/utils/stack_trace_utils'
6
+ require 'contrast/agent/reporting/details/path_traversal_details'
7
+ require 'contrast/agent/reporting/details/path_traversal_semantic_analysis_details'
6
8
 
7
9
  module Contrast
8
10
  module Agent
@@ -48,7 +50,7 @@ module Contrast
48
50
  append_to_activity(context, result)
49
51
  return unless blocked?
50
52
 
51
- cef_logging(result, :successful_attack, path)
53
+ cef_logging(result, :successful_attack)
52
54
  raise(Contrast::SecurityException.new(self,
53
55
  "Path Traversal rule triggered. Call to File.#{ method } blocked."))
54
56
  end
@@ -65,9 +67,9 @@ module Contrast
65
67
  # evaluation
66
68
  def build_sample context, input_analysis_result, path, **_kwargs
67
69
  sample = build_base_sample(context, input_analysis_result)
68
- sample.path_traversal = Contrast::Api::Dtm::PathTraversalDetails.new
70
+ sample.details = Contrast::Agent::Reporting::Details::PathTraversalDetails.new
69
71
  path ||= input_analysis_result.value
70
- sample.path_traversal.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
72
+ sample.details.path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
71
73
  sample
72
74
  end
73
75
 
@@ -76,17 +78,17 @@ module Contrast
76
78
  # Build a subclass of the RaspRuleSample if the sample matches
77
79
  def build_rep_sample context, path
78
80
  sample = build_base_sample(context, nil)
79
- sample.path_traversal_semantic = Contrast::Api::Dtm::PathTraversalSemanticAnalysisDetails.new
81
+ sample.details = Contrast::Agent::Reporting::Details::PathTraversalSemanticAnalysisDetails.new
80
82
  path = Contrast::Utils::StringUtils.protobuf_safe_string(path)
81
- sample.path_traversal_semantic.path = path
83
+ sample.details.path = path
82
84
 
83
85
  if custom_code_access_sysfile_enabled? && custom_code_accessing_system_file?(path)
84
- sample.path_traversal_semantic.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
86
+ sample.details.findings << :CUSTOM_CODE_ACCESSING_SYSTEM_FILES
85
87
  return sample
86
88
  end
87
89
 
88
90
  if common_file_exploits_enabled? && contains_known_attack_signatures?(path)
89
- sample.path_traversal_semantic.findings << :COMMON_FILE_EXPLOITS
91
+ sample.details.findings << :COMMON_FILE_EXPLOITS
90
92
  return sample
91
93
  end
92
94
 
@@ -3,6 +3,8 @@
3
3
 
4
4
  require 'contrast/agent/protect/rule/base'
5
5
  require 'contrast/agent/protect/rule/base_service'
6
+ require 'contrast/agent/reporting/details/sqli_details'
7
+ require 'contrast/agent/reporting/details/no_sqli_details'
6
8
 
7
9
  module Contrast
8
10
  module Agent
@@ -18,16 +20,16 @@ module Contrast
18
20
  # @candidate_string [String] the value of the input which may be an attack
19
21
  # @kwargs [Hash] key - value pairs of context individual rules need to build out details
20
22
  # to send to the Service to tell the story of the attack
21
- # @return [Contrast::Api::Dtm::RaspRuleSample] the sample from this attack
23
+ # @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
22
24
  module SqliSample
23
25
  def build_sample context, input_analysis_result, candidate_string, **kwargs
24
26
  sqli_sample = build_base_sample(context, input_analysis_result)
25
- sqli_sample.sqli = Contrast::Api::Dtm::SqlInjectionDetails.new
26
- sqli_sample.sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
27
- sqli_sample.sqli.start_idx = kwargs[:start_idx]
28
- sqli_sample.sqli.end_idx = kwargs[:end_idx]
29
- sqli_sample.sqli.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
30
- sqli_sample.sqli.input_boundary_idx = kwargs[:input_boundary_idx].to_i
27
+ sqli_sample.details = Contrast::Agent::Reporting::Details::SqliDetails.new
28
+ sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
29
+ sqli_sample.details.start_idx = kwargs[:start_idx]
30
+ sqli_sample.details.end_idx = kwargs[:end_idx]
31
+ sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
32
+ sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
31
33
  sqli_sample
32
34
  end
33
35
  end
@@ -41,16 +43,16 @@ module Contrast
41
43
  # @candidate_string [String] the value of the input which may be an attack
42
44
  # @kwargs [Hash] key - value pairs of context individual rules need to build out details
43
45
  # to send to the Service to tell the story of the attack
44
- # @return [Contrast::Api::Dtm::RaspRuleSample] the sample from this attack
46
+ # @return [Contrast::Agent::Reporting::RaspRuleSample] the sample from this attack
45
47
  module NoSqliSample
46
48
  def build_sample context, input_analysis_result, candidate_string, **kwargs
47
49
  no_sqli_sample = build_base_sample(context, input_analysis_result)
48
- no_sqli_sample.no_sqli = Contrast::Api::Dtm::NoSqlInjectionDetails.new
49
- no_sqli_sample.no_sqli.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
50
- no_sqli_sample.no_sqli.start_idx = kwargs[:start_idx].to_i
51
- no_sqli_sample.no_sqli.end_idx = kwargs[:end_idx].to_i
52
- no_sqli_sample.no_sqli.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
53
- no_sqli_sample.no_sqli.input_boundary_idx = kwargs[:input_boundary_idx].to_i
50
+ no_sqli_sample.details = Contrast::Agent::Reporting::Details::NoSqliDetails.new
51
+ no_sqli_sample.details.query = Contrast::Utils::StringUtils.protobuf_safe_string(candidate_string)
52
+ no_sqli_sample.details.start_idx = kwargs[:start_idx].to_i
53
+ no_sqli_sample.details.end_idx = kwargs[:end_idx].to_i
54
+ no_sqli_sample.details.boundary_overrun_idx = kwargs[:boundary_overrun_idx].to_i
55
+ no_sqli_sample.details.input_boundary_idx = kwargs[:input_boundary_idx].to_i
54
56
  no_sqli_sample
55
57
  end
56
58
  end
@@ -0,0 +1,51 @@
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 Protect
7
+ module Rule
8
+ # This is the Base Rule
9
+ class SqliBaseRule < Contrast::Agent::Protect::Rule::BaseService
10
+ include Contrast::Components::Logger::InstanceMethods
11
+ include Contrast::Agent::Reporting::InputType
12
+
13
+ BLOCK_MESSAGE = 'SQLi rule triggered. Response blocked.'
14
+
15
+ APPLICABLE_USER_INPUTS = [
16
+ BODY, COOKIE_NAME, COOKIE_VALUE, HEADER,
17
+ PARAMETER_NAME, PARAMETER_VALUE, JSON_VALUE,
18
+ MULTIPART_VALUE, MULTIPART_FIELD_NAME,
19
+ XML_VALUE, DWR_VALUE
20
+ ].cs__freeze
21
+
22
+ class << self
23
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
24
+ # @return [Hash] the details for this specific rule
25
+ def extract_details attack_sample
26
+ {
27
+ start: attack_sample.sqli.start_idx,
28
+ end: attack_sample.sqli.end_idx,
29
+ boundaryOverrunIndex: attack_sample.sqli.boundary_overrun_idx,
30
+ inputBoundaryIndex: attack_sample.sqli.input_boundary_idx,
31
+ query: attack_sample.sqli.query
32
+ }
33
+ end
34
+ end
35
+
36
+ def infilter context, database, query_string
37
+ return unless infilter?(context)
38
+
39
+ result = find_attacker(context, query_string, database: database)
40
+ return unless result
41
+
42
+ append_to_activity(context, result)
43
+
44
+ cef_logging(result, :successful_attack)
45
+ raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,67 @@
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
+ require 'contrast/agent/reporting/details/sqli_dangerous_functions'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Protect
9
+ module Rule
10
+ # This class will include the check for SQL Injection Semantic Dangerous Functions
11
+ class SqliDangerousFunctions < Contrast::Agent::Protect::Rule::SqliBaseRule
12
+ NAME = 'sql-injection-semantic-dangerous-functions'
13
+ BLOCK_MESSAGE = 'SQLi Semantic Dangerous Functions rule triggered. Response blocked.'
14
+
15
+ SQL_DANGEROUS_FUNCTIONS = %w[unhex waitfor xp_cmdshell exec].cs__freeze
16
+
17
+ def rule_name
18
+ NAME
19
+ end
20
+
21
+ def infilter context, query_string
22
+ return unless infilter?(context)
23
+ return unless violated?(query_string)
24
+
25
+ result = build_violation(context, query_string)
26
+ return unless result
27
+
28
+ append_to_activity(context, result)
29
+
30
+ cef_logging(result, :successful_attack)
31
+ raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
32
+ end
33
+
34
+ protected
35
+
36
+ def violated? attack_string
37
+ SQL_DANGEROUS_FUNCTIONS.any? { |dang_func| attack_string.downcase.include?(dang_func) }
38
+ end
39
+
40
+ def build_violation context, potential_attack_string
41
+ result = build_attack_result(context)
42
+ update_successful_attack_response(context, nil, result, potential_attack_string)
43
+ append_sample(context, nil, result, potential_attack_string)
44
+ result
45
+ end
46
+
47
+ # Override if rule can make use of the candidate string or kwargs to
48
+ # build rasp rule sample.
49
+ def build_sample context, ia_result, candidate_string, **_kwargs
50
+ sample = build_base_sample(context, nil)
51
+ sample.details = Contrast::Agent::Reporting::Details::SqliDangerousFunctions.new
52
+ sample.details.query = candidate_string
53
+
54
+ if ia_result.nil?
55
+ ui = Contrast::Agent::Reporting::UserInput.new
56
+ ui.input_type = :UNKNOWN
57
+ ui.value = candidate_string
58
+ sample.user_input = ui
59
+ end
60
+
61
+ sample
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -5,13 +5,15 @@ require 'contrast/agent/protect/rule/base_service'
5
5
  require 'contrast/agent/protect/policy/applies_sqli_rule'
6
6
  require 'contrast/agent/protect/rule/sql_sample_builder'
7
7
  require 'contrast/agent/reporting/input_analysis/input_type'
8
+ require 'contrast/agent/protect/rule/sqli/sqli_base_rule'
9
+ require 'contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions'
8
10
 
9
11
  module Contrast
10
12
  module Agent
11
13
  module Protect
12
14
  module Rule
13
15
  # The Ruby implementation of the Protect SQL Injection rule.
14
- class Sqli < Contrast::Agent::Protect::Rule::BaseService
16
+ class Sqli < Contrast::Agent::Protect::Rule::SqliBaseRule
15
17
  # Generate a sample for the SQLI injection detection rule, allowing for reporting to and rendering
16
18
  # by TeamServer
17
19
  include SqlSampleBuilder::SqliSample
@@ -22,28 +24,9 @@ module Contrast
22
24
  include Contrast::Agent::Reporting::InputType
23
25
  end
24
26
 
25
- APPLICABLE_USER_INPUTS = [
26
- BODY, COOKIE_NAME, COOKIE_VALUE, HEADER,
27
- PARAMETER_NAME, PARAMETER_VALUE, JSON_VALUE,
28
- MULTIPART_VALUE, MULTIPART_FIELD_NAME,
29
- XML_VALUE, DWR_VALUE
30
- ].cs__freeze
31
27
  NAME = 'sql-injection'
32
- BLOCK_MESSAGE = 'SQLi rule triggered. Response blocked.'
33
28
 
34
- class << self
35
- # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
36
- # @return [Hash] the details for this specific rule
37
- def extract_details attack_sample
38
- {
39
- start: attack_sample.sqli.start_idx,
40
- end: attack_sample.sqli.end_idx,
41
- boundaryOverrunIndex: attack_sample.sqli.boundary_overrun_idx,
42
- inputBoundaryIndex: attack_sample.sqli.input_boundary_idx,
43
- query: attack_sample.sqli.query
44
- }
45
- end
46
- end
29
+ SUB_RULES = [Contrast::Agent::Protect::Rule::SqliDangerousFunctions.new].cs__freeze
47
30
 
48
31
  def rule_name
49
32
  NAME
@@ -53,16 +36,8 @@ module Contrast
53
36
  BLOCK_MESSAGE
54
37
  end
55
38
 
56
- def infilter context, database, query_string
57
- return unless infilter?(context)
58
-
59
- result = find_attacker(context, query_string, database: database)
60
- return unless result
61
-
62
- append_to_activity(context, result)
63
-
64
- cef_logging(result, :successful_attack, query_string)
65
- raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE)) if blocked?
39
+ def sub_rules
40
+ SUB_RULES
66
41
  end
67
42
  end
68
43
  end
@@ -2,6 +2,9 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'contrast/agent/protect/rule/base'
5
+ require 'contrast/agent/reporting/details/xxe_details'
6
+ require 'contrast/agent/reporting/details/xxe_match'
7
+ require 'contrast/agent/reporting/details/xxe_wrapper'
5
8
  require 'contrast/utils/timer'
6
9
  require 'contrast/components/logger'
7
10
 
@@ -56,13 +59,15 @@ module Contrast
56
59
  # @raise [Contrast::SecurityException] Security exception if an XXE
57
60
  # attack is found and the rule is in block mode.
58
61
  def infilter context, framework, xml
62
+ return if protect_excluded_by_url?(context)
63
+
59
64
  result = find_attacker(context, xml, framework: framework)
60
65
  return unless result
61
66
 
62
67
  append_to_activity(context, result)
63
68
  return unless blocked?
64
69
 
65
- cef_logging(result, :successful_attack, xml)
70
+ cef_logging(result, :successful_attack, value: xml)
66
71
  raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE))
67
72
  end
68
73
 
@@ -105,7 +110,7 @@ module Contrast
105
110
  entity_wrapper = Contrast::Agent::Protect::Rule::Xxe::EntityWrapper.new(ss.matched)
106
111
  next unless entity_wrapper.external_entity?
107
112
 
108
- xxe_details ||= Contrast::Api::Dtm::XxeDetails.new
113
+ xxe_details ||= Contrast::Agent::Reporting::Details::XxeDetails.new
109
114
  xxe_details.declared_entities << build_match(ss)
110
115
  xxe_details.entities_resolved << build_wrapper(entity_wrapper)
111
116
  end
@@ -119,12 +124,12 @@ module Contrast
119
124
  def build_sample context, ia_result, _url, **kwargs
120
125
  sample = build_base_sample(context, ia_result)
121
126
  sample.user_input = build_user_input(ia_result)
122
- sample.xxe = kwargs[:details]
127
+ sample.details = kwargs[:details]
123
128
  sample
124
129
  end
125
130
 
126
131
  def build_user_input ia_result
127
- input = Contrast::Api::Dtm::UserInput.new
132
+ input = Contrast::Agent::Reporting::UserInput.new
128
133
  input.key = INPUT_NAME
129
134
  input.input_type = :UNKNOWN
130
135
  input.document_type = :XML
@@ -146,14 +151,14 @@ module Contrast
146
151
  end
147
152
 
148
153
  def build_match string_scanner
149
- match = Contrast::Api::Dtm::XxeMatch.new
154
+ match = Contrast::Agent::Reporting::Details::XxeMatch.new
150
155
  match.end_idx = string_scanner.pos.to_i
151
156
  match.start_idx = match.end_idx - string_scanner.matched_size
152
157
  match
153
158
  end
154
159
 
155
160
  def build_wrapper entity_wrapper
156
- wrapper = Contrast::Api::Dtm::XxeWrapper.new
161
+ wrapper = Contrast::Agent::Reporting::Details::XxeWrapper.new
157
162
  wrapper.system_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.system_id)
158
163
  wrapper.public_id = Contrast::Utils::StringUtils.protobuf_safe_string(entity_wrapper.public_id)
159
164
  wrapper
@@ -28,12 +28,14 @@ require 'contrast/agent/protect/rule/sqli/default_sql_scanner'
28
28
  require 'contrast/agent/protect/rule/sqli/mysql_sql_scanner'
29
29
  require 'contrast/agent/protect/rule/sqli/postgres_sql_scanner'
30
30
  require 'contrast/agent/protect/rule/sqli/sqlite_sql_scanner'
31
+ require 'contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions'
31
32
 
32
33
  # The classes required for Path Traversal
33
34
  require 'contrast/agent/protect/rule/path_traversal'
34
35
 
35
- # The classes required for Command Injection
36
+ # The classes required for Command Injection and sub-rules
36
37
  require 'contrast/agent/protect/rule/cmd_injection'
38
+ require 'contrast/agent/protect/rule/cmdi/cmdi_backdoors'
37
39
 
38
40
  # The classes required for XXE
39
41
  require 'contrast/agent/protect/rule/xxe'
@@ -57,6 +57,14 @@ module Contrast
57
57
  def tags= tags
58
58
  @_tags = tags if tags.is_a?(String)
59
59
  end
60
+
61
+ def details
62
+ @_details ||= {}
63
+ end
64
+
65
+ def details= protect_details
66
+ @_details = protect_details if protect_details.is_a?(Contrast::Agent::Reporting::Details::ProtectRuleDetails)
67
+ end
60
68
  end
61
69
  end
62
70
  end
@@ -4,48 +4,103 @@
4
4
  require 'contrast/utils/object_share'
5
5
  require 'contrast/utils/timer'
6
6
  require 'contrast/agent/reporting/attack_result/user_input'
7
+ require 'contrast/agent/reporting/input_analysis/input_type'
8
+ require 'contrast/agent/reporting/details/protect_rule_details'
7
9
 
8
10
  module Contrast
9
11
  module Agent
10
12
  module Reporting
11
13
  # This class will hold the new RaspRuleSample.
12
- # protect rules.
13
- class RaspRuleSample # rubocop:disable Lint/EmptyClass
14
- # def timestamp
15
- # @_timestamp ||= 0
16
- # end
14
+ # It is mainly used to build samples for each
15
+ # protect rule, and translate data from SP IA.
16
+ # It is not a reporting event.
17
+ class RaspRuleSample
18
+ # Any rules specific details
17
19
  #
18
- # def timestamp= timestamp_ms
19
- # @_timestamp = timestamp_ms
20
- # end
21
- #
22
- # def user_input
23
- # @_user_input ||= Contrast::Agent::Reporting::UserInput.new
24
- # end
25
- #
26
- # def user_input= input
27
- # @_user_input = input if input.is_a?(Contrast::Agent::Reporting::UserInput)
28
- # end
29
- #
30
- # def build context, ia_result
31
- # sample = self
32
- # sample.timestamp = context&.timer&.start_ms
33
- # sample.user_input = build_user_input_from_ia(ia_result)
34
- # sample.user_input.document_type = if context&.request
35
- # Contrast::Utils::StringUtils.force_utf8(context.request.document_type)
36
- # end
37
- # sample
38
- # end
39
- #
40
- # def build_user_input_from_ia ia_result
41
- # user_input = Contrast::Agent::Reporting::UserInput.new
42
- # user_input.input_type = ia_result.input_type
43
- # user_input.matcher_ids = ia_result.ids
44
- # user_input.path = ia_result.path
45
- # user_input.key = ia_result.key if ia_result.key
46
- # user_input.value = ia_result.value if ia_result.value
47
- # user_input
48
- # end
20
+ # @return [Contrast::Agent::Reporting::Details::ProtectRuleDetails, nil]
21
+ attr_accessor :details
22
+ # @return [Contrast::Agent::Reporting::Details::IpDenylistDetails, nil]
23
+ attr_accessor :ip_denylist
24
+ # @return [Contrast::Agent::Reporting::Details::VirtualPatchDetails, nil]
25
+ attr_accessor :virtual_patch
26
+
27
+ class << self
28
+ # @param context [Contrast::Agent::RequestContext]
29
+ # @param ia_result [Contrast::Api::Settings::InputAnalysisResult] the analysis of the input that was
30
+ # determined to be an attack
31
+ # @return [Contrast::Agent::Reporting::RaspRuleSample]
32
+ def build context, ia_result
33
+ sample = new
34
+ sample.time_stamp = context&.timer&.start_ms
35
+ sample.user_input = build_user_input_from_ia(ia_result)
36
+ sample.user_input.document_type = if context&.request
37
+ Contrast::Utils::StringUtils.force_utf8(context.request.document_type)
38
+ end
39
+ sample
40
+ end
41
+
42
+ # @param ia_result [Contrast::Api::Settings::InputAnalysisResult] the analysis of the input that was
43
+ # determined to be an attack
44
+ def build_user_input_from_ia ia_result
45
+ # TODO: RUBY-99999 remove once only using Agent IA
46
+ result = if ia_result.cs__is_a?(Contrast::Api::Settings::InputAnalysisResult)
47
+ transform_ia_result(ia_result)
48
+ else
49
+ # Use Agent ia_result
50
+ ia_result
51
+ end
52
+ user_input = Contrast::Agent::Reporting::UserInput.new
53
+ return user_input unless result
54
+
55
+ user_input.input_type = result.input_type
56
+ user_input.matcher_ids = result.ids
57
+ user_input.path = result.path
58
+ user_input.key = result.key if result.key
59
+ user_input.value = result.value if result.value
60
+ user_input
61
+ end
62
+
63
+ # @param [Contrast::Api::Settings::InputAnalysisResult]
64
+ # @return [Contrast::Agent::Reporting::InputAnalysisResult]
65
+ def transform_ia_result dtm_ia_result
66
+ ia_result = Contrast::Agent::Reporting::InputAnalysisResult.new
67
+ ia_result.input_type = Contrast::Agent::Reporting::InputType.to_a.find do |value|
68
+ value == dtm_ia_result.input_type.name # rubocop:disable Security/Module/Name
69
+ end
70
+ ia_result.score_level = dtm_ia_result.score_level.name # rubocop:disable Security/Module/Name
71
+ ia_result.value = dtm_ia_result.value
72
+ ia_result.key = dtm_ia_result.key
73
+ ia_result.path = dtm_ia_result.path
74
+ ia_result.rule_id = dtm_ia_result.rule_id
75
+ ia_result.attack_count = dtm_ia_result.attack_count
76
+ ia_result.ids = dtm_ia_result.ids
77
+ ia_result
78
+ end
79
+ end
80
+
81
+ def time_stamp
82
+ @_time_stamp ||= Contrast::Agent::REQUEST_TRACKER.current&.timer&.start_ms || 0
83
+ end
84
+
85
+ def time_stamp= timestamp_ms
86
+ @_time_stamp = timestamp_ms
87
+ end
88
+
89
+ def user_input
90
+ @_user_input ||= Contrast::Agent::Reporting::UserInput.new
91
+ end
92
+
93
+ def user_input= input
94
+ @_user_input = input if input.is_a?(Contrast::Agent::Reporting::UserInput)
95
+ end
96
+
97
+ def to_controlled_hash
98
+ {
99
+ timeStamp: Time.at(time_stamp).iso8601,
100
+ userInput: user_input.to_controlled_hash,
101
+ details: details&.to_controlled_hash
102
+ }
103
+ end
49
104
  end
50
105
  end
51
106
  end
@@ -81,6 +81,17 @@ module Contrast
81
81
  def matcher_ids= ids
82
82
  @_matcher_ids = ids if ids.is_a?(Array) && ids.any?(String)
83
83
  end
84
+
85
+ def to_controlled_hash
86
+ {
87
+ path: path,
88
+ key: key,
89
+ value: value,
90
+ inputType: input_type.to_s,
91
+ documentType: document_type.to_s,
92
+ matcherIds: matcher_ids&.map(&:to_s)
93
+ }
94
+ end
84
95
  end
85
96
  end
86
97
  end
@@ -0,0 +1,29 @@
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
+ require 'contrast/agent/reporting/details/protect_rule_details'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ module Details
10
+ # Bot blocker IA result details info.
11
+ class BotBlockerDetails < ProtectRuleDetails
12
+ # @return [String]
13
+ attr_accessor :bot
14
+ # User agent header value
15
+ #
16
+ # @return [String]
17
+ attr_accessor :user_agent
18
+
19
+ def to_controlled_hash
20
+ {
21
+ bot: bot,
22
+ userAgent: user_agent
23
+ }
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
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
+ require 'contrast/agent/reporting/details/protect_rule_details'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ module Details
10
+ # CMDI IA result details info.
11
+ class CmdInjectionDetails < ProtectRuleDetails
12
+ # @return [String]
13
+ attr_accessor :cmd
14
+ # @return [Integer]
15
+ attr_accessor :start_idx
16
+ # @return [Integer]
17
+ attr_accessor :end_idx
18
+
19
+ def to_controlled_hash
20
+ {
21
+ command: cmd,
22
+ startIndex: start_idx,
23
+ endIndex: end_idx
24
+ }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end