contrast-agent 7.5.0 → 7.6.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.
- checksums.yaml +4 -4
- data/ext/cs__common/cs__common.c +5 -5
- data/ext/cs__contrast_patch/cs__contrast_patch.c +2 -1
- data/ext/cs__scope/cs__scope.c +6 -5
- data/lib/contrast/agent/assess/events/event_data.rb +11 -2
- data/lib/contrast/agent/assess/finalizers/freeze.rb +1 -0
- data/lib/contrast/agent/assess/finalizers/hash.rb +7 -0
- data/lib/contrast/agent/assess/policy/patcher.rb +2 -0
- data/lib/contrast/agent/assess/policy/policy.rb +4 -0
- data/lib/contrast/agent/assess/policy/policy_node.rb +29 -7
- data/lib/contrast/agent/assess/policy/preshift.rb +34 -1
- data/lib/contrast/agent/assess/policy/propagation_method.rb +16 -1
- data/lib/contrast/agent/assess/policy/propagation_node.rb +40 -1
- data/lib/contrast/agent/assess/policy/propagator/append.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/base.rb +10 -0
- data/lib/contrast/agent/assess/policy/propagator/buffer.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagator/center.rb +14 -0
- data/lib/contrast/agent/assess/policy/propagator/custom.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagator/database_write.rb +14 -0
- data/lib/contrast/agent/assess/policy/propagator/insert.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagator/match_data.rb +38 -0
- data/lib/contrast/agent/assess/policy/propagator/next.rb +6 -0
- data/lib/contrast/agent/assess/policy/propagator/prepend.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/remove.rb +4 -0
- data/lib/contrast/agent/assess/policy/propagator/replace.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/reverse.rb +5 -0
- data/lib/contrast/agent/assess/policy/propagator/select.rb +30 -0
- data/lib/contrast/agent/assess/policy/propagator/splat.rb +10 -0
- data/lib/contrast/agent/assess/policy/source_node.rb +5 -1
- data/lib/contrast/agent/assess/policy/source_validation/cross_site_validator.rb +4 -0
- data/lib/contrast/agent/assess/policy/trigger/reflected_xss.rb +16 -0
- data/lib/contrast/agent/assess/policy/trigger/xpath.rb +19 -0
- data/lib/contrast/agent/assess/policy/trigger_method.rb +8 -1
- data/lib/contrast/agent/assess/policy/trigger_node.rb +11 -1
- data/lib/contrast/agent/assess/policy/trigger_validation/redos_validator.rb +4 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/ssrf_validator.rb +6 -0
- data/lib/contrast/agent/assess/policy/trigger_validation/xss_validator.rb +6 -0
- data/lib/contrast/agent/hooks/at_exit_hook.rb +1 -0
- data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +1 -1
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +3 -3
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +18 -6
- data/lib/contrast/agent/request/request_handler.rb +1 -0
- data/lib/contrast/agent/version.rb +1 -1
- data/lib/contrast/configuration.rb +1 -1
- data/lib/contrast/utils/middleware_utils.rb +9 -0
- data/lib/contrast/utils/routes_sent.rb +3 -2
- data/lib/contrast.rb +2 -2
- data/resources/assess/policy.json +50 -1
- data/ruby-agent.gemspec +13 -13
- 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 | 
            -
                       | 
| 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
         | 
| @@ -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:: | 
| 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:: | 
| 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:: | 
| 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:: | 
| 23 | 
            +
                    # @param response [Net::HTTPResponse, nil]
         | 
| 24 24 | 
             
                    # @param event [Contrast::Agent::Reporting::ReportingEvent] The event sent to TeamServer.
         | 
| 25 | 
            -
                    # @return response [Net:: | 
| 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:: | 
| 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:: | 
| 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 [ | 
| 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:: | 
| 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:: | 
| 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:: | 
| 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
         | 
| @@ -124,7 +124,7 @@ module Contrast | |
| 124 124 |  | 
| 125 125 | 
             
                # @return [Contrast::Components::Assess::Interface]
         | 
| 126 126 | 
             
                def assess
         | 
| 127 | 
            -
                  @assess ||= Contrast::Components:: | 
| 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  | 
| 29 | 
            -
                    return false  | 
| 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 |  |