contrast-agent 7.5.0 → 7.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) 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/hooks/at_exit_hook.rb +1 -0
  39. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
  40. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
  41. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +1 -1
  42. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +3 -3
  43. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +18 -6
  44. data/lib/contrast/agent/request/request_handler.rb +1 -0
  45. data/lib/contrast/agent/version.rb +1 -1
  46. data/lib/contrast/configuration.rb +1 -1
  47. data/lib/contrast/utils/middleware_utils.rb +9 -0
  48. data/lib/contrast/utils/routes_sent.rb +3 -2
  49. data/lib/contrast.rb +2 -2
  50. data/resources/assess/policy.json +50 -1
  51. data/ruby-agent.gemspec +13 -13
  52. metadata +23 -22
@@ -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
@@ -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,7 +69,7 @@ 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
72
+ # @return response [Net::HTTPResponse, nil] response from TS if no response
73
73
  def send_event event, connection
74
74
  return unless connection
75
75
  return unless event.valid?
@@ -90,7 +90,7 @@ 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
94
  def process_settings_response response, event
95
95
  res = response_handler.process(response, event)
96
96
  if res
@@ -20,9 +20,9 @@ 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
28
  return unless analyze_response?(response)
@@ -107,7 +107,7 @@ module Contrast
107
107
  # Handles the errors code received from TS and takes appropriate action.
108
108
  # If we are here the response.code is an error that needs handling [4XX]
109
109
  #
110
- # @param response [Net::HTTP::Response]
110
+ # @param response [Net::HTTPResponse]
111
111
  def handle_error response
112
112
  case response&.code
113
113
  when ERROR_CODES[:message_not_sent]
@@ -68,7 +68,7 @@ module Contrast
68
68
 
69
69
  # check if response code is valid before analyze it
70
70
  #
71
- # @param response [Net::HTTP::Response, nil]
71
+ # @param response [Net::HTTPResponse, nil]
72
72
  # @return [Boolean]
73
73
  def analyze_response? response
74
74
  # Code descriptions:
@@ -118,7 +118,7 @@ module Contrast
118
118
  @_last_response_code = response_code
119
119
  return true if ANALYZE_WHEN.include?(response_code)
120
120
 
121
- handle_error(response) if ERROR_CODES.value?(response_code)
121
+ handle_error(response) if ERROR_CODES.value?(response_code) && response&.body
122
122
  # There was error, so analyze the Error and nothing more.
123
123
  false
124
124
  end
@@ -126,7 +126,7 @@ module Contrast
126
126
  # Analyze the headers of the response code. They have information about the
127
127
  # retry timeout and some response bodies contains error messages.
128
128
  #
129
- # @param response [String] the response code from Net::HTTPResponse, which is obnoxiousy a String, not an
129
+ # @param response [Net::HTTPResponse]
130
130
  # Integer
131
131
  # @param message [String] Message to log.
132
132
  # @param mode [Symbol, nil]
@@ -142,6 +142,8 @@ module Contrast
142
142
  error_message: error_message || 'none',
143
143
  auth_error: auth_error || 'none')
144
144
  end
145
+ return unless rejected_by_ts?(response)
146
+
145
147
  suspend_reporting(message, ready_after, error_message) if mode == @_mode.resending
146
148
  return unless mode == @_mode.disabled
147
149
 
@@ -152,7 +154,7 @@ module Contrast
152
154
 
153
155
  # Extract what we've received.
154
156
  #
155
- # @param response [Net::HTTP::Response, nil]
157
+ # @param response [Net::HTTPResponse, nil]
156
158
  # @return [Array<String, Integer>] all collected error info.
157
159
  def extract_response_info response
158
160
  # Extract what we got from the response:
@@ -164,11 +166,21 @@ module Contrast
164
166
  [ready_after.to_i, error_message, auth_error]
165
167
  end
166
168
 
169
+ # We only want to shut down the agent if TeamServer actually told us to, not because of a network error
170
+ #
171
+ # @param [Net::HTTPResponse]
172
+ # @return Boolean
173
+ def rejected_by_ts? response
174
+ response_body = response&.body || Contrast::Utils::ObjectShare::EMPTY_STRING
175
+ response_data = Contrast::Utils::Json.parse(response_body, deep_symbolize: true)
176
+ response_data.key?(:success) && response_data[:success] == false
177
+ end
178
+
167
179
  # Extract Last-Modified header from ServerSettings response.
168
180
  # The new GET server settings endpoint have different payload.
169
181
  # Extract the last modify headers with last update form TS.
170
182
  #
171
- # @param response [Net::HTTP::Response, nil]
183
+ # @param response [Net::HTTPResponse, nil]
172
184
  # @param event [Contrast::Agent::Reporting::ServerSettings,
173
185
  # Contrast::Agent::Reporting::ApplicationSettings, nil]
174
186
  # @return last_modified[integer, nil] Time since last server update
@@ -250,7 +262,7 @@ module Contrast
250
262
  #
251
263
  # This method works to extract away these differences.
252
264
  #
253
- # @param response [Net::HTTP::Response, nil]
265
+ # @param response [Net::HTTPResponse, nil]
254
266
  # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
255
267
  # @return response [Contrast::Agent::Reporting::Response]
256
268
  def convert_response response, event
@@ -26,6 +26,7 @@ module Contrast
26
26
  #
27
27
  def report_observed_route
28
28
  return unless (reporter = Contrast::Agent.reporter)
29
+ return if Contrast::Agent::REQUEST_TRACKER.current&.response&.response_code == 404
29
30
 
30
31
  reporter.send_event(context.observed_route) if Contrast::ROUTES_SENT.sendable?(context.observed_route)
31
32
  end
@@ -3,6 +3,6 @@
3
3
 
4
4
  module Contrast
5
5
  module Agent
6
- VERSION = '7.5.0'
6
+ VERSION = '7.6.0'
7
7
  end
8
8
  end
@@ -124,7 +124,7 @@ module Contrast
124
124
 
125
125
  # @return [Contrast::Components::Assess::Interface]
126
126
  def assess
127
- @assess ||= Contrast::Components::Assess::Interface.new # rubocop:disable Naming/MemoizedInstanceVariableName
127
+ @assess ||= Contrast::Components::Settings::Interface.new # rubocop:disable Naming/MemoizedInstanceVariableName
128
128
  end
129
129
 
130
130
  # @return [Contrast::Components::Inventory::Interface]
@@ -91,6 +91,15 @@ module Contrast
91
91
  rescue Contrast::SecurityException => e
92
92
  logger.trace('Security Exception raised during application lifecycle to prevent an attack', e)
93
93
  raise(e)
94
+ rescue StandardError => e
95
+ # If there is a routing error of this type, then we cannot find a method explicitly mapped to this route.
96
+ # In this case, we should report nothing.
97
+ if Contrast::Utils::ClassUtil.truly_defined?('ActionController::RoutingError') &&
98
+ e.is_a?(ActionController::RoutingError)
99
+
100
+ Contrast::Agent::REQUEST_TRACKER.current&.observed_route = nil
101
+ end
102
+ raise(e)
94
103
  end
95
104
  end
96
105
  end
@@ -25,8 +25,9 @@ module Contrast
25
25
  # @param route [Contrast::Agent::Reporting::ObservedRoute] the route
26
26
  # @return [boolean]
27
27
  def sendable? route
28
- return false if Contrast::Utils::DuckUtils.empty_duck?(route.signature)
29
- return false if Contrast::Utils::DuckUtils.empty_duck?(route.url)
28
+ return false unless route
29
+ return false unless route.signature && !route.signature.blank?
30
+ return false unless route.url && !route.url.blank?
30
31
 
31
32
  route_hash = route.hash_id
32
33