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
@@ -14,67 +14,67 @@ module Contrast
14
14
  # This module will do the Input Classification stage of Unsafe File Upload
15
15
  # rule. As a result input would be marked as DEFINITEATTACK or IGNORE.
16
16
  module UnsafeFileUploadInputClassification
17
- UNSAFE_UPLOAD_MATCH = 'unsafe-file-upload-input-tracing-v1'
18
-
19
- class << self
20
- include InputClassificationBase
21
- include Contrast::Agent::Protect::Rule::UnsafeFileUploadMatcher
22
-
23
- # Input Classification stage is done to determine if an user input is
24
- # DEFINITEATTACK or to be ignored.
25
- #
26
- # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input.
27
- # @param value [String, Array<String>] the value of the input.
28
- # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis] Holds all the results from the
29
- # agent analysis from the current
30
- # Request.
31
- # @return ia [Contrast::Agent::Reporting::InputAnalysis] with updated results.
32
- def classify input_type, value, input_analysis
33
- unless Contrast::Agent::Protect::Rule::UnsafeFileUpload::APPLICABLE_USER_INPUTS.include?(input_type)
34
- return
35
- end
36
- return unless input_analysis.request
37
-
38
- rule_id = Contrast::Agent::Protect::Rule::UnsafeFileUpload::NAME
39
- results = []
40
-
41
- Array(value).each do |val|
42
- Array(val).each do |v|
43
- results << create_new_input_result(input_analysis.request, rule_id, input_type, v)
44
- end
45
- end
46
-
47
- input_analysis.results = results
48
- input_analysis
49
- end
50
-
51
- private
52
-
53
- # This methods checks if input is tagged DEFINITEATTACK or IGNORE matches value with it's
54
- # key if needed and Creates new isntance of InputAnalysisResult.
55
- #
56
- # @param request [Contrast::Agent::Request] the current request context.
57
- # @param rule_id [String] The name of the Protect Rule.
58
- # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input.
59
- # @param value [String, Array<String>] the value of the input.
60
- #
61
- # @return res [Contrast::Agent::Reporting::InputAnalysisResult]
62
- def create_new_input_result request, rule_id, input_type, value
63
- ia_result = new_ia_result rule_id, input_type, request.path, value
64
- if unsafe_match? value
65
- ia_result.score_level = DEFINITEATTACK
66
- ia_result.ids << UNSAFE_UPLOAD_MATCH
67
- else
68
- ia_result.score_level = IGNORE
69
- end
70
- ia_result.key = if input_type == MULTIPART_FIELD_NAME
71
- Contrast::Agent::Protect::InputAnalyzer::DISPOSITION_FILENAME
72
- else
73
- Contrast::Agent::Protect::InputAnalyzer::DISPOSITION_NAME
74
- end
75
- ia_result
76
- end
77
- end
17
+ # UNSAFE_UPLOAD_MATCH = 'unsafe-file-upload-input-tracing-v1'
18
+ #
19
+ # class << self
20
+ # include InputClassificationBase
21
+ # include Contrast::Agent::Protect::Rule::UnsafeFileUploadMatcher
22
+ #
23
+ # # Input Classification stage is done to determine if an user input is
24
+ # # DEFINITEATTACK or to be ignored.
25
+ # #
26
+ # # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input.
27
+ # # @param value [String, Array<String>] the value of the input.
28
+ # # @param input_analysis [Contrast::Agent::Reporting::InputAnalysis] Holds all the results from the
29
+ # # agent analysis from the current
30
+ # # Request.
31
+ # # @return ia [Contrast::Agent::Reporting::InputAnalysis] with updated results.
32
+ # def classify input_type, value, input_analysis
33
+ # unless Contrast::Agent::Protect::Rule::UnsafeFileUpload::APPLICABLE_USER_INPUTS.include?(input_type)
34
+ # return
35
+ # end
36
+ # return unless input_analysis.request
37
+ #
38
+ # rule_id = Contrast::Agent::Protect::Rule::UnsafeFileUpload::NAME
39
+ # results = []
40
+ #
41
+ # Array(value).each do |val|
42
+ # Array(val).each do |v|
43
+ # results << create_new_input_result(input_analysis.request, rule_id, input_type, v)
44
+ # end
45
+ # end
46
+ #
47
+ # input_analysis.results = results
48
+ # input_analysis
49
+ # end
50
+ #
51
+ # private
52
+ #
53
+ # # This methods checks if input is tagged DEFINITEATTACK or IGNORE matches value with it's
54
+ # # key if needed and Creates new isntance of InputAnalysisResult.
55
+ # #
56
+ # # @param request [Contrast::Agent::Request] the current request context.
57
+ # # @param rule_id [String] The name of the Protect Rule.
58
+ # # @param input_type [Contrast::Agent::Reporting::InputType] The type of the user input.
59
+ # # @param value [String, Array<String>] the value of the input.
60
+ # #
61
+ # # @return res [Contrast::Agent::Reporting::InputAnalysisResult]
62
+ # def create_new_input_result request, rule_id, input_type, value
63
+ # ia_result = new_ia_result rule_id, input_type, request.path, value
64
+ # if unsafe_match? value
65
+ # ia_result.score_level = DEFINITEATTACK
66
+ # ia_result.ids << UNSAFE_UPLOAD_MATCH
67
+ # else
68
+ # ia_result.score_level = IGNORE
69
+ # end
70
+ # ia_result.key = if input_type == MULTIPART_FIELD_NAME
71
+ # Contrast::Agent::Protect::InputAnalyzer::DISPOSITION_FILENAME
72
+ # else
73
+ # Contrast::Agent::Protect::InputAnalyzer::DISPOSITION_NAME
74
+ # end
75
+ # ia_result
76
+ # end
77
+ # end
78
78
  end
79
79
  end
80
80
  end
@@ -8,36 +8,36 @@ module Contrast
8
8
  # Here will be made the match of the user input provided by the
9
9
  # Agent InputAnalysis.
10
10
  module UnsafeFileUploadMatcher
11
- EXPLOIT_CHARS = %w[.. ; � < > ~ *].cs__freeze
12
- # Extensions that can be executed on the server side or can be dangerous on the client side:
13
- # https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
14
- EXPLOITABLE_EXTENSIONS = %w[.php .exe .rb .jsp .pht .phtml .shtml .asa .cer .asax .swf .xap].cs__freeze
15
-
16
- # Match the user input to see if the filename
17
- # contains malicious file extension.
11
+ # EXPLOIT_CHARS = %w[.. ; � < > ~ *].cs__freeze # rubocop:disable Style/AsciiComments
12
+ # # Extensions that can be executed on the server side or can be dangerous on the client side:
13
+ # # https://owasp.org/www-community/vulnerabilities/Unrestricted_File_Upload
14
+ # EXPLOITABLE_EXTENSIONS = %w[.php .exe .rb .jsp .pht .phtml .shtml .asa .cer .asax .swf .xap].cs__freeze
18
15
  #
19
- # @param input [String] The filename
20
- # extracted from the current request
21
- # @return true | false
22
- def unsafe_match? input
23
- suspicious_chars?(input) || suspicious_extensions?(input)
24
- end
25
-
26
- private
27
-
28
- # @param input [String] The filename
29
- # extracted from the current request
30
- # @return true | false
31
- def suspicious_chars? input
32
- input.chars.any? { |c| EXPLOIT_CHARS.include? c }
33
- end
34
-
35
- # @param input [String] The filename
36
- # extracted from the current request
37
- # @return true | false
38
- def suspicious_extensions? input
39
- EXPLOITABLE_EXTENSIONS.include? File.extname(input).downcase
40
- end
16
+ # # Match the user input to see if the filename
17
+ # # contains malicious file extension.
18
+ # #
19
+ # # @param input [String] The filename
20
+ # # extracted from the current request
21
+ # # @return true | false
22
+ # def unsafe_match? input
23
+ # suspicious_chars?(input) || suspicious_extensions?(input)
24
+ # end
25
+ #
26
+ # private
27
+ #
28
+ # # @param input [String] The filename
29
+ # # extracted from the current request
30
+ # # @return true | false
31
+ # def suspicious_chars? input
32
+ # input.chars.any? { |c| EXPLOIT_CHARS.include? c }
33
+ # end
34
+ #
35
+ # # @param input [String] The filename
36
+ # # extracted from the current request
37
+ # # @return true | false
38
+ # def suspicious_extensions? input
39
+ # EXPLOITABLE_EXTENSIONS.include? File.extname(input).downcase
40
+ # end
41
41
  end
42
42
  end
43
43
  end
@@ -23,40 +23,40 @@ module Contrast
23
23
  NAME
24
24
  end
25
25
 
26
- def block_message
27
- BLOCK_MESSAGE
26
+ # This rule is solely based on input analysis, which the Service handles. When we move from the Service to the
27
+ # agent with protect library, we should re-enable these tests and that rule.
28
+ # TODO: RUBY-1574
29
+ def enabled?
30
+ super && false
28
31
  end
29
32
 
30
- def prefilter context
31
- return unless prefilter?(context)
32
-
33
- ia_results = gather_ia_results context
34
-
35
- ia_results.each do |ia_result|
36
- result = build_attack_result(context)
37
- build_attack_without_match context, ia_result, result
38
- append_to_activity context, result
39
-
40
- cef_logging result, :successful_attack
41
- raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
42
- end
43
- end
44
-
45
- private
46
-
47
- def prefilter? context
48
- return false unless context&.agent_input_analysis&.results
49
- return false unless enabled?
50
- return false if protect_excluded_by_code?
51
-
52
- true
53
- end
54
-
55
- def gather_ia_results context
56
- context.agent_input_analysis.results.select do |ia_result|
57
- ia_result.rule_id == rule_name
58
- end
59
- end
33
+ # def block_message
34
+ # BLOCK_MESSAGE
35
+ # end
36
+ #
37
+ # def prefilter context
38
+ # return unless prefilter?(context)
39
+ #
40
+ # ia_results = gather_ia_results context
41
+ #
42
+ # ia_results.each do |ia_result|
43
+ # result = build_attack_result(context)
44
+ # build_attack_without_match context, ia_result, result
45
+ # append_to_activity context, result
46
+ #
47
+ # cef_logging result, :successful_attack
48
+ # raise Contrast::SecurityException.new(self, BLOCK_MESSAGE) if blocked?
49
+ # end
50
+ # end
51
+ #
52
+ # private
53
+ #
54
+ # def prefilter? _context
55
+ # return false unless enabled?
56
+ # return false if protect_excluded_by_code?
57
+ #
58
+ # true
59
+ # end
60
60
  end
61
61
  end
62
62
  end
@@ -12,6 +12,23 @@ module Contrast
12
12
  NAME = 'reflected-xss'
13
13
  BLOCK_MESSAGE = 'XSS rule triggered. Response blocked.'
14
14
 
15
+ class << self
16
+ # @param attack_sample [Contrast::Api::Dtm::RaspRuleSample]
17
+ # @return [Hash] the details for this specific rule
18
+ def extract_details attack_sample
19
+ {
20
+ input: attack_sample.xss.input,
21
+ matches: attack_sample.xss.matches.map do |match|
22
+ {
23
+ evidenceStart: match.evidence_start_ms,
24
+ evidence: match.evidence,
25
+ offset: match.offset
26
+ }
27
+ end
28
+ }
29
+ end
30
+ end
31
+
15
32
  def rule_name
16
33
  NAME
17
34
  end
@@ -11,6 +11,20 @@ module Contrast
11
11
  class EntityWrapper
12
12
  attr_reader :system_id, :public_id
13
13
 
14
+ DTD_MARKER = '.dtd'
15
+ FILE_START = 'file:'
16
+ FTP_START = 'ftp:'
17
+ GOPHER_START = 'gopher:'
18
+ JAR_START = 'jar:'
19
+ UP_DIR_LINUX = '../'
20
+ UP_DIR_WIN = '..\\'
21
+ # <!ENTITY name SYSTEM "URI">
22
+ SYSTEM_ID_REGEXP = /<!ENTITY\s+(?<name>[a-zA-Z0-f]+)\s+SYSTEM\s+"(?<id>.*?)">/.cs__freeze
23
+ # <!ENTITY name PUBLIC "public_ID" "URI">
24
+ PUBLIC_ID_REGEXP = /<!ENTITY\s+(?<name>[a-zA-Z0-f]+)\s+PUBLIC\s+".*?"\s+"(?<id>.*?)">/.cs__freeze
25
+ # we only use this against lowercase strings, removed A-Z for speed
26
+ FILE_PATTERN_WINDOWS = /^\\*[a-z]{1,3}:.*/.cs__freeze
27
+
14
28
  def initialize entity
15
29
  @system_id = parse_system_id(entity)
16
30
  # an entity cannot be system and public
@@ -30,29 +44,16 @@ module Contrast
30
44
  @_external_entity
31
45
  end
32
46
 
33
- # <!ENTITY name SYSTEM "URI">
34
- SYSTEM_ID_REGEXP = /<!ENTITY\s+(?<name>[a-zA-Z0-f]+)\s+SYSTEM\s+"(?<id>.*?)">/.cs__freeze
35
47
  def parse_system_id entity
36
48
  match = SYSTEM_ID_REGEXP.match(entity)
37
49
  match[:id] if match
38
50
  end
39
51
 
40
- # <!ENTITY name PUBLIC "public_ID" "URI">
41
- PUBLIC_ID_REGEXP = /<!ENTITY\s+(?<name>[a-zA-Z0-f]+)\s+PUBLIC\s+".*?"\s+"(?<id>.*?)">/.cs__freeze
42
52
  def parse_public_id entity
43
53
  match = PUBLIC_ID_REGEXP.match(entity)
44
54
  match[:id] if match
45
55
  end
46
56
 
47
- DTD_MARKER = '.dtd'
48
- FILE_START = 'file:'
49
- FTP_START = 'ftp:'
50
- GOPHER_START = 'gopher:'
51
- JAR_START = 'jar:'
52
- UP_DIR_LINUX = '../'
53
- UP_DIR_WIN = '..\\'
54
- # we only use this against lowercase strings, removed A-Z for speed
55
- FILE_PATTERN_WINDOWS = /^\\*[a-z]{1,3}:.*/.cs__freeze
56
57
  def external_id? entity_id
57
58
  return false unless entity_id
58
59
 
@@ -13,11 +13,34 @@ module Contrast
13
13
  # of unsafe external entity resolution.
14
14
  class Xxe < Contrast::Agent::Protect::Rule::Base
15
15
  include Contrast::Components::Logger::InstanceMethods
16
+ INPUT_NAME = 'XML Prolog'
16
17
 
17
18
  NAME = 'xxe'
18
19
  BLOCK_MESSAGE = 'XXE rule triggered. Response blocked.'
19
20
  EXTERNAL_ENTITY_PATTERN = /<!ENTITY\s+[a-zA-Z0-f]+\s+(?:SYSTEM|PUBLIC)\s+(.*?)>/.cs__freeze
20
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
+ xml: attack_sample.xxe.xml,
28
+ declaredEntities: attack_sample.xxe.declared_entities.map do |entity|
29
+ {
30
+ start: entity.start_idx,
31
+ end: entity.end_idx
32
+ }
33
+ end,
34
+ entitiesResolved: attack_sample.xxe.entities_resolved.map do |entity|
35
+ {
36
+ systemId: entity.system_id,
37
+ publicId: entity.public_id
38
+ }
39
+ end
40
+ }
41
+ end
42
+ end
43
+
21
44
  def rule_name
22
45
  NAME
23
46
  end
@@ -39,8 +62,8 @@ module Contrast
39
62
  append_to_activity(context, result)
40
63
  return unless blocked?
41
64
 
42
- cef_logging result, :successful_attack, xml
43
- raise Contrast::SecurityException.new(self, BLOCK_MESSAGE)
65
+ cef_logging(result, :successful_attack, xml)
66
+ raise(Contrast::SecurityException.new(self, BLOCK_MESSAGE))
44
67
  end
45
68
 
46
69
  protected
@@ -100,7 +123,6 @@ module Contrast
100
123
  sample
101
124
  end
102
125
 
103
- INPUT_NAME = 'XML Prolog'
104
126
  def build_user_input ia_result
105
127
  input = Contrast::Api::Dtm::UserInput.new
106
128
  input.key = INPUT_NAME
@@ -33,7 +33,7 @@ module Contrast
33
33
 
34
34
  case reaction.operation
35
35
  when Contrast::Api::Settings::Reaction::Operation::DISABLE
36
- Contrast::Agent::DisableReaction.run reaction, level
36
+ Contrast::Agent::DisableReaction.run(reaction, level)
37
37
  when Contrast::Api::Settings::Reaction::Operation::NOOP
38
38
  # NOOP
39
39
  else
@@ -10,42 +10,42 @@ module Contrast
10
10
  module Reporting
11
11
  # This class will hold the new RaspRuleSample.
12
12
  # protect rules.
13
- class RaspRuleSample
14
- def timestamp
15
- @_timestamp ||= 0
16
- end
17
-
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
13
+ class RaspRuleSample # rubocop:disable Lint/EmptyClass
14
+ # def timestamp
15
+ # @_timestamp ||= 0
16
+ # end
17
+ #
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
49
49
  end
50
50
  end
51
51
  end
@@ -84,16 +84,16 @@ module Contrast
84
84
  params = activity.http_request.normalized_request_params
85
85
  return unless params
86
86
 
87
- mask_with_dictionary activity.results, params
87
+ mask_with_dictionary(activity.results, params)
88
88
  end
89
89
 
90
90
  def mask_request_headers activity
91
91
  if activity.http_request.parsed_request_headers
92
92
  # Used normalized request_headers
93
- mask_with_dictionary activity.results, activity.http_request.normalized_request_headers
93
+ mask_with_dictionary(activity.results, activity.http_request.normalized_request_headers)
94
94
  else
95
95
  headers = activity.http_request.request_headers
96
- mask_field_hash headers, activity.results
96
+ mask_field_hash(headers, activity.results)
97
97
  end
98
98
  end
99
99
 
@@ -105,7 +105,7 @@ module Contrast
105
105
  cookies = activity.http_request.normalized_cookies
106
106
  return unless cookies
107
107
 
108
- mask_with_dictionary activity.results, cookies
108
+ mask_with_dictionary(activity.results, cookies)
109
109
  end
110
110
 
111
111
  # Mask request query string:
@@ -117,8 +117,8 @@ module Contrast
117
117
  qs = activity.http_request.query_string
118
118
  return if qs.nil? || qs.empty?
119
119
 
120
- mask_field_hash qs, activity.results unless qs.cs__is_a?(String)
121
- mask_raw_query qs, activity.results
120
+ mask_field_hash(qs, activity.results) unless qs.cs__is_a?(String)
121
+ mask_raw_query(qs, activity.results)
122
122
  end
123
123
 
124
124
  # Mask if the value in the passed hash are matched against dictionary
@@ -134,17 +134,17 @@ module Contrast
134
134
  return if hash.nil? || hash.empty?
135
135
 
136
136
  hash.each do |key, val|
137
- match = dictionary_matcher key
137
+ match = dictionary_matcher(key)
138
138
  next unless match
139
139
 
140
140
  # The normalized values are paired.
141
141
  # key => Contrast::Api::Dtm::Pair (key, val<Values>).
142
142
  # try one level down
143
- if val.cs__respond_to? :values
144
- mask_values key, val, results
143
+ if val.cs__respond_to?(:values)
144
+ mask_values(key, val, results)
145
145
  else
146
146
  # Just assign keys.
147
- mask_hash key, val, hash, results
147
+ mask_hash(key, val, hash, results)
148
148
  end
149
149
  end
150
150
  hash
@@ -30,7 +30,7 @@ module Contrast
30
30
  hash[arr[0]] = arr[1]
31
31
  end
32
32
  # Mask the newly created hash.
33
- mask_with_dictionary results, hash
33
+ mask_with_dictionary(results, hash)
34
34
 
35
35
  # Restore to original form.
36
36
  hash.each { |k, v| masked += "#{ k }=#{ v }; " }
@@ -50,7 +50,7 @@ module Contrast
50
50
  def mask_raw_query query, results
51
51
  masked = EMPTY_STRING
52
52
  hash = URI.decode_www_form(query).to_h
53
- mask_with_dictionary results, hash
53
+ mask_with_dictionary(results, hash)
54
54
  # Restore to string form.
55
55
  hash.each { |k, v| masked += "#{ k }=#{ v }&" }
56
56
  query = masked
@@ -17,14 +17,13 @@ module Contrast
17
17
  # @return [Contrast::Agent::Reporting::ApplicationActivity]
18
18
  def convert app_activity_dtm
19
19
  app_activity = new
20
- app_activity.attach_data app_activity_dtm
20
+ app_activity.attach_data(app_activity_dtm)
21
21
  app_activity
22
22
  end
23
23
  end
24
24
 
25
25
  def initialize
26
- @defend = []
27
- @inventory = []
26
+ @event_method = :PUT
28
27
  @event_type = :application_activity
29
28
  @event_endpoint = Contrast::Agent::Reporting::Endpoints.application_activity
30
29
  super
@@ -35,17 +34,16 @@ module Contrast
35
34
  end
36
35
 
37
36
  def to_controlled_hash
38
- {
39
- lastUpdate: since_last_update,
40
- defend: @defend.map(&:to_controlled_hash),
41
- inventory: @inventory.map(&:to_controlled_hash)
42
- }
37
+ hsh = { lastUpdate: since_last_update }
38
+ hsh[:defend] = @defend&.to_controlled_hash if @defend
39
+ hsh[:inventory] = @inventory&.to_controlled_hash if @inventory
40
+ hsh
43
41
  end
44
42
 
45
43
  # @param activity_dtm [Contrast::Api::Dtm::ApplicationActivity]
46
44
  def attach_data activity_dtm
47
- @defend << Contrast::Agent::Reporting::ApplicationDefendActivity.convert(activity_dtm)
48
- @inventory << Contrast::Agent::Reporting::ApplicationInventoryActivity.convert(activity_dtm)
45
+ @defend = Contrast::Agent::Reporting::ApplicationDefendActivity.convert(activity_dtm)
46
+ @inventory = Contrast::Agent::Reporting::ApplicationInventoryActivity.convert(activity_dtm)
49
47
  end
50
48
  end
51
49
  end