contrast-agent 7.5.0 → 7.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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/assess/rule/response/base_rule.rb +2 -1
- 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 +35 -7
- data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +8 -2
- data/lib/contrast/agent/reporting/reporting_utilities/reporting_storage.rb +1 -6
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +4 -3
- data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +22 -12
- data/lib/contrast/agent/request/request.rb +1 -1
- 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/hash_digest.rb +0 -14
- data/lib/contrast/utils/hash_digest_extend.rb +16 -5
- data/lib/contrast/utils/json.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 +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
|
-
|
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
|
-
|
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
|
@@ -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,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
|
-
|
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
|
-
|
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
|
-
|
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 [
|
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::
|
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
|
-
|
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::
|
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
|
+
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::
|
111
|
+
# @param response [Net::HTTPResponse]
|
111
112
|
def handle_error response
|
112
113
|
case response&.code
|
113
114
|
when ERROR_CODES[:message_not_sent]
|