contrast-agent 7.5.0 → 7.6.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/ext/cs__common/cs__common.c +5 -5
  3. data/ext/cs__contrast_patch/cs__contrast_patch.c +2 -1
  4. data/ext/cs__scope/cs__scope.c +6 -5
  5. data/lib/contrast/agent/assess/events/event_data.rb +11 -2
  6. data/lib/contrast/agent/assess/finalizers/freeze.rb +1 -0
  7. data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
  8. data/lib/contrast/agent/assess/policy/patcher.rb +2 -0
  9. data/lib/contrast/agent/assess/policy/policy.rb +4 -0
  10. data/lib/contrast/agent/assess/policy/policy_node.rb +29 -7
  11. data/lib/contrast/agent/assess/policy/preshift.rb +34 -1
  12. data/lib/contrast/agent/assess/policy/propagation_method.rb +16 -1
  13. data/lib/contrast/agent/assess/policy/propagation_node.rb +40 -1
  14. data/lib/contrast/agent/assess/policy/propagator/append.rb +5 -0
  15. data/lib/contrast/agent/assess/policy/propagator/base.rb +10 -0
  16. data/lib/contrast/agent/assess/policy/propagator/buffer.rb +6 -0
  17. data/lib/contrast/agent/assess/policy/propagator/center.rb +14 -0
  18. data/lib/contrast/agent/assess/policy/propagator/custom.rb +6 -0
  19. data/lib/contrast/agent/assess/policy/propagator/database_write.rb +14 -0
  20. data/lib/contrast/agent/assess/policy/propagator/insert.rb +6 -0
  21. data/lib/contrast/agent/assess/policy/propagator/match_data.rb +38 -0
  22. data/lib/contrast/agent/assess/policy/propagator/next.rb +6 -0
  23. data/lib/contrast/agent/assess/policy/propagator/prepend.rb +5 -0
  24. data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -0
  25. data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -0
  26. data/lib/contrast/agent/assess/policy/propagator/reverse.rb +5 -0
  27. data/lib/contrast/agent/assess/policy/propagator/select.rb +30 -0
  28. data/lib/contrast/agent/assess/policy/propagator/splat.rb +10 -0
  29. data/lib/contrast/agent/assess/policy/source_node.rb +5 -1
  30. data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +4 -0
  31. data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -0
  32. data/lib/contrast/agent/assess/policy/trigger/xpath.rb +19 -0
  33. data/lib/contrast/agent/assess/policy/trigger_method.rb +8 -1
  34. data/lib/contrast/agent/assess/policy/trigger_node.rb +11 -1
  35. data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +4 -0
  36. data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +6 -0
  37. data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +6 -0
  38. data/lib/contrast/agent/assess/rule/response/base_rule.rb +2 -1
  39. data/lib/contrast/agent/hooks/at_exit_hook.rb +1 -0
  40. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
  41. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +35 -7
  42. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -2
  43. data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +1 -6
  44. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -3
  45. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +22 -12
  46. data/lib/contrast/agent/request/request.rb +1 -1
  47. data/lib/contrast/agent/request/request_handler.rb +1 -0
  48. data/lib/contrast/agent/version.rb +1 -1
  49. data/lib/contrast/configuration.rb +1 -1
  50. data/lib/contrast/utils/hash_digest.rb +0 -14
  51. data/lib/contrast/utils/hash_digest_extend.rb +16 -5
  52. data/lib/contrast/utils/json.rb +1 -1
  53. data/lib/contrast/utils/middleware_utils.rb +9 -0
  54. data/lib/contrast/utils/routes_sent.rb +3 -2
  55. data/lib/contrast.rb +2 -2
  56. data/resources/assess/policy.json +50 -1
  57. data/ruby-agent.gemspec +13 -13
  58. metadata +24 -23
@@ -15,6 +15,13 @@ module Contrast
15
15
  # MatchData#match methods, since the last was introduced after
16
16
  # Ruby 3.1.0, but shares similar functionality, except it does not
17
17
  # support ranges.
18
+ #
19
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
20
+ # propagation action required by this method.
21
+ # @param preshift [Object] pre call state of the things.
22
+ # @param ret [Object] the return value of the method.
23
+ # @param _block [Proc] the block passed to the method.
24
+ # @return [Object] the return value of the method.
18
25
  def square_bracket_tagger propagation_node, preshift, ret, _block
19
26
  case ret
20
27
  when Array
@@ -36,6 +43,14 @@ module Contrast
36
43
  ret
37
44
  end
38
45
 
46
+ # Captures is a method that returns an array of MatchData objects.
47
+ #
48
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
49
+ # propagation action required by this method.
50
+ # @param preshift [Object] pre call state of the things.
51
+ # @param ret [Object] the return value of the method.
52
+ # @param _block [Proc] the block passed to the method.
53
+ # @return [Object] the return value of the method.
39
54
  def captures_tagger propagation_node, preshift, ret, _block
40
55
  return unless ret
41
56
 
@@ -52,6 +67,12 @@ module Contrast
52
67
  ret
53
68
  end
54
69
 
70
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
71
+ # propagation action required by this method.
72
+ # @param preshift [Object] pre call state of the things.
73
+ # @param ret [Object] the return value of the method.
74
+ # @param _block [Proc] the block passed to the method.
75
+ # @return [Object] the return value of the method.
55
76
  def to_a_tagger propagation_node, preshift, ret, _block
56
77
  idx = 0
57
78
  while idx < ret.length
@@ -65,6 +86,12 @@ module Contrast
65
86
  ret
66
87
  end
67
88
 
89
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
90
+ # propagation action required by this method.
91
+ # @param preshift [Object] pre call state of the things.
92
+ # @param ret [Object] the return value of the method.
93
+ # @param _block [Proc] the block passed to the method.
94
+ # @return [Object] the return value of the method.
68
95
  def values_at_tagger propagation_node, preshift, ret, _block
69
96
  idx = 0
70
97
  while idx < ret.length
@@ -81,6 +108,9 @@ module Contrast
81
108
 
82
109
  private
83
110
 
111
+ # @param preshift [Object] pre call state of the things.
112
+ # @param index [Integer] the index of the argument to retrieve.
113
+ # @return [Integer] the index of the argument to retrieve.
84
114
  def target_matchdata_index preshift, index
85
115
  if preshift.args[0].is_a?(Range)
86
116
  arg_range = preshift.args[0]
@@ -90,6 +120,14 @@ module Contrast
90
120
  end
91
121
  end
92
122
 
123
+ # Handles the propagation of a single argument.
124
+ #
125
+ # @param argument_index [Integer] the index of the argument to retrieve.
126
+ # @param preshift [Object] pre call state of the things.
127
+ # @param return_value [Object] the return value of the method.
128
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
129
+ # propagation action required by this method.
130
+ # @return [Object] the return value of the method.
93
131
  def square_bracket_single argument_index, preshift, return_value, propagation_node
94
132
  original_start_index = preshift.object.begin(argument_index)
95
133
  original_end_index = preshift.object.end(argument_index)
@@ -14,6 +14,12 @@ module Contrast
14
14
  class << self
15
15
  # String has some silly methods like next. Basically, this flips a
16
16
  # character in a predictable manner
17
+ #
18
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
19
+ # propagation action required by this method.
20
+ # @param preshift [Object] pre call state of the things.
21
+ # @param target [Object] the object to which the source is being appended
22
+ # @return [Object] the target with the tags applied
17
23
  def propagate propagation_node, preshift, target
18
24
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
19
25
 
@@ -13,6 +13,11 @@ module Contrast
13
13
  class << self
14
14
  # For the source, prepend its tags to the target. It's basically the
15
15
  # opposite of append. :-P
16
+ #
17
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
18
+ # propagation action required by this method.
19
+ # @param preshift [Object] pre call state of the things.
20
+ # @param target [Object] the object to which the source is being appended
16
21
  def propagate propagation_node, preshift, target
17
22
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
18
23
 
@@ -37,6 +37,10 @@ module Contrast
37
37
  handle_removal(propagation_node, source, target)
38
38
  end
39
39
 
40
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
41
+ # propagation action required by this method.
42
+ # @param source [Object] the object to which the source is being appended
43
+ # @param target [Object] the object to which the source is being appended
40
44
  def handle_removal propagation_node, source, target
41
45
  return unless source
42
46
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
@@ -13,6 +13,11 @@ module Contrast
13
13
  class << self
14
14
  # Replace means we're replacing the target w/ the source. Anything
15
15
  # on the source should be passed to the target.
16
+ #
17
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
18
+ # propagation action required by this method.
19
+ # @param preshift [Object] pre call state of the things.
20
+ # @param target [Object] the object to which the source is being appended
16
21
  def propagate propagation_node, preshift, target
17
22
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
18
23
 
@@ -12,6 +12,11 @@ module Contrast
12
12
  # overlapping tags.
13
13
  class Reverse < Contrast::Agent::Assess::Policy::Propagator::Base
14
14
  class << self
15
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
16
+ # propagation action required by this method.
17
+ # @param preshift [Object] pre call state of the things.
18
+ # @param target [Object] the object to which the source is being appended
19
+ # @return [Object] the target with the tags applied
15
20
  def propagate propagation_node, preshift, target
16
21
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
17
22
 
@@ -13,6 +13,10 @@ module Contrast
13
13
  # a 'get it right' state soon.
14
14
  class Select
15
15
  class << self
16
+ # @param patcher [Contrast::Agent::Assess::Patcher] the patcher
17
+ # @param preshift [Object] pre call state of the things.
18
+ # @param ret [Object] the return value of the method.
19
+ # @param _block [Proc] the block passed to the method.
16
20
  def select_tagger patcher, preshift, ret, _block
17
21
  source = preshift.object
18
22
  args = preshift.args
@@ -42,6 +46,12 @@ module Contrast
42
46
 
43
47
  private
44
48
 
49
+ # Handles the integer case for select.
50
+ #
51
+ # @param args [Array] the arguments passed to the method
52
+ # @param arg [Object] the first argument passed to the method
53
+ # @param source [String] the source string
54
+ # @return [Range, nil] the range to select from the source string
45
55
  def handle_integer args, arg, source
46
56
  length = args[1] || 1
47
57
  # (void) negative range
@@ -49,11 +59,22 @@ module Contrast
49
59
  arg...(arg + length)
50
60
  end
51
61
 
62
+ # Handles the string case for select.
63
+ #
64
+ # @param arg [String] the first argument passed to the method
65
+ # @param source [String] the source string
66
+ # @return [Range, nil] the range to select from the source string
52
67
  def handle_string arg, source
53
68
  idx = source.index(arg)
54
69
  idx...(idx + arg.length)
55
70
  end
56
71
 
72
+ # Handles the regexp case for select.
73
+ #
74
+ # @param args [Array] the arguments passed to the method
75
+ # @param arg [Regexp] the first argument passed to the method
76
+ # @param source [String] the source string
77
+ # @return [Range, nil] the range to select from the source string
57
78
  def handle_regexp args, arg, source
58
79
  match_data = arg.match(source)
59
80
  # nil has the same meaning as 0. use full match
@@ -61,6 +82,11 @@ module Contrast
61
82
  match_data.begin(group)...match_data.end(group)
62
83
  end
63
84
 
85
+ # Handles the range case for select.
86
+ #
87
+ # @param arg [Range] the first argument passed to the method
88
+ # @param source [String] the source string
89
+ # @return [Range, nil] the range to select from the source string
64
90
  def handle_range arg, source
65
91
  start = arg.begin
66
92
  finish = arg.end
@@ -73,6 +99,10 @@ module Contrast
73
99
  start...finish
74
100
  end
75
101
 
102
+ # Determines the range to select from the source string.
103
+ #
104
+ # @param source [Object] the source string
105
+ # @param args [Array] the arguments passed to the method
76
106
  def determine_select_range source, args
77
107
  arg = args[0]
78
108
  case arg
@@ -11,6 +11,11 @@ module Contrast
11
11
  # tags are unaffected beyond any merging of overlapping tags.
12
12
  class Splat < Contrast::Agent::Assess::Policy::Propagator::Base
13
13
  class << self
14
+ # @param propagation_node [Contrast::Agent::Assess::Policy::PropagationNode] the node responsible for the
15
+ # propagation action required by this method.
16
+ # @param preshift [Object] pre call state of the things.
17
+ # @param target [Object] the object to which the source is being appended
18
+ # @return [Object] the target with the tags applied
14
19
  def propagate propagation_node, preshift, target
15
20
  tracked_inputs = []
16
21
 
@@ -28,6 +33,11 @@ module Contrast
28
33
  Contrast::Agent::Assess::Tracker.properties(target)&.cleanup_tags
29
34
  end
30
35
 
36
+ # Handles the splatting of tags from the tracked inputs to the target.
37
+ #
38
+ # @param tracked_inputs [Array<String>] storage for the inputs to act on later
39
+ # @param target [Object] the object to which the source is being appended
40
+ # @return [Object] the target with the tags applied
31
41
  def splat_tags tracked_inputs, target
32
42
  return if tracked_inputs.empty?
33
43
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(target))
@@ -22,19 +22,23 @@ module Contrast
22
22
  @tags << SOURCE_TAG
23
23
  end
24
24
 
25
+ # @return [String]
25
26
  def node_class
26
27
  SOURCE
27
28
  end
28
29
 
29
30
  # This is confusing. Sources are Creation action but
30
31
  # Propagation type. Oh and also Type refers to input type,
31
- # like parameter, so we have to call this node_type. :-/
32
+ # like parameter, so we have to call this node_type. :-/\
33
+ #
34
+ # @return [Symbol]
32
35
  def node_type
33
36
  :TYPE_PROPAGATION
34
37
  end
35
38
 
36
39
  # Standard validation + TS trace version two rules:
37
40
  # Must have source and type
41
+ #
38
42
  # @raise[ArgumentError] raises if any of the required fields is missing or invalid
39
43
  def validate
40
44
  super
@@ -16,6 +16,10 @@ module Contrast
16
16
  # ActionDispatch::Http::Headers to convert keys like `referer` to `HTTP_REFERER` before they get to the
17
17
  # Rack::Request#get_header method
18
18
  # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/reflected_xss.md
19
+ #
20
+ # @param tag [String] the tag to be applied.
21
+ # @param source_type [String] the type of the source.
22
+ # @param source_name [String] the name of the source.
19
23
  def self.valid? tag, source_type, source_name
20
24
  return true unless tag == 'CROSS_SITE'
21
25
  return false if source_type == Contrast::Agent::Assess::Policy::SourceMethod::HEADER_KEY_TYPE
@@ -24,6 +24,12 @@ module Contrast
24
24
  }.cs__freeze
25
25
  TEMPLATE_PROPAGATION_NODE = Contrast::Agent::Assess::Policy::PropagationNode.new(NODE_HASH)
26
26
 
27
+ # @param trigger_node [Contrast::Agent::Assess::Policy::MethodPolicy] the node that governs this
28
+ # propagation event.
29
+ # @param _source [Object] the source of the propagation
30
+ # @param object [Object] the object to which the source is being appended
31
+ # @param args [Array] the arguments to the method
32
+ # @param ret [Object] the return value of the method
27
33
  def xss_tilt_trigger trigger_node, _source, object, ret, *args
28
34
  return unless (properties = Contrast::Agent::Assess::Tracker.properties!(ret))
29
35
 
@@ -63,6 +69,11 @@ module Contrast
63
69
 
64
70
  private
65
71
 
72
+ # @param scope [Object] the scope of the template
73
+ # @param erb_template_prerender [String] the template string
74
+ # @param properties [Contrast::Agent::Assess::Policy::Properties] the properties of the return value
75
+ # @param interpolated_inputs [Array<String>] the interpolated inputs
76
+ # @param ret [String] the return value of the method
66
77
  def handle_binding_variables scope, erb_template_prerender, ret, properties, interpolated_inputs
67
78
  binding_variables = scope.instance_variables
68
79
 
@@ -81,6 +92,11 @@ module Contrast
81
92
  end
82
93
  end
83
94
 
95
+ # @param args [Array<String>] the arguments to the method
96
+ # @param erb_template_prerender [String] the template string
97
+ # @param ret [String] the return value of the method
98
+ # @param properties [Contrast::Agent::Assess::Policy::Properties] the properties of the return value
99
+ # @param interpolated_inputs [Array<String>] the interpolated inputs
84
100
  def handle_local_variables args, erb_template_prerender, ret, properties, interpolated_inputs
85
101
  locals = args[1]
86
102
  locals.each do |local_name, local_value|
@@ -15,12 +15,26 @@ module Contrast
15
15
  # a finding if so.
16
16
  class Xpath
17
17
  class << self
18
+ # @param trigger_node [Contrast::Agent::Assess::Policy::MethodPolicy] the node that governs this
19
+ # propagation event.
20
+ # @param _source [Object] the source of the propagation
21
+ # @param object [Object] the object to which the source is being appended
22
+ # @param args [Array<Object>] the arguments to the method
23
+ # @param ret [Object] the return value of the method
24
+ # @return [Object] the return value of the method
18
25
  def xpath_expression_trigger trigger_node, _source, object, ret, *args
19
26
  return ret unless args
20
27
 
21
28
  process(trigger_node, object, ret, *args)
22
29
  end
23
30
 
31
+ # @param trigger_node [Contrast::Agent::Assess::Policy::MethodPolicy] the node that governs this
32
+ # propagation event.
33
+ # @param _source [Object] the source of the propagation
34
+ # @param object [Object] the object to which the source is being appended
35
+ # @param args [Array<Object>] the arguments to the method
36
+ # @param ret [Object] the return value of the method
37
+ # @return [Object] the return value of the method
24
38
  def xpath_oga_trigger trigger_node, _source, object, ret, *args
25
39
  return ret unless args
26
40
 
@@ -32,6 +46,11 @@ module Contrast
32
46
 
33
47
  private
34
48
 
49
+ # @param trigger_node [Contrast::Agent::Assess::Policy::MethodPolicy] the node that governs this
50
+ # propagation event.
51
+ # @param object [Object] the object to which the source is being appended
52
+ # @param ret [Object] the return value of the method
53
+ # @param args [Array<Object>] the arguments to the method
35
54
  def process trigger_node, object, ret, *args
36
55
  args.each do |arg|
37
56
  next unless arg.cs__is_a?(String) || arg.cs__is_a?(Symbol)
@@ -178,6 +178,10 @@ module Contrast
178
178
  Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(rule_id)
179
179
  end
180
180
 
181
+ # Check if the finding should be excluded due to the assess exclusion rules.
182
+ #
183
+ # @param finding [Contrast::Agent::Reporting::Finding]
184
+ # @param rule_id [String]
181
185
  def excluded_by_input_and_rule? finding, rule_id
182
186
  return false unless Contrast::SETTINGS.excluder.exclusions.any?
183
187
  return unless Contrast::Agent::REQUEST_TRACKER.current
@@ -185,7 +189,9 @@ module Contrast
185
189
  Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(finding, rule_id)
186
190
  end
187
191
 
188
- # Handles the Stored Xss rule. If a vector is stored in the database
192
+ # Handles the Stored Xss rule. If a vector is stored in the database.
193
+ #
194
+ # @param finding [Contrast::Agent::Reporting::Finding]
189
195
  def check_for_stored_xss finding
190
196
  return unless finding && finding.rule_id == 'reflected-xss'
191
197
 
@@ -200,6 +206,7 @@ module Contrast
200
206
  end
201
207
  end
202
208
 
209
+ # @param finding [Contrast::Agent::Reporting::Finding]
203
210
  def extract_dynamic_source_info finding
204
211
  return unless finding
205
212
 
@@ -29,7 +29,16 @@ module Contrast
29
29
  # from the application. Some rules rely on Content-Type validation.
30
30
  COLLECTABLE_RULES = %w[reflected-xss].cs__freeze
31
31
 
32
- attr_reader :rule_id, :required_tags, :disallowed_tags, :good_value, :bad_value
32
+ # @return [String]
33
+ attr_reader :rule_id
34
+ # @return [Array<String>]
35
+ attr_reader :required_tags
36
+ # @return [Array<String>]
37
+ attr_reader :disallowed_tags
38
+ # @return [Array<String>]
39
+ attr_reader :good_value
40
+ # @return [Array<String>]
41
+ attr_reader :bad_value
33
42
 
34
43
  ENCODER_START = 'CUSTOM_ENCODED_'
35
44
  # By default, any rule will be triggered if the source
@@ -46,6 +55,7 @@ module Contrast
46
55
  LIMITED_CHARS = 'LIMITED_CHARS'
47
56
  CUSTOM_ENCODED = 'CUSTOM_ENCODED'
48
57
  CUSTOM_VALIDATED = 'CUSTOM_VALIDATED'
58
+
49
59
  def initialize trigger_hash = {}, rule_hash = {}
50
60
  super(trigger_hash)
51
61
  good_value = trigger_hash[JSON_GOOD_VALUE]
@@ -16,6 +16,10 @@ module Contrast
16
16
  NEGATIVE_INFINITY = -POSITIVE_INFINITY
17
17
 
18
18
  class << self
19
+ # @param _patcher [Contrast::Agent::Patcher] the patcher instance
20
+ # @param object [Object] the object that was called
21
+ # @param _ret [Object] the return value of the method
22
+ # @param args [Array<Object>] the arguments passed to the method
19
23
  def valid? _patcher, object, _ret, args
20
24
  # Can arrive here from either:
21
25
  # regexp =~ string
@@ -21,6 +21,12 @@ module Contrast
21
21
  # a valid URL in which the User controls a section prior to the
22
22
  # querystring
23
23
  # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/server_side_request_forgery.md
24
+ #
25
+ # @param patcher [Contrast::Agent::Patcher] the patcher instance
26
+ # @param _object [Object] the object that was called
27
+ # @param _ret [Object] the return value of the method
28
+ # @param args [Array<Object>] the arguments passed to the method
29
+ # @return [Boolean] true if the finding is valid, false otherwise
24
30
  def self.valid? patcher, _object, _ret, args
25
31
  return true if patcher.id.to_s.start_with?(PATH_ONLY_PATCH_MARKER)
26
32
 
@@ -16,6 +16,12 @@ module Contrast
16
16
  # A finding is valid for XSS if the response type is not one of
17
17
  # those assumed to be safe
18
18
  # https://bitbucket.org/contrastsecurity/assess-specifications/src/master/rules/dataflow/reflected_xss.md
19
+ #
20
+ # @param patcher [Contrast::Agent::Patcher] the patcher instance
21
+ # @param _object [Object] the object that was called
22
+ # @param _ret [Object] the return value of the method
23
+ # @param args [Array<Object>] the arguments passed to the method
24
+ # @return [Boolean] true if the finding is valid, false otherwise
19
25
  def self.valid? _patcher, _object, _ret, _args
20
26
  content_type = Contrast::Agent::REQUEST_TRACKER.current&.response&.content_type
21
27
  return false unless content_type
@@ -73,7 +73,8 @@ module Contrast
73
73
  finding.routes << context.discovered_route if context&.discovered_route
74
74
  build_evidence(evidence, finding)
75
75
  finding.request = Contrast::Agent::Reporting::FindingRequest.convert(context.request) if context&.request
76
- hash = Contrast::Utils::HashDigest.generate_config_hash(finding)
76
+ # Hash must be built last so that finding has full context.
77
+ hash = Contrast::Utils::HashDigest.generate_response_hash(finding, context&.request)
77
78
  finding.hash_code = Contrast::Utils::StringUtils.force_utf8(hash)
78
79
  finding
79
80
  end
@@ -43,6 +43,7 @@ module Contrast
43
43
 
44
44
  def self.report_traces
45
45
  return unless Contrast::ASSESS.enabled?
46
+ return if ENV.fetch('CONTRAST__PIPELINE__RUN', nil) == 'true'
46
47
 
47
48
  collection = Contrast::Agent::Reporting::ReportingStorage.collection
48
49
 
@@ -24,7 +24,7 @@ module Contrast
24
24
  #
25
25
  # @param event [Contrast::Agent::Reporting::ReportingEvent] One of the DTMs valid for the
26
26
  # event field of Contrast::Agent::Reporting::ReportingEvent
27
- # @param response_data [Net::HTTP::Response]
27
+ # @param response_data [Net::HTTPResponse]
28
28
  def audit_event event, response_data = nil
29
29
  return unless ::Contrast::API.request_audit_requests || ::Contrast::API.request_audit_responses
30
30
 
@@ -69,21 +69,46 @@ module Contrast
69
69
  # @param event [Contrast::Agent::Reporting::ReportingEvent] The event to send to TeamServer. Really a
70
70
  # child of the ReportingEvent rather than a literal one.
71
71
  # @param connection [Net::HTTP] open connection
72
- # @return response [Net::HTTP::Response, nil] response from TS if no response
73
72
  def send_event event, connection
74
73
  return unless connection
75
74
  return unless event.valid?
76
75
 
77
76
  logger.debug('[Reporter] Preparing to send reporting event', event_class: event.cs__class)
78
77
  request = build_request(event)
79
- response = connection.request(request)
78
+ begin
79
+ response = connection.request(request)
80
+ rescue StandardError => e
81
+ handle_response_error(event, connection, e)
82
+ return
83
+ end
80
84
  audit.audit_event(event, response) if ::Contrast::API.request_audit_enable
81
- process_settings_response(response, event)
82
- process_preflight_response(event, response, connection)
83
- report_configuration(response, event)
85
+ process_event_response(event, response, connection)
84
86
  response
85
87
  rescue StandardError => e
86
- handle_response_error(event, connection, e)
88
+ logger.error('[Reporter] Error while processing event sent to TeamServer',
89
+ error: e,
90
+ event_type: event&.cs__class)
91
+ end
92
+
93
+ # Only two types of events require processing when returned from TeamServer; preflight and settings.
94
+ # For Settings, we want to update internally and then report the resulting configuration.
95
+ # For preflight, we want to process the response and send findings if requested.
96
+ #
97
+ # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer
98
+ # @param response [Net::HTTPResponse] the response from TeamServer
99
+ # @param connection [Net::HTTP] open connection
100
+ def process_event_response event, response, connection
101
+ case event
102
+ when Contrast::Agent::Reporting::Preflight
103
+ process_preflight_response(event, response, connection)
104
+ when Contrast::Agent::Reporting::AgentStartup,
105
+ Contrast::Agent::Reporting::ApplicationStartup,
106
+ Contrast::Agent::Reporting::ApplicationSettings,
107
+ Contrast::Agent::Reporting::ServerSettings
108
+
109
+ process_settings_response(response, event)
110
+ report_configuration(response, event)
111
+ end
87
112
  end
88
113
 
89
114
  # Write effective config to file:
@@ -91,7 +116,7 @@ module Contrast
91
116
  # 200 or 304. In case of 304 there will be no new settings and we can write current ones.
92
117
  # This is done on every settings request.
93
118
  #
94
- # @param response [Contrast::Agent::Reporting::Response, nil]
119
+ # @param response [Net::HTTPResponse, nil]
95
120
  # @param event [Contrast::Agent::Reporting::ReportingEvent]
96
121
  def report_configuration response, event
97
122
  return unless response
@@ -109,14 +134,17 @@ module Contrast
109
134
  logger.error('[Reporter] Error while reporting configuration', error: e, event_type: event&.cs__class)
110
135
  end
111
136
 
137
+ # @return [Contrast::Agent::Reporting::ConnectionStatus]
112
138
  def status
113
139
  @_status ||= Contrast::Agent::Reporting::ConnectionStatus.new
114
140
  end
115
141
 
142
+ # @return [Contrast::Agent::Reporting::ResponseHandler]
116
143
  def response_handler
117
144
  @_response_handler ||= Contrast::Agent::Reporting::ResponseHandler.new
118
145
  end
119
146
 
147
+ # @return [Contrast::Config::Diagnostics::Monitor]
120
148
  def diagnostics
121
149
  @_diagnostics ||= Contrast::Config::Diagnostics::Monitor.new(Contrast::LOGGER.path)
122
150
  end
@@ -90,7 +90,8 @@ module Contrast
90
90
  # Handles response processing and sets status
91
91
  #
92
92
  # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
93
- # @param response [Net::HTTP::Response]
93
+ # @param response [Net::HTTPResponse]
94
+ # @return [Contrast::Agent::Reporting::Response]
94
95
  def process_settings_response response, event
95
96
  res = response_handler.process(response, event)
96
97
  if res
@@ -115,7 +116,6 @@ module Contrast
115
116
  return unless response&.body && connection
116
117
 
117
118
  findings_to_return = response.body.split(',').delete_if { |el| el.include?('*') }
118
- mode.resend.reset_rescue_attempts
119
119
  findings_to_return.each do |index|
120
120
  preflight_message = event.messages[index.to_i]
121
121
  preflight_data = preflight_message&.data
@@ -127,6 +127,12 @@ module Contrast
127
127
 
128
128
  send_event(corresponding_finding, connection)
129
129
  end
130
+
131
+ # Event rejected traces should be removed from the set.
132
+ # We could clean this up to know if the message was already deleted by the loop above.
133
+ event.messages&.each do |preflight_message|
134
+ Contrast::Agent::Reporting::ReportingStorage.delete(preflight_message&.data)
135
+ end
130
136
  rescue StandardError => e
131
137
  logger.error('[Reporter] Unable to handle preflight response', e)
132
138
  end
@@ -44,12 +44,7 @@ module Contrast
44
44
  def delete key
45
45
  return unless key
46
46
 
47
- logger.debug('Starting deleting value for', key: key)
48
-
49
- deleted_value = collection.delete(key)
50
- logger.debug('Key wasn\'t found') unless deleted_value
51
-
52
- deleted_value
47
+ collection.delete(key)
53
48
  end
54
49
 
55
50
  # @param rule_id [String] the rule_id
@@ -20,11 +20,12 @@ module Contrast
20
20
 
21
21
  # Process the response from TS
22
22
  #
23
- # @param response [Net::HTTP::Response, nil]
23
+ # @param response [Net::HTTPResponse, nil]
24
24
  # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
25
- # @return response [Net::HTTP::Response, nil]
25
+ # @return response [Net::HTTPResponse, nil]
26
26
  def process response, event
27
27
  logger.debug('[Reporter] Received a response')
28
+ return if event&.cs__is_a?(Contrast::Agent::Reporting::Finding)
28
29
  return unless analyze_response?(response)
29
30
 
30
31
  # Handle the response body and obtain server_features or app_settings
@@ -107,7 +108,7 @@ module Contrast
107
108
  # Handles the errors code received from TS and takes appropriate action.
108
109
  # If we are here the response.code is an error that needs handling [4XX]
109
110
  #
110
- # @param response [Net::HTTP::Response]
111
+ # @param response [Net::HTTPResponse]
111
112
  def handle_error response
112
113
  case response&.code
113
114
  when ERROR_CODES[:message_not_sent]