contrast-agent 5.1.0 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__assess_kernel/cs__assess_kernel.c +7 -4
  3. data/ext/cs__assess_module/cs__assess_module.c +7 -7
  4. data/ext/cs__common/cs__common.c +4 -0
  5. data/ext/cs__common/cs__common.h +1 -0
  6. data/ext/cs__contrast_patch/cs__contrast_patch.c +52 -27
  7. data/ext/cs__contrast_patch/cs__contrast_patch.h +2 -0
  8. data/ext/cs__scope/cs__scope.c +747 -0
  9. data/ext/cs__scope/cs__scope.h +88 -0
  10. data/ext/cs__scope/extconf.rb +5 -0
  11. data/lib/contrast/agent/assess/contrast_event.rb +20 -13
  12. data/lib/contrast/agent/assess/contrast_object.rb +4 -1
  13. data/lib/contrast/agent/assess/policy/propagation_node.rb +2 -5
  14. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +2 -0
  15. data/lib/contrast/agent/assess/policy/trigger_method.rb +4 -1
  16. data/lib/contrast/agent/assess/rule/response/{autocomplete_rule.rb → auto_complete_rule.rb} +4 -3
  17. data/lib/contrast/agent/assess/rule/response/base_rule.rb +12 -79
  18. data/lib/contrast/agent/assess/rule/response/body_rule.rb +109 -0
  19. data/lib/contrast/agent/assess/rule/response/cache_control_header_rule.rb +157 -0
  20. data/lib/contrast/agent/assess/rule/response/click_jacking_header_rule.rb +26 -0
  21. data/lib/contrast/agent/assess/rule/response/csp_header_insecure_rule.rb +14 -15
  22. data/lib/contrast/agent/assess/rule/response/csp_header_missing_rule.rb +5 -25
  23. data/lib/contrast/agent/assess/rule/response/framework/rails_support.rb +29 -0
  24. data/lib/contrast/agent/assess/rule/response/header_rule.rb +70 -0
  25. data/lib/contrast/agent/assess/rule/response/hsts_header_rule.rb +12 -36
  26. data/lib/contrast/agent/assess/rule/response/parameters_pollution_rule.rb +2 -1
  27. data/lib/contrast/agent/assess/rule/response/x_content_type_header_rule.rb +26 -0
  28. data/lib/contrast/agent/assess/rule/response/x_xss_protection_header_rule.rb +36 -0
  29. data/lib/contrast/agent/middleware.rb +1 -0
  30. data/lib/contrast/agent/patching/policy/after_load_patcher.rb +1 -3
  31. data/lib/contrast/agent/patching/policy/patch.rb +2 -6
  32. data/lib/contrast/agent/patching/policy/patcher.rb +1 -1
  33. data/lib/contrast/agent/protect/input_analyzer/input_analyzer.rb +94 -0
  34. data/lib/contrast/agent/protect/rule/base.rb +28 -1
  35. data/lib/contrast/agent/protect/rule/base_service.rb +10 -1
  36. data/lib/contrast/agent/protect/rule/cmd_injection.rb +2 -0
  37. data/lib/contrast/agent/protect/rule/deserialization.rb +6 -0
  38. data/lib/contrast/agent/protect/rule/http_method_tampering.rb +5 -1
  39. data/lib/contrast/agent/protect/rule/no_sqli.rb +1 -0
  40. data/lib/contrast/agent/protect/rule/path_traversal.rb +1 -0
  41. data/lib/contrast/agent/protect/rule/sqli/sqli_input_classification.rb +124 -0
  42. data/lib/contrast/agent/protect/rule/sqli/sqli_worth_watching.rb +121 -0
  43. data/lib/contrast/agent/protect/rule/sqli.rb +33 -0
  44. data/lib/contrast/agent/protect/rule/xxe.rb +4 -0
  45. data/lib/contrast/agent/reporting/input_analysis/input_analysis.rb +44 -0
  46. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +115 -0
  47. data/lib/contrast/agent/reporting/input_analysis/input_type.rb +44 -0
  48. data/lib/contrast/agent/reporting/input_analysis/score_level.rb +21 -0
  49. data/lib/contrast/agent/reporting/report.rb +1 -0
  50. data/lib/contrast/agent/reporting/reporter.rb +8 -1
  51. data/lib/contrast/agent/reporting/reporting_events/finding.rb +69 -36
  52. data/lib/contrast/agent/reporting/reporting_events/finding_event.rb +88 -59
  53. data/lib/contrast/agent/reporting/reporting_events/{finding_object.rb → finding_event_object.rb} +24 -20
  54. data/lib/contrast/agent/reporting/reporting_events/finding_event_parent_object.rb +39 -0
  55. data/lib/contrast/agent/reporting/reporting_events/finding_event_property.rb +40 -0
  56. data/lib/contrast/agent/reporting/reporting_events/{finding_signature.rb → finding_event_signature.rb} +29 -24
  57. data/lib/contrast/agent/reporting/reporting_events/finding_event_source.rb +12 -8
  58. data/lib/contrast/agent/reporting/reporting_events/{finding_stack.rb → finding_event_stack.rb} +23 -19
  59. data/lib/contrast/agent/reporting/reporting_events/{finding_taint_range.rb → finding_event_taint_range.rb} +17 -15
  60. data/lib/contrast/agent/reporting/reporting_events/finding_request.rb +26 -53
  61. data/lib/contrast/agent/reporting/reporting_events/poll.rb +29 -0
  62. data/lib/contrast/agent/reporting/reporting_events/reporting_event.rb +5 -4
  63. data/lib/contrast/agent/reporting/reporting_events/route_discovery.rb +1 -0
  64. data/lib/contrast/agent/reporting/reporting_events/server_activity.rb +1 -1
  65. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  66. data/lib/contrast/agent/reporting/reporting_utilities/endpoints.rb +0 -1
  67. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -0
  68. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +28 -20
  69. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +1 -1
  70. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +13 -1
  71. data/lib/contrast/agent/request_context.rb +6 -1
  72. data/lib/contrast/agent/request_context_extend.rb +85 -21
  73. data/lib/contrast/agent/scope.rb +102 -107
  74. data/lib/contrast/agent/service_heartbeat.rb +45 -2
  75. data/lib/contrast/agent/version.rb +1 -1
  76. data/lib/contrast/api/decorators/bot_blocker.rb +37 -0
  77. data/lib/contrast/api/decorators/ip_denylist.rb +37 -0
  78. data/lib/contrast/api/decorators/rasp_rule_sample.rb +29 -0
  79. data/lib/contrast/api/decorators/user_input.rb +11 -1
  80. data/lib/contrast/api/decorators/virtual_patch.rb +34 -0
  81. data/lib/contrast/components/logger.rb +5 -0
  82. data/lib/contrast/components/protect.rb +4 -2
  83. data/lib/contrast/components/scope.rb +98 -91
  84. data/lib/contrast/config/agent_configuration.rb +58 -12
  85. data/lib/contrast/config/api_configuration.rb +100 -12
  86. data/lib/contrast/config/api_proxy_configuration.rb +55 -3
  87. data/lib/contrast/config/application_configuration.rb +114 -15
  88. data/lib/contrast/config/assess_configuration.rb +106 -12
  89. data/lib/contrast/config/assess_rules_configuration.rb +44 -3
  90. data/lib/contrast/config/base_configuration.rb +1 -0
  91. data/lib/contrast/config/certification_configuration.rb +74 -3
  92. data/lib/contrast/config/exception_configuration.rb +61 -3
  93. data/lib/contrast/config/heap_dump_configuration.rb +101 -17
  94. data/lib/contrast/config/inventory_configuration.rb +64 -3
  95. data/lib/contrast/config/logger_configuration.rb +46 -3
  96. data/lib/contrast/config/protect_rule_configuration.rb +36 -9
  97. data/lib/contrast/config/protect_rules_configuration.rb +120 -17
  98. data/lib/contrast/config/request_audit_configuration.rb +68 -3
  99. data/lib/contrast/config/ruby_configuration.rb +96 -22
  100. data/lib/contrast/config/sampling_configuration.rb +76 -10
  101. data/lib/contrast/config/server_configuration.rb +56 -11
  102. data/lib/contrast/configuration.rb +6 -3
  103. data/lib/contrast/logger/cef_log.rb +151 -0
  104. data/lib/contrast/utils/hash_digest.rb +14 -6
  105. data/lib/contrast/utils/log_utils.rb +114 -0
  106. data/lib/contrast/utils/middleware_utils.rb +6 -7
  107. data/lib/contrast/utils/net_http_base.rb +12 -9
  108. data/lib/contrast/utils/patching/policy/patch_utils.rb +0 -4
  109. data/lib/contrast.rb +4 -3
  110. data/ruby-agent.gemspec +1 -1
  111. data/service_executables/VERSION +1 -1
  112. data/service_executables/linux/contrast-service +0 -0
  113. data/service_executables/mac/contrast-service +0 -0
  114. metadata +41 -21
  115. data/lib/contrast/agent/assess/rule/response/cachecontrol_rule.rb +0 -184
  116. data/lib/contrast/agent/assess/rule/response/clickjacking_rule.rb +0 -66
  117. data/lib/contrast/agent/assess/rule/response/x_content_type_rule.rb +0 -52
  118. data/lib/contrast/agent/assess/rule/response/x_xss_protection_rule.rb +0 -53
  119. data/lib/contrast/extension/kernel.rb +0 -54
@@ -6,31 +6,36 @@ require 'contrast/utils/object_share'
6
6
  module Contrast
7
7
  module Agent
8
8
  module Reporting
9
- # This is the new FindingSignature class which will include all the needed information for the new reporting
10
- # system to relay this information in the Finding/Trace messages. These FindingObjects are used by TeamServer to
11
- # construct the vulnerability information for the assess feature. They represent the signature of the method that
12
- # was invoked in this event.
13
- #
14
- # @attr_reader arg_types [String] the types of the arguments in this event; may be different for each invocation
15
- # of the method.
16
- # @attr_reader class_name [String] the name of the class of this object or the name itself if of Module type.
17
- # @attr_reader constructor [Boolean] if the method is a constructor or not.
18
- # @attr_reader expression_type [String] unused.
19
- # @attr_reader flags [Integer] the Java flags.
20
- # @attr_reader method_name [String] the name of the method.
21
- # @attr_reader operator [String] unused.
22
- # @attr_reader return_type [String] the type of the return in this event; may be different for each invocation of
23
- # the method.
24
- # @attr_reader signature [String] the id of the Object this represents.
25
- # @attr_reader void_method [Boolean] if the method is void or not; may be different for each invocation of the
26
- # method.
27
- class FindingSignature
28
- attr_reader :arg_types, :class_name, :constructor, :expression_type, :flags, :method_name, :operator,
29
- :return_type, :signature, :void_method
9
+ # This is the new FindingEventSignature class which will include all the needed information for the new reporting
10
+ # system to relay this information in the Finding/Trace messages. These FindingEventSignatures are used by
11
+ # TeamServer to construct the method signature for the assess feature. They represent the method invoked when the
12
+ # FindingEvent was generated.
13
+ class FindingEventSignature
14
+ # @return [String] the types of the arguments in this event; may be different for each invocation of the
15
+ # method.
16
+ attr_reader :arg_types
17
+ # @return [String] the name of the class of this object or the name itself if of Module type.
18
+ attr_reader :class_name
19
+ # @return [Boolean] if the method is a constructor or not.
20
+ attr_reader :constructor
21
+ # @return [String] unused.
22
+ attr_reader :expression_type
23
+ # @return [Integer] the Java flags; static or not.
24
+ attr_reader :flags
25
+ # @return [String] the name of the method.
26
+ attr_reader :method_name
27
+ # @return [String] unused.
28
+ attr_reader :operator
29
+ # @return [String] the type of the return in this event; may be different for each invocation of the method.
30
+ attr_reader :return_type
31
+ # @return [String] unused.
32
+ attr_reader :signature
33
+ # @return [Boolean] if the method is void or not; may be different for each invocation of the method.
34
+ attr_reader :void_method
30
35
 
31
36
  class << self
32
37
  # @param event [Contrast::Agent::Assess::ContrastEvent] the event to build a signature for
33
- # @return [Contrast::Agent::Reporting::FindingSignature]
38
+ # @return [Contrast::Agent::Reporting::FindingEventSignature]
34
39
  def convert event
35
40
  report = new
36
41
  report.attach_data(event)
@@ -39,7 +44,7 @@ module Contrast
39
44
  end
40
45
 
41
46
  # Parse the data from a Contrast::Agent::Assess::ContrastEvent to attach what is required for reporting to
42
- # TeamServer to this Contrast::Agent::Reporting::FindingSignature
47
+ # TeamServer to this Contrast::Agent::Reporting::FindingEventSignature
43
48
  #
44
49
  # @param event [Contrast::Agent::Assess::ContrastEvent]
45
50
  def attach_data event
@@ -52,7 +57,7 @@ module Contrast
52
57
  @constructor = node.method_name == :new || node.method_name == :initialize
53
58
  # 8 is STATIC in Java... we have to placate them for now it has been requested that flags be removed since it
54
59
  # isn't used
55
- @flags = 8 unless policy_node.instance_method?
60
+ @flags = 8 unless node.instance_method?
56
61
  @method_name = node.method_name
57
62
  @return_type = type_name(event.ret)
58
63
  # if there's a ret, then this method isn't nil. not 100% full proof since you can return nil, but this is the
@@ -2,6 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'base64'
5
+ require 'contrast/agent/assess/contrast_event'
6
+ require 'contrast/agent/assess/events/source_event'
5
7
 
6
8
  module Contrast
7
9
  module Agent
@@ -10,16 +12,18 @@ module Contrast
10
12
  # system to relay this information in the Finding/Trace messages. These FindingEventSource are used by TeamServer
11
13
  # to construct the vulnerability information for the assess feature. They indicate the type of data that the
12
14
  # event represents.
13
- #
14
- # @attr_reader name [String] the name of the source
15
- # @attr_reader type [String] the type of the source
16
15
  class FindingEventSource
17
- attr_reader :name, :type
16
+ # @return [String] the name of the source
17
+ attr_reader :name
18
+ # @return [String] the type of the source
19
+ attr_reader :type
18
20
 
19
21
  class << self
20
- # @param event [Contrast::Agent::Assess::Events::SourceEvent] the event to pull the source off of
21
- # @return [Contrast::Agent::Reporting::FindingObject]
22
+ # @param event [Contrast::Agent::Assess::Events::ContrastEvent] the event to pull the source off of
23
+ # @return [Contrast::Agent::Reporting::FindingEventSource]
22
24
  def convert event
25
+ return unless event.cs__is_a?(Contrast::Agent::Assess::Events::SourceEvent)
26
+
23
27
  report = new
24
28
  report.attach_data(event)
25
29
  report
@@ -43,8 +47,8 @@ module Contrast
43
47
  def to_controlled_hash
44
48
  validate
45
49
  {
46
- name: name, # rubocop:disable Security/Module/Name
47
- type: type
50
+ sourceName: name, # rubocop:disable Security/Module/Name
51
+ sourceType: type
48
52
  }
49
53
  end
50
54
 
@@ -4,25 +4,29 @@
4
4
  module Contrast
5
5
  module Agent
6
6
  module Reporting
7
- # This is the new FindingStack class which will include all the needed information for the new reporting system
8
- # to relay this information in the Finding/Trace messages. These FindingStack are used by TeamServer to construct
9
- # the vulnerability information for the assess feature. They represent the callstack at the time that each
10
- # FindingEvent was generated.
11
- #
12
- # @attr_reader eval [String] unused
13
- # @attr_reader file [String] the stack frame to show in TeamServer; the value of an entry in #caller
14
- # @attr_reader line_number [String] unused
15
- # @attr_reader method [String] unused
16
- # @attr_reader signature [String] unused
17
- # @attr_reader type [String] unused
18
- class FindingStack
19
- attr_reader :eval, :file, :line_number, :method, :signature, :type
7
+ # This is the new FindingEventStack class which will include all the needed information for the new reporting
8
+ # system to relay this information in the Finding/Trace messages. These FindingEventStack are used by TeamServer
9
+ # to construct the vulnerability information for the assess feature. They represent the callstack at the time
10
+ # that each FindingEvent was generated.
11
+ class FindingEventStack
12
+ # @return [String] unused
13
+ attr_reader :eval
14
+ # @return [String] the stack frame to show in TeamServer; the value of an entry in #caller
15
+ attr_reader :file
16
+ # @return [String] unused
17
+ attr_reader :line_number
18
+ # @return [String] unused
19
+ attr_reader :method
20
+ # @return [String] unused
21
+ attr_reader :signature
22
+ # @return [String] unused
23
+ attr_reader :type
20
24
 
21
25
  AGENT_CLASS_MARKER = '/lib/contrast/'
22
26
 
23
27
  class << self
24
- # @param stack [Array<String>]
25
- # @return [Contrast::Agent::Reporting::FindingStack,nil]
28
+ # @param stack [String]
29
+ # @return [Contrast::Agent::Reporting::FindingEventStack,nil]
26
30
  def convert stack
27
31
  return unless stack
28
32
  return if stack.include?(AGENT_CLASS_MARKER)
@@ -34,9 +38,9 @@ module Contrast
34
38
  end
35
39
 
36
40
  # Parse the data from a Contrast::Agent::Assess::Tag to attach what is required for reporting to TeamServer to
37
- # this Contrast::Agent::Reporting::FindingTaintRange
41
+ # this Contrast::Agent::Reporting::FindingEventTaintRange
38
42
  #
39
- # @param stack [Array<String>]
43
+ # @param stack [String]
40
44
  def attach_data stack
41
45
  @file = stack
42
46
  end
@@ -49,8 +53,8 @@ module Contrast
49
53
  def to_controlled_hash
50
54
  validate
51
55
  {
52
- # eval: eval,
53
- file: file # ,
56
+ file: file
57
+ # eval: eval, # This is unused by the Ruby agent
54
58
  # line_number: line_number, # This is unused by the Ruby agent
55
59
  # method: method, # This is unused by the Ruby agent
56
60
  # signature: signature, # This is unused by the Ruby agent
@@ -1,22 +1,24 @@
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/assess/tag'
5
+
4
6
  module Contrast
5
7
  module Agent
6
8
  module Reporting
7
- # This is the new FindingObject class which will include all the needed information for the new reporting system
8
- # to relay this information in the Finding/Trace messages. These FindingTaintRanges are used by TeamServer to
9
- # construct the vulnerability information for the assess feature. They represent those parts of the objects that
10
- # are tracked because of a security relevant operation acting on them.
11
- #
12
- # @attr_reader range [String] the range (inclusive:exclusive), that this tag covers.
13
- # @attr_reader tag [String] the type of action this tag represents.
14
- class FindingTaintRange
15
- attr_reader :range, :tag
9
+ # This is the new FindingEventTaintRange class which will include all the needed information for the new
10
+ # reporting system to relay this information in the Finding/Trace messages. These FindingTaintRanges are used by
11
+ # TeamServer to construct the vulnerability information for the assess feature. They represent those parts of the
12
+ # objects that are tracked because of a security relevant operation acting on them.
13
+ class FindingEventTaintRange
14
+ # @return [String] the range (inclusive:exclusive), that this tag covers.
15
+ attr_reader :range
16
+ # @return [String] the type of action this tag represents.
17
+ attr_reader :tag
16
18
 
17
19
  class << self
18
20
  # @param tag [Contrast::Agent::Assess::Tag] the tag to convert
19
- # @return [Contrast::Agent::Reporting::FindingTaintRange]
21
+ # @return [Contrast::Agent::Reporting::FindingEventTaintRange]
20
22
  def convert tag
21
23
  report = new
22
24
  report.attach_data(tag)
@@ -25,7 +27,7 @@ module Contrast
25
27
  end
26
28
 
27
29
  # Parse the data from a Contrast::Agent::Assess::Tag to attach what is required for reporting to TeamServer to
28
- # this Contrast::Agent::Reporting::FindingTaintRange
30
+ # this Contrast::Agent::Reporting::FindingEventTaintRange
29
31
  #
30
32
  # @param tag [Contrast::Agent::Assess::Tag] the tag to convert
31
33
  def attach_data tag
@@ -47,10 +49,10 @@ module Contrast
47
49
  end
48
50
 
49
51
  def validate
50
- raise(ArgumentError, "#{ self } did not have a proper hash. Unable to continue.") unless hash && !hash.empty?
51
- return unless value && !value.empty?
52
-
53
- raise(ArgumentError, "#{ self } did not have a proper value. Unable to continue.")
52
+ unless range && !range.empty?
53
+ raise(ArgumentError, "#{ self } did not have a proper range. Unable to continue.")
54
+ end
55
+ raise(ArgumentError, "#{ self } did not have a proper tag. Unable to continue.") unless tag && !tag.empty?
54
56
  end
55
57
  end
56
58
  end
@@ -4,31 +4,36 @@
4
4
  module Contrast
5
5
  module Agent
6
6
  module Reporting
7
- # This is the new Finding class which will include all the needed information for the new reporting system to
8
- # relay this information in the Finding/Trace messages. These findings are used by TeamServer to construct the
9
- # vulnerability information for the assess feature. They represent those parts of the application, either through
10
- # configuration, method invocation, or dataflow, which are determined to be insecure.
11
- #
12
- # @attr_accessor events [Array<Contrast::Agent::Assess::ContrastEvent>] if a dataflow based finding, the
13
- # representation of those method calls which constitute a dangerous code path.
14
- # @attr_accessor properties [Hash<String,String>] a set of values that TeamServer can use to provide more context
15
- # to the user when rendering the finding. For some findings, a specific set of keys and values are required.
16
- # @attr_accessor request [Contrast::Agent::Request, nil] the request, if any, in which this finding occurred
17
- # @attr_accessor hash_code [String] the unique identifier of this finding.
18
- # @attr_reader rule_id [String] the name of the rule violated; must match those in TeamServer.
7
+ # This is the new FindingRequest class which will include all the needed information for the new reporting system
8
+ # to relay this information in the Finding/Trace messages. These requests are used by TeamServer to construct the
9
+ # HTTP information for the assess feature. They represent the literal request made that resulted in the
10
+ # vulnerability being triggered.
19
11
  class FindingRequest
20
- attr_reader :body, :headers, :method, :parameters, :port, :protocol, :query_string, :uri, :version
12
+ # @return [String] the body of this request
13
+ attr_reader :body
14
+ # @return [Hash<String,Array<String>>] the headers of this request
15
+ attr_reader :headers
16
+ # @return [String] the HTTP verb of this request
17
+ attr_reader :method
18
+ # @return [Hash<String,Array<String>>] the parameters of this request
19
+ attr_reader :parameters
20
+ # @return [Integer] the port to which this request connected
21
+ attr_reader :port
22
+ # @return [String] the HTTP(S) protocol of this request
23
+ attr_reader :protocol
24
+ # @return [String] the query string of this request
25
+ attr_reader :query_string
26
+ # @return [String] the url, including path and script, of this request
27
+ attr_reader :uri
28
+ # @return [String] the HTTP version of this request
29
+ attr_reader :version
21
30
 
22
31
  class << self
23
- # @param request [Contrast::Api::Dtm::HttpRequest,Contrast::Agent::Request]
32
+ # @param request [Contrast::Agent::Request]
24
33
  # @return [Contrast::Agent::Reporting::FindingRequest]
25
34
  def convert request
26
35
  report = new
27
- if request.cs__is_a?(Contrast::Agent::Request)
28
- report.attach_data(request)
29
- else
30
- report.attach_dtm_data(request)
31
- end
36
+ report.attach_data(request)
32
37
  report
33
38
  end
34
39
  end
@@ -41,8 +46,6 @@ module Contrast
41
46
  @body = request.body
42
47
  @headers = {}
43
48
  request.headers.each_pair do |key, value|
44
- next unless key && value
45
-
46
49
  # We need to change from the uppercase _ format to capitalized - format.
47
50
  header = key.split('_')
48
51
  header.each(&:capitalize!)
@@ -51,9 +54,7 @@ module Contrast
51
54
  end
52
55
  @method = request.request_method
53
56
  @parameters = {}
54
- request.parameters.each_pair do |key, value|
55
- @parameters[key] = value
56
- end
57
+ request.parameters.each_pair { |key, value| @parameters[key] = Array(value) }
57
58
  @port = request.port || 0
58
59
  @protocol = request.scheme
59
60
  @query_string = request.query_string
@@ -61,34 +62,6 @@ module Contrast
61
62
  @version = request.version
62
63
  end
63
64
 
64
- # Parse the data from a Contrast::Api::Dtm::HttpRequest to attach what is required for reporting to TeamServer
65
- # to this Contrast::Agent::Reporting::FindingRequest
66
- #
67
- # @param request_dtm [Contrast::Api::Dtm::HttpRequest]
68
- def attach_dtm_data request_dtm
69
- @body = request_dtm.request_body_binary
70
- @headers = {}
71
- request_dtm.request_headers.each_pair do |key, value|
72
- next unless key && value
73
-
74
- # We need to change from the uppercase _ format to capitalized - format.
75
- header = key.split('_')
76
- header.each(&:capitalize!)
77
- header = header.join('-')
78
- headers[header] = value.split
79
- end
80
- @method = request_dtm.method # rubocop:disable Security/Object/Method
81
- @parameters = {}
82
- request_dtm.normalized_request_params.each_value do |pair|
83
- @parameters[pair.key] = pair.values
84
- end
85
- @port = nil
86
- @protocol = request_dtm.protocol
87
- @query_string = request_dtm.query_string
88
- @uri = request_dtm.uri
89
- @version = request_dtm.version
90
- end
91
-
92
65
  # Convert the instance variables on the class, and other information, into the identifiers required for
93
66
  # TeamServer to process the JSON form of this message.
94
67
  #
@@ -110,7 +83,7 @@ module Contrast
110
83
  end
111
84
 
112
85
  def validate
113
- unless cs__method && !cs__method.empty?
86
+ unless method && !method.empty? # rubocop:disable Security/Object/Method
114
87
  raise(ArgumentError, "#{ self } did not have a proper method. Unable to continue.")
115
88
  end
116
89
  raise(ArgumentError, "#{ self } did not have a proper uri. Unable to continue.") unless uri && !uri.empty?
@@ -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/reporting_events/reporting_event'
5
+
6
+ module Contrast
7
+ module Agent
8
+ module Reporting
9
+ # This is the new Poll class for the Heartbeat Service.
10
+ class Poll < Contrast::Agent::Reporting::ReportingEvent
11
+ def initialize
12
+ @event_type = :heartbeat
13
+ @event_endpoint = Contrast::Agent::Reporting::Endpoints.heartbeat
14
+ super
15
+ end
16
+
17
+ # This is commented out as I am not aware if we're supposed to send some information and/or parse it to hash
18
+ # In https://github.com/Contrast-Security-Inc/contrast-service/blob/next/reporting/tsreporter.go
19
+ #
20
+ # Convert the instance variables on the class, and other information, into the identifiers required for
21
+ # TeamServer to process the JSON form of this message.
22
+ #
23
+ # @return [Hash]
24
+ # @raise [ArgumentError]
25
+ # def to_controlled_hash; end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -12,13 +12,14 @@ module Contrast
12
12
  # reporting system.
13
13
  #
14
14
  # @abstract
15
- # @attr_reader routes [Array]
16
- # @attr_reader event_method [Symbol] the HTTP method to use to send this event
17
15
  class ReportingEvent
18
- attr_reader :event_endpoint, :event_method
16
+ # @return [String] the endpoint, with host, to which this event should be sent
17
+ attr_reader :event_endpoint
18
+ # @return event_method [Symbol] the HTTP method to use to send this event
19
+ attr_reader :event_method
19
20
 
20
21
  def initialize
21
- @agent_session_id_value = 0 # TODO: RUBY-99999 Contrast::APP_CONTEXT.session_id once we have app start
22
+ @agent_session_id_value = '0' # TODO: RUBY-1514 Contrast::APP_CONTEXT.session_id once we have app start
22
23
  @event_endpoint ||= nil
23
24
  @event_method ||= :POST
24
25
  end
@@ -49,6 +49,7 @@ module Contrast
49
49
  def to_controlled_hash
50
50
  validate
51
51
  {
52
+ count: 0, # we have this to make TS happy
52
53
  observations: @observations.map(&:to_controlled_hash),
53
54
  signature: @signature
54
55
  }
@@ -23,7 +23,7 @@ module Contrast
23
23
 
24
24
  def initialize
25
25
  @event_method = :PUT
26
- @event_endpoint = "#{ Contrast::API.api_url }/api/ng/activity/servers"
26
+ @event_endpoint = "#{ Contrast::API.api_url }/api/ng/activity/server"
27
27
  super
28
28
  end
29
29
 
@@ -84,13 +84,13 @@ module Contrast
84
84
  # Here we will generate the directories for the requests and responses
85
85
  def generate_paths
86
86
  message_directories = File.expand_path(path_to_audits)
87
- FileUtils.mkdir_p(message_directories) unless Dir.exist?(message_directories)
87
+ make_directory(message_directories) unless Dir.exist?(message_directories)
88
88
 
89
89
  requests_destination = File.expand_path(File.join(message_directories, '/requests'))
90
90
  responses_destination = File.expand_path(File.join(message_directories, '/responses'))
91
91
 
92
- Dir.mkdir(requests_destination) if enabled_for_requests? && !Dir.exist?(requests_destination)
93
- Dir.mkdir(responses_destination) if enabled_for_responses? && !Dir.exist?(responses_destination)
92
+ make_directory(requests_destination) if enabled_for_requests? && !Dir.exist?(requests_destination)
93
+ make_directory(responses_destination) if enabled_for_responses? && !Dir.exist?(responses_destination)
94
94
 
95
95
  @path_for_requests ||= requests_destination if enabled_for_requests?
96
96
  @path_for_responses ||= responses_destination if enabled_for_responses?
@@ -98,6 +98,13 @@ module Contrast
98
98
  logger.warn('Generating the paths failed with: ', e: e)
99
99
  end
100
100
 
101
+ # Make the directory provided, including any required intermediary directories.
102
+ # We do this here in order to make allow us to easily override directory
103
+ # creation while testing.
104
+ def make_directory directory
105
+ FileUtils.mkdir_p(directory)
106
+ end
107
+
101
108
  # Retrieves the configuration value if the request audit is enabled
102
109
  # @return [Boolean]
103
110
  def enabled?
@@ -59,7 +59,6 @@ module Contrast
59
59
 
60
60
  # @return [String,nil]
61
61
  def observed_route
62
- # "/v1.0/routes/localhost///rails/ruby/observed"
63
62
  with_rescue do
64
63
  "#{ route_endpoint }/observed".cs__freeze
65
64
  end
@@ -57,6 +57,7 @@ module Contrast
57
57
  # @return response [Net::HTTP::Response, nil] response from TS if no response
58
58
  def send_event event, connection, send_immediately = false
59
59
  return unless Contrast::Agent::Reporter.enabled?
60
+ return unless connection
60
61
 
61
62
  response = send_events(event, connection)
62
63
  log_send_event event if send_immediately
@@ -15,8 +15,35 @@ module Contrast
15
15
  include Contrast::Components::Scope::InstanceMethods
16
16
  include Contrast::Agent::Reporting::Endpoints
17
17
 
18
+ # @param event [Contrast::Agent::Reporting::Preflight] the preflight we handle here
19
+ # @param response [Net::HTTP::Response,nil] The response we handle and read from
20
+ # @param connection [Net::HTTP] open connection
21
+ def handle_response event, response, connection
22
+ return unless event && response && connection
23
+ return unless event.cs__is_a?(Contrast::Agent::Reporting::Preflight)
24
+
25
+ preflight_message = event.messages[0]
26
+ # for handling multiple findings
27
+ # we'll only extract the indexes without *
28
+ # findings_to_return = response.body.split(',').delete_if { |el| el.include?('*') }
29
+ # after that we'll do some magic and return them the same way we do for corresponding_finding
30
+ corresponding_finding = Contrast::Agent::Reporting::ReportingStorage.delete(preflight_message.hash_code)
31
+ return unless corresponding_finding
32
+
33
+ audit&.audit_event(corresponding_finding, response) if ::Contrast::API.request_audit_enable?
34
+ send_event(corresponding_finding, connection, true)
35
+ nil
36
+ rescue StandardError => e
37
+ logger.error('Unable to handle response', e)
38
+ end
39
+
18
40
  private
19
41
 
42
+ # TODO: RUBY-1466 find better home for this?
43
+ def audit
44
+ @_audit ||= Contrast::Agent::Reporting::Audit.new
45
+ end
46
+
20
47
  # This method will build headers of the request required for TS communication
21
48
  #
22
49
  # @param request [Net::HTTPRequest]
@@ -67,7 +94,7 @@ module Contrast
67
94
  # @param response [Net::HTTP::Response]
68
95
  # @return response [Net::HTTP::Response]
69
96
  def process_response response
70
- response_handler.process response
97
+ response_handler.process(response)
71
98
  logger.debug('Successfully sent startup messages to service.')
72
99
  status.success!
73
100
  response
@@ -129,25 +156,6 @@ module Contrast
129
156
  end
130
157
 
131
158
  # Eventually here we'll handle more response types and etc
132
-
133
- # @param event [Contrast::Agent::Reporting::Preflight] the preflight we handle here
134
- # @param response [Net::HTTP::Response,nil] The response we handle and read from
135
- # @param connection [Net::HTTP] open connection
136
- def handle_response event, response, connection
137
- return unless event || response || connection
138
- return unless event.cs__is_a?(Contrast::Agent::Reporting::Preflight)
139
-
140
- preflight_message = event.messages[0]
141
- # for handling multiple findings
142
- # we'll only extract the indexes without *
143
- # findings_to_return = response.body.split(',').delete_if { |el| el.include?('*') }
144
- # after that we'll do some magic and return them the same way we do for corresponding_finding
145
- corresponding_finding = Contrast::Agent::Reporting::ReportingStorage.delete(preflight_message.hash_code)
146
- return unless corresponding_finding
147
-
148
- send_event corresponding_finding, connection, true
149
- nil
150
- end
151
159
  end
152
160
  end
153
161
  end
@@ -37,7 +37,7 @@ module Contrast
37
37
  assess_on: ::Contrast::ASSESS.enabled?)
38
38
  response
39
39
  rescue StandardError => e
40
- logger.error(e.message)
40
+ logger.error('Unable to process response from TeamServer', e)
41
41
  nil
42
42
  end
43
43
 
@@ -54,7 +54,7 @@ module Contrast
54
54
  #
55
55
  # @param response [Contrast::Agent::Reporting::Response]
56
56
  def update_reaction response
57
- return unless response.application_settings.reactions.any?
57
+ return unless response.application_settings.reactions&.any?
58
58
 
59
59
  response.application_settings.reactions.each do |reaction|
60
60
  # The enums are all uppercase, we need to downcase them before attempting to log.
@@ -134,6 +134,8 @@ module Contrast
134
134
  # @return res [Contrast::Agent::Reporting::Response]
135
135
  def extract_assess response_data, res
136
136
  assessments = response_data[:settings][:assessment]
137
+ return unless assessments
138
+
137
139
  res.application_settings.assess.disabled_rules = assessments[:disabledRules]
138
140
  res.application_settings.assess.session_id = assessments[:session_id]
139
141
  end
@@ -142,6 +144,8 @@ module Contrast
142
144
  # @return res [Contrast::Agent::Reporting::Response]
143
145
  def extract_protect response_data, res
144
146
  protect = response_data[:settings][:defend]
147
+ return unless protect
148
+
145
149
  res.application_settings.protect.protection_rules = protect[:protectionRules]
146
150
  res.application_settings.protect.virtual_patches = protect[:virtualPatches]
147
151
  end
@@ -150,6 +154,8 @@ module Contrast
150
154
  # @return res [Contrast::Agent::Reporting::Response]
151
155
  def extract_exclusions response_data, res
152
156
  exclusions = response_data[:settings][:exceptions]
157
+ return unless exclusions
158
+
153
159
  res.application_settings.exclusions.code_exclusions = exclusions[:codeExceptions]
154
160
  res.application_settings.exclusions.input_exclusions = exclusions[:inputExceptions]
155
161
  res.application_settings.exclusions.url_exclusions = exclusions[:urlExceptions]
@@ -165,6 +171,8 @@ module Contrast
165
171
  # @return res [Contrast::Agent::Reporting::Response]
166
172
  def extract_assess_server_features response_data, res
167
173
  assess = response_data[:assessment]
174
+ return unless assess
175
+
168
176
  res.server_features.assess.enabled = assess[:enabled]
169
177
  res.server_features.assess.disabled_rules = assess[:disabledRules]
170
178
  res.server_features.assess.sampling = assess[:sampling]
@@ -176,6 +184,8 @@ module Contrast
176
184
  # @return res [Contrast::Agent::Reporting::Response]
177
185
  def extract_protect_server_features response_data, res
178
186
  protect = response_data[:defend]
187
+ return unless protect
188
+
179
189
  res.server_features.protect.enabled = protect[:enabled]
180
190
  res.server_features.protect.bot_blocker = protect[:bot_blocker]
181
191
  res.server_features.protect.syslog = protect[:syslog]
@@ -185,6 +195,8 @@ module Contrast
185
195
  # @return res [Contrast::Agent::Reporting::Response]
186
196
  def extract_protect_lists response_data, res
187
197
  protect = response_data[:defend]
198
+ return unless protect
199
+
188
200
  res.server_features.protect.ip_allowlist = protect[:ipAllowlist]
189
201
  res.server_features.protect.ip_denylist = protect[:ipDenyList]
190
202
  res.server_features.protect.log_enchancers = protect[:logEnhancers]