contrast-agent 7.1.0 → 7.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/ext/extconf_common.rb +88 -14
  3. data/lib/contrast/agent/assess/policy/source_method.rb +13 -4
  4. data/lib/contrast/agent/assess/policy/trigger_method.rb +12 -18
  5. data/lib/contrast/agent/excluder/excluder.rb +64 -31
  6. data/lib/contrast/agent/protect/rule/base.rb +4 -6
  7. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker.rb +1 -1
  8. data/lib/contrast/agent/protect/rule/bot_blocker/bot_blocker_input_classification.rb +2 -2
  9. data/lib/contrast/agent/protect/rule/cmdi/cmdi_backdoors.rb +1 -1
  10. data/lib/contrast/agent/protect/rule/cmdi/cmdi_base_rule.rb +1 -1
  11. data/lib/contrast/agent/protect/rule/deserialization/deserialization.rb +2 -2
  12. data/lib/contrast/agent/protect/rule/path_traversal/path_traversal_semantic_security_bypass.rb +1 -1
  13. data/lib/contrast/agent/protect/rule/sqli/sqli_semantic/sqli_dangerous_functions.rb +1 -1
  14. data/lib/contrast/agent/protect/rule/utils/filters.rb +6 -6
  15. data/lib/contrast/agent/protect/rule/xxe/xxe.rb +1 -1
  16. data/lib/contrast/agent/reporting/client/interface.rb +132 -0
  17. data/lib/contrast/agent/reporting/client/interface_base.rb +27 -0
  18. data/lib/contrast/agent/reporting/connection_status.rb +0 -1
  19. data/lib/contrast/agent/reporting/input_analysis/input_analysis_result.rb +6 -4
  20. data/lib/contrast/agent/reporting/reporter.rb +11 -26
  21. data/lib/contrast/agent/reporting/reporting_events/discovered_route.rb +1 -1
  22. data/lib/contrast/agent/reporting/reporting_utilities/audit.rb +10 -3
  23. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client.rb +47 -6
  24. data/lib/contrast/agent/reporting/reporting_utilities/reporter_client_utils.rb +40 -31
  25. data/lib/contrast/agent/reporting/reporting_utilities/resend.rb +144 -0
  26. data/lib/contrast/agent/reporting/reporting_utilities/response_handler.rb +35 -13
  27. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_mode.rb +14 -1
  28. data/lib/contrast/agent/reporting/reporting_utilities/response_handler_utils.rb +11 -11
  29. data/lib/contrast/agent/request/request.rb +27 -12
  30. data/lib/contrast/agent/telemetry/base.rb +18 -19
  31. data/lib/contrast/agent/telemetry/exception/obfuscate.rb +97 -0
  32. data/lib/contrast/agent/telemetry/exception.rb +1 -0
  33. data/lib/contrast/agent/version.rb +1 -1
  34. data/lib/contrast/components/config/sources.rb +6 -5
  35. data/lib/contrast/components/settings.rb +9 -0
  36. data/lib/contrast/config/diagnostics/source_config_value.rb +5 -1
  37. data/lib/contrast/config/diagnostics/tools.rb +4 -4
  38. data/lib/contrast/config/validate.rb +2 -2
  39. data/lib/contrast/configuration.rb +11 -19
  40. data/lib/contrast/framework/grape/support.rb +1 -2
  41. data/lib/contrast/framework/manager.rb +17 -8
  42. data/lib/contrast/framework/rack/support.rb +99 -1
  43. data/lib/contrast/framework/rails/support.rb +1 -2
  44. data/lib/contrast/framework/sinatra/support.rb +1 -2
  45. data/lib/contrast/logger/aliased_logging.rb +18 -9
  46. data/lib/contrast/utils/hash_utils.rb +21 -2
  47. data/lib/contrast/utils/request_utils.rb +14 -0
  48. data/resources/assess/policy.json +11 -0
  49. metadata +6 -3
  50. data/lib/contrast/agent/reporting/input_analysis/details/bot_blocker_details.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81222798666699f86b31b925d531d2ee2229eb7934582d2a502cc61de3ca4e0b
4
- data.tar.gz: 7dd4d41a58600b7d57b5f57cf95c42bd2f6d198f5f1906e93751e33c09efa3e0
3
+ metadata.gz: 1a9469e29e067a8c67e64a771b3d60522c4999ecd1133fdc96c3f6c9ff528f71
4
+ data.tar.gz: 8cbed6cbdf60a9017c80f0edbd5bbe11ea104620ae606f2eac5db4d2c930667f
5
5
  SHA512:
6
- metadata.gz: 02e5d3aa6b342e8c4277ad6cefde65819aed6f6b4a1079cfe7528fcce236ec702aa8a63fc756c403c0df6e3ef38d5d25dc9f1c6e5c450d272122d699b6fd9872
7
- data.tar.gz: 9cc83b5f69edeea949784ae766be7e02c1d589e58b2af5b5c1bd7975dcee1450d294bc7de647f60a9a3f18a1ea05a43bfe452a52d4760ed1b1cfcb0a7339a6ab
6
+ metadata.gz: 3ba2a3f4887a593a5b61544ce6622df70cfe54d1a6b7fe6c4e57927714ab5b6c16ccf13b8c7cd703acb0f55e405c6bcb8e55c781e6f7171d087dc48885942a33
7
+ data.tar.gz: 7d791ee31ffe0857ac51a6d26f7489baf986f3b4445cc697f202df3ab47dd77c2e484a2516ca2ed442afbfe9be02e8139dab2a866dcc0358210774a6c1e33ca9
@@ -5,6 +5,31 @@ require 'mkmf'
5
5
  require 'rbconfig'
6
6
  require_relative '../lib/contrast/agent/version'
7
7
 
8
+ # Create explicit symbol list, that will be set as promise to be loaded dynamic on run time.
9
+ SYMS = %w[
10
+ _assess
11
+ _policy
12
+ _assess_policy
13
+ _assess_propagator
14
+ _core_assess
15
+ _contrast_patcher
16
+ _contrast_check_prepended
17
+ _contrast_check_and_register_instance_patch
18
+ _contrast_register_singleton_prepend_patch
19
+ _contrast_register_patch
20
+ _contrast_register_singleton_patch
21
+ _inst_methods_enter_cntr_scope
22
+ _inst_methods_enter_method_scope
23
+ _inst_methods_exit_cntr_scope
24
+ _inst_methods_exit_method_scope
25
+ _inst_methods_in_cntr_scope
26
+ _rb_sym_method
27
+ _rb_sym_hash_tracked
28
+ _rb_sym_skip_assess_analysis
29
+ _rb_sym_skip_contrast_analysis
30
+ _patch_via_funchook
31
+ ].freeze # rubocop:disable Security/Object/Freeze
32
+
8
33
  # The mkmf.rb file uses all passed flags from Ruby configuration (RbConfig::CONFIG) on
9
34
  # Ruby build time. Problem with Clang and GCC is that it do not keep up with c89 and finds
10
35
  # error on including <ryby.h> as not allowing inline variables.
@@ -32,8 +57,7 @@ require_relative '../lib/contrast/agent/version'
32
57
  STANDARD_FLAGS = '-std=gnu89'
33
58
  CLANG = 'clang'
34
59
 
35
- # TODO: RUBY-999999 Add -pedantic flag, remove all warning flags and see to it that as many as possible become obsolete.
36
- # Note: Adding -pedantic could raise <ruby.h> warnings, and we are not in control of that code.
60
+ # Adding -pedantic could raise <ruby.h> warnings, and we are not in control of that code.
37
61
  # e.g. error: '_Bool' is a C99 extension [-Werror,-Wc99-extensions] ; empty macros and etc.
38
62
  #
39
63
  # -Wno-int-conversion => Passing VALUEs as function args but required as unsigned long parameters.
@@ -55,8 +79,19 @@ WARNING_FLAGS = %w[
55
79
  # Flags that are only recognized by gcc:
56
80
  GCC_FLAGS = %w[-Wno-maybe-uninitialized].freeze # rubocop:disable Security/Object/Freeze
57
81
 
82
+ def darwin?
83
+ RbConfig::CONFIG['target_os'].include?('darwin')
84
+ end
85
+
58
86
  # Extend $CFLAGS passed directly to compiler in ruby mkmf
87
+ #
88
+ # Extended flags are mainly tested with clang and gcc. Experience with other compilers may vary.
89
+ # To that end if something brakes on client side we must have a mechanism to go back to previous
90
+ # non strict gnu89 standard and be able to maintain the build.
91
+ # We can disable newly added changes with this setting CONTRAST_USE_C89=false.
59
92
  def extend_cflags
93
+ return if ENV['CONTRAST__USE_GNU89'] == 'false'
94
+
60
95
  $CFLAGS += " #{ [STANDARD_FLAGS, WARNING_FLAGS].flatten.join(' ') }"
61
96
  # Extend with GCC specific flags:
62
97
  unless RbConfig::MAKEFILE_CONFIG['CC'].downcase.include?(CLANG) ||
@@ -67,6 +102,52 @@ def extend_cflags
67
102
  end
68
103
  end
69
104
 
105
+ # use C compiler if set.
106
+ def enable_env_cc
107
+ RbConfig::CONFIG['CC'] = RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
108
+ end
109
+
110
+ # Since we cannot link directly the bundles created after the extensions being build,
111
+ # we can pass flags to the linker to resolve symbols not being found during compilation,
112
+ # since they are going to be available on load time. Ruby first is loaded then the extensions.
113
+ # MacOS introduced new fixups with Xcode 14. This causes the issue of symbols not being linked during
114
+ # compilation for the Agent, and other ruby related issues with objective C objects being used,
115
+ # bigdecimal being different and so on.
116
+ #
117
+ # Ruby itself is build on MacOS with '--enabled-shared' flag, also Ruby interpreters compiled
118
+ # under Xcode14 no longer specify by default in DLDFLAGS this flag:
119
+ #
120
+ # '-undefined dynamic_lookup'
121
+ # ( As of Xcode14.3 behavior of dynamic_lookup is reverted back.)
122
+ #
123
+ # This simply is telling the linker that any unknown symbols will be resolved dynamic on extension load.
124
+ # However with the new fixup chain introduced an warning may be produced:
125
+ # ld: warning: -undefined dynamic_lookup may not work with chained fixups.
126
+ # The fixups are responsible for the dynamic linking of the dylibs.
127
+ def mac_symbol_resolve
128
+ # If this is braking the build, it can be disabled.
129
+ return if ENV['CONTRAST__NO_MAC_LD_FIX'] == 'true'
130
+ return unless darwin?
131
+
132
+ # Disabled as of the unknown changes to newly ruby interpreter builds:
133
+ # RbConfig::CONFIG['EXTDLDFLAGS'] = "-bundle_loader #{ RbConfig::CONFIG['EXTDLDFLAGS'] }"
134
+
135
+ # Avoid using this, as not desirable:
136
+ #
137
+ # Adding -no_fixup_chains will brake older compilers but will work on newer.
138
+ # This flag solves the problem but on the long run might not be the solution needed, now not being default,
139
+ # any new feature changes on Mac or Ruby side might brake things again.
140
+ # Use this only if explicitly set as ENV var,as a backup on client's end.
141
+ #
142
+ # $LDFLAGS << " " << flag
143
+ append_ldflags('-Wl,-no_fixup_chains -undefined dynamic_lookup') if ENV['CONTRAST__MAC_LD_USE_ALT'] == 'true'
144
+
145
+ # Another alternative is to use -Wl,-U with explicit listed depending symbols.
146
+ # Append the symfile:
147
+ SYMS.each { |sym| $DLDFLAGS << " -Wl,-U,#{ sym }" } unless ENV['CONTRAST__MAC_LD_USE_ALT'] == 'true'
148
+ end
149
+
150
+ # Generate Makefile.
70
151
  def make!
71
152
  create_makefile("#{ $TO_MAKE }/#{ $TO_MAKE }")
72
153
  end
@@ -75,9 +156,9 @@ end
75
156
  # | MOVING CODE BELLOW THIS SECTION MAY BRAKE MAKEFILE. ORDER MATTERS! |
76
157
  # ----------------------------------------------------------------------
77
158
 
159
+ # __dir__ is relative to the file you're reading.
160
+ # this file you're reading is presently within $APP_ROOT/ext/.
78
161
  def ext_path
79
- # __dir__ is relative to the file you're reading.
80
- # this file you're reading is presently within $APP_ROOT/ext/.
81
162
  __dir__
82
163
  end
83
164
 
@@ -85,14 +166,7 @@ end
85
166
  # funchook.h file. Then we can pass CFLAGS and extend makefile flags and invoke make!
86
167
  require_relative './build_funchook'
87
168
 
88
- # Extended flags are mainly tested with clang and gcc. Experience with other compilers may vary.
89
- # To that end if something brakes on client side we must have a mechanism to go back to previous
90
- # non strict gnu89 standard and be able to maintain the build.
91
- # We can disable newly added changes with this setting CONTRAST_USE_C89=false.
92
- extend_cflags unless ENV['CONTRAST__USE_GNU89'] == 'false'
93
-
94
- # use same C compiler if set.
95
- RbConfig::CONFIG['CC'] = RbConfig::MAKEFILE_CONFIG['CC'] = ENV['CC'] if ENV['CC']
96
-
97
- # Generate Makefile.
169
+ enable_env_cc
170
+ mac_symbol_resolve
171
+ extend_cflags
98
172
  make!
@@ -43,6 +43,7 @@ module Contrast
43
43
  return unless analyze?(method_policy, object, ret, args)
44
44
  return if event_limit?(method_policy)
45
45
  return unless (source_node = method_policy.source_node)
46
+ # Exclusions makes method slow:
46
47
  return if excluded_by_url?
47
48
 
48
49
  # used to hold the object and ret
@@ -76,6 +77,7 @@ module Contrast
76
77
 
77
78
  source_name ||= determine_source_name(source_node, source_data.object, source_data.ret, *args)
78
79
 
80
+ # Exclusions makes method slow:
79
81
  return if excluded_by_input?(source_type, source_name)
80
82
 
81
83
  # We know we only work on certain things.
@@ -188,16 +190,23 @@ module Contrast
188
190
  #
189
191
  # @return [Boolean]
190
192
  def excluded_by_input? source_type, source_name
191
- context = Contrast::Agent::REQUEST_TRACKER.current
192
- Contrast::SETTINGS.excluder.assess_excluded_by_input?(context.request, source_type, source_name)
193
+ return false unless Contrast::SETTINGS.excluder.exclusions.any?
194
+ return false unless Contrast::Agent::REQUEST_TRACKER.current
195
+ # skip if the source is collection, it will be evaluated by it's elements. Collections are not
196
+ # trackable.
197
+ return false if source_name.cs__is_a?(Hash)
198
+
199
+ Contrast::SETTINGS.excluder.assess_excluded_by_input?(source_type, source_name)
193
200
  end
194
201
 
195
202
  # Should a source be excluded because it matches one of the url exclusion rules?
196
203
  #
197
204
  # @return [Boolean]
198
205
  def excluded_by_url?
199
- context = Contrast::Agent::REQUEST_TRACKER.current
200
- Contrast::SETTINGS.excluder.assess_excluded_by_url?(context.request)
206
+ return false unless Contrast::SETTINGS.excluder.exclusions.any?
207
+ return false unless Contrast::Agent::REQUEST_TRACKER.current
208
+
209
+ Contrast::SETTINGS.excluder.assess_excluded_by_url?
201
210
  end
202
211
  end
203
212
  end
@@ -93,11 +93,11 @@ module Contrast
93
93
 
94
94
  request = find_request(source)
95
95
  return unless reportable?(request&.env)
96
- return if excluded_by_url_and_rule?(request, trigger_node.rule_id)
96
+ return if excluded_by_url_and_rule?(trigger_node.rule_id)
97
97
 
98
98
  finding = Contrast::Agent::Reporting::Finding.new(trigger_node.rule_id)
99
99
  finding.attach_data(trigger_node, source, object, ret, request, *args)
100
- return if excluded_by_input_and_rule?(request, finding, trigger_node.rule_id)
100
+ return if excluded_by_input_and_rule?(finding, trigger_node.rule_id)
101
101
 
102
102
  finding.hash_code = Contrast::Utils::HashDigest.generate_event_hash(finding, source, request)
103
103
  check_for_stored_xss(finding)
@@ -167,28 +167,22 @@ module Contrast
167
167
  end
168
168
  end
169
169
 
170
- def build_events finding, event
171
- return unless event
172
-
173
- event.parent_events&.each do |parent_event|
174
- build_events(finding, parent_event)
175
- end
176
- # events could technically be nil, but we would have failed the rule check before getting here. not
177
- # worth the nil check
178
- finding.events << event.to_dtm_event
179
- end
180
-
181
170
  # Check if the finding should be excluded due to the assess exclusion rules.
182
171
  #
183
- # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
184
172
  # @param rule_id [String]
185
173
  # return [Boolean]
186
- def excluded_by_url_and_rule? request, rule_id
187
- Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(request, rule_id)
174
+ def excluded_by_url_and_rule? rule_id
175
+ return false unless Contrast::SETTINGS.excluder.exclusions.any?
176
+ return unless Contrast::Agent::REQUEST_TRACKER.current
177
+
178
+ Contrast::SETTINGS.excluder.assess_excluded_by_url_and_rule?(rule_id)
188
179
  end
189
180
 
190
- def excluded_by_input_and_rule? request, finding, rule_id
191
- Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(request, finding, rule_id)
181
+ def excluded_by_input_and_rule? finding, rule_id
182
+ return false unless Contrast::SETTINGS.excluder.exclusions.any?
183
+ return unless Contrast::Agent::REQUEST_TRACKER.current
184
+
185
+ Contrast::SETTINGS.excluder.assess_excluded_by_input_and_rule?(finding, rule_id)
192
186
  end
193
187
 
194
188
  # Handles the Stored Xss rule. If a vector is stored in the database
@@ -3,6 +3,8 @@
3
3
 
4
4
  require 'contrast/agent/reporting/settings/url_exclusion'
5
5
  require 'contrast/agent/reporting/input_analysis/input_type'
6
+ require 'contrast/utils/object_share'
7
+ require 'contrast/utils/assess/object_store'
6
8
 
7
9
  module Contrast
8
10
  module Agent
@@ -19,11 +21,14 @@ module Contrast
19
21
  @exclusions = exclusions
20
22
  end
21
23
 
24
+ def cached_paths
25
+ @_cached_paths ||= Contrast::Utils::Assess::ObjectStore.new(10)
26
+ end
27
+
22
28
  # Determine if an input is excluded for protect rule.
23
29
  #
24
30
  # @param results [Array<Contrast::Agent::Reporting::InputAnalysisResult>]
25
- # @param request_path [String] Current request path
26
- def protect_excluded_by_input? results, request_path
31
+ def protect_excluded_by_input? results
27
32
  return false unless results.any?
28
33
 
29
34
  exclusion_matched = 0
@@ -35,8 +40,7 @@ module Contrast
35
40
 
36
41
  # Based on strategy:
37
42
  match = input_match_strategy(exclusion_match,
38
- input_match?(exclusion_match, rule_result.input_type, rule_result.key),
39
- request_path)
43
+ input_match?(exclusion_match, rule_result.input_type, rule_result.key))
40
44
  exclusion_matched += 1 if match
41
45
  end
42
46
  end
@@ -48,25 +52,21 @@ module Contrast
48
52
  # If an assess URL exclusion rule applies to the current url, *and* is defined as "All Rules"
49
53
  # then we can avoid any tracking for the request.
50
54
  #
51
- # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
52
55
  # @return [Boolean]
53
- def assess_excluded_by_url? request
56
+ def assess_excluded_by_url?
54
57
  assess_url_exclusions_for_all_rules.any? do |exclusion_matcher|
55
- path_match?(exclusion_matcher, request.path)
58
+ path_match?(exclusion_matcher)
56
59
  end
57
60
  end
58
61
 
59
62
  # If an assess URL exclusion rule applies to the current url, *and* also covers the
60
63
  # provided rule_id, then we can avoid tracking this entry.
61
64
  #
62
- # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
63
65
  # @param rule_id [String]
64
66
  # return [Boolean]
65
- def assess_excluded_by_url_and_rule? request, rule_id
66
- request_path = request.path
67
-
67
+ def assess_excluded_by_url_and_rule?rule_id
68
68
  assess_url_exclusions.any? do |exclusion_matcher|
69
- path_match?(exclusion_matcher, request_path) &&
69
+ path_match?(exclusion_matcher) &&
70
70
  (exclusion_matcher.assess_rules.empty? || exclusion_matcher.assess_rules.include?(rule_id))
71
71
  end
72
72
  end
@@ -74,35 +74,30 @@ module Contrast
74
74
  # If an assess INPUT exclusion rule applies to the current url, *and* also covers all
75
75
  # rules, then we can avoid tracking this entry.
76
76
  #
77
- # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
78
77
  # @param source_type [String]
79
78
  # @param source_name [String]
80
79
  # return [Boolean]
81
- def assess_excluded_by_input? request, source_type, source_name
82
- request_path = request.path
83
-
80
+ def assess_excluded_by_input?source_type, source_name
84
81
  assess_input_exclusions_for_all_rules.any? do |exclusion_matcher|
85
- input_match?(exclusion_matcher, source_type, source_name) && path_match?(exclusion_matcher, request_path)
82
+ input_match?(exclusion_matcher, source_type, source_name) && path_match?(exclusion_matcher)
86
83
  end
87
84
  end
88
85
 
89
86
  # If an assess INPUT exclusion rule covers the provided rule_id *for all finding event sources*, then we
90
87
  # can avoid tracking this entry. If any event source *isn't excluded* then we don't exclude the finding.
91
88
  #
92
- # @param request [Contrast::Agent::Request] a wrapper around the Rack::Request for the current request
93
89
  # @param finding [Contrast::Agent::Reporting::Finding]
94
90
  # @param rule [String]
95
91
  # return [Boolean]
96
- def assess_excluded_by_input_and_rule? request, finding, rule
92
+ def assess_excluded_by_input_and_rule?finding, rule
97
93
  return false if finding.events.empty?
98
94
 
99
95
  # We need to check for url exclusions here for the input rules as the url exclusions
100
96
  # that have already been checked didn't include the INPUT exclusions. So we look for
101
97
  # any INPUT exclusions that apply to the current url and the supplied rule.
102
- path = request.path
103
98
  rule_input_exclusions = assess_input_exclusions.select do |exclusion_matcher|
104
99
  (exclusion_matcher.protect_rules.empty? || exclusion_matcher.protect_rules.include?(rule)) &&
105
- path_match?(exclusion_matcher, path)
100
+ path_match?(exclusion_matcher)
106
101
  end
107
102
  return false if rule_input_exclusions.empty?
108
103
 
@@ -122,13 +117,12 @@ module Contrast
122
117
  # then we can avoid using the rule for the request.
123
118
  #
124
119
  # @param rule_id [String]
125
- # @param path [String]
126
120
  # return [Boolean]
127
- def protect_excluded_by_url? rule_id, path
121
+ def protect_excluded_by_url? rule_id
128
122
  protect_url_exclusions.any? do |exclusion_matcher|
129
123
  next unless exclusion_matcher.protection_rule?(rule_id)
130
124
 
131
- return true if path_match?(exclusion_matcher, path)
125
+ return true if path_match?(exclusion_matcher)
132
126
  end
133
127
  end
134
128
 
@@ -140,14 +134,13 @@ module Contrast
140
134
  #
141
135
  # @param exclusion_match [Contrast::Agent::ExclusionMatcher]
142
136
  # @param input_match [Boolean] does the input match the exclusion
143
- # @param request_path [String] Current request path
144
137
  # @return [Boolean]
145
- def input_match_strategy exclusion_match, input_match, request_path
138
+ def input_match_strategy exclusion_match, input_match
146
139
  # for ALL urls
147
140
  return input_match if exclusion_match.match_all?
148
141
 
149
142
  # for ONLY match we need to check if there is an input and url match.
150
- input_match && path_match?(exclusion_match, request_path)
143
+ input_match && path_match?(exclusion_match)
151
144
  end
152
145
 
153
146
  # @return [Array<Contrast::Agent::ExclusionMatcher>]
@@ -202,12 +195,25 @@ module Contrast
202
195
  end
203
196
  end
204
197
 
198
+ # Returns true if context.request.path matches any url exclusion.
199
+ #
205
200
  # @return [Boolean]
206
- def path_match? exclusion_matcher, path
207
- return false unless path
201
+ def path_match? exclusion_matcher
202
+ return false unless Contrast::Agent::REQUEST_TRACKER.current&.request&.path
203
+
204
+ return_cached_result(exclusion_matcher)
205
+ matches = 0
206
+ matches += 1 if exclusion_matcher.wildcard_url
207
+ exclusion_matcher.urls.any? do |url|
208
+ if url.match?(Contrast::Agent::REQUEST_TRACKER.current.request.path) ||
209
+ regexp_match?(url, Contrast::Agent::REQUEST_TRACKER.current.request.path)
210
+
211
+ matches += 1
212
+ end
213
+ end
208
214
 
209
- exclusion_matcher.wildcard_url ||
210
- exclusion_matcher.urls.any? { |url| url.match?(path) || regexp_match?(url, path) }
215
+ add_cached_path(exclusion_matcher, matches)
216
+ matches.positive?
211
217
  end
212
218
 
213
219
  # @param exclusion [Contrast::Agent::ExclusionMatcher]
@@ -301,6 +307,33 @@ module Contrast
301
307
  def cookie_types
302
308
  @_cookie_types ||= [COOKIE_NAME, COOKIE_VALUE].cs__freeze
303
309
  end
310
+
311
+ # Adds new cached result unless it already exists
312
+ #
313
+ # @param exclusion_matcher [Contrast::Agent::ExclusionMatcher]
314
+ # @param matches [Boolean] the result of last iteration
315
+ # @return [Hash, nil]
316
+ def add_cached_path exclusion_matcher, matches
317
+ return if cached_paths[Contrast::Agent::REQUEST_TRACKER.current.request.path.__id__]
318
+
319
+ cached_paths[Contrast::Agent::REQUEST_TRACKER.
320
+ current.request.path.__id__] = { matcher: exclusion_matcher.__id__, result: matches.positive? }
321
+ rescue StandardError
322
+ nil
323
+ end
324
+
325
+ # returns a cached result if current path and matcher are the same.
326
+ # @param exclusion_matcher [Contrast::Agent::ExclusionMatcher]
327
+ # @return [Boolean, nil]
328
+ def return_cached_result exclusion_matcher
329
+ return unless !cached_paths[Contrast::Agent::REQUEST_TRACKER.current.request.path.__id__].nil? &&
330
+ (cached_paths[Contrast::Agent::REQUEST_TRACKER.current.
331
+ request.path.__id__][:matcher] == exclusion_matcher.__id__)
332
+
333
+ cached_paths[Contrast::Agent::REQUEST_TRACKER.current.request.path.__id__][:result]
334
+ rescue StandardError
335
+ nil
336
+ end
304
337
  end
305
338
  end
306
339
  end
@@ -166,17 +166,15 @@ module Contrast
166
166
  # Check if the protect rules is excluded by url from the exclusion rules for this application.
167
167
  #
168
168
  # @param rule_id [String]
169
- # @param request_path [String] Current request path
170
- def protect_excluded_by_url? rule_id, request_path
171
- Contrast::SETTINGS.excluder.protect_excluded_by_url?(rule_id, request_path)
169
+ def protect_excluded_by_url? rule_id
170
+ Contrast::SETTINGS.excluder.protect_excluded_by_url?(rule_id)
172
171
  end
173
172
 
174
173
  # Check if the protect rules is excluded by input from the exclusion rules for this application.
175
174
  #
176
175
  # @param results [Array<Contrast::Agent::Reporting::InputAnalysis>]
177
- # @param request_path [String] Current request path
178
- def protect_excluded_by_input? results, request_path
179
- Contrast::SETTINGS.excluder.protect_excluded_by_input?(results, request_path)
176
+ def protect_excluded_by_input? results
177
+ Contrast::SETTINGS.excluder.protect_excluded_by_input?(results)
180
178
  end
181
179
 
182
180
  # Allows for the InputAnalysis from Agent Library to be extracted early
@@ -77,7 +77,7 @@ module Contrast
77
77
  # @return [Contrast::Agent::Reporting::RaspRuleSample]
78
78
  def build_sample context, ia_result, _candidate_string, **_kwargs
79
79
  sample = build_base_sample(context, ia_result)
80
- sample.details = Contrast::Agent::Reporting::BotBlockerDetails.new
80
+ sample.details = Contrast::Agent::Reporting::Details::BotBlockerDetails.new
81
81
  sample.details.bot = ia_result.value
82
82
  sample.details.user_agent = context&.request&.user_agent
83
83
  sample
@@ -3,7 +3,7 @@
3
3
 
4
4
  require 'contrast/agent/reporting/input_analysis/input_type'
5
5
  require 'contrast/agent/reporting/input_analysis/score_level'
6
- require 'contrast/agent/reporting/input_analysis/details/bot_blocker_details'
6
+ require 'contrast/agent/reporting/details/bot_blocker_details'
7
7
  require 'contrast/utils/input_classification_base'
8
8
  require 'contrast/utils/object_share'
9
9
 
@@ -73,7 +73,7 @@ module Contrast
73
73
  if score >= THRESHOLD
74
74
  ia_result.score_level = DEFINITEATTACK
75
75
  ia_result.ids << BOT_BLOCKER_MATCH
76
- ia_result.details = Contrast::Agent::Reporting::BotBlockerDetails.new
76
+ ia_result.details = Contrast::Agent::Reporting::Details::BotBlockerDetails.new
77
77
  # details:
78
78
  add_details(ia_result, value)
79
79
  else
@@ -37,7 +37,7 @@ module Contrast
37
37
  # @raise [Contrast::SecurityException] if the rule mode ise set
38
38
  # to BLOCK and valid cdmi is detected.
39
39
  def infilter context, classname, method, command
40
- return if protect_excluded_by_url?(rule_name, context.request.path)
40
+ return if protect_excluded_by_url?(rule_name)
41
41
  return unless backdoors_match?(command)
42
42
  return unless (result = build_attack_with_match(context, nil, nil, command,
43
43
  **{ classname: classname, method: method }))
@@ -43,7 +43,7 @@ module Contrast
43
43
  # to BLOCK and valid cdmi is detected.
44
44
  def infilter context, classname, method, command
45
45
  return unless infilter?(command)
46
- return if protect_excluded_by_url?(rule_name, context.request.path)
46
+ return if protect_excluded_by_url?(rule_name)
47
47
  return unless (result = build_violation(context, command))
48
48
 
49
49
  append_to_activity(context, result)
@@ -58,9 +58,9 @@ module Contrast
58
58
  # Per the spec, this rule applies regardless of input. Only the mode
59
59
  # of the rule and code exclusions apply at this point.
60
60
  # @return [Boolean] should the rule apply to this call.
61
- def infilter? context
61
+ def infilter?_context
62
62
  return false unless enabled?
63
- return false if protect_excluded_by_url?(rule_name, context.request.path)
63
+ return false if protect_excluded_by_url?(rule_name)
64
64
 
65
65
  true
66
66
  end
@@ -46,7 +46,7 @@ module Contrast
46
46
  # @raise [Contrast::SecurityException] if the rule mode ise set
47
47
  # to BLOCK and valid cdmi is detected.
48
48
  def infilter context, method, path
49
- return if protect_excluded_by_url?(rule_name, context.request.path)
49
+ return if protect_excluded_by_url?(rule_name)
50
50
  return unless rule_violated?(path)
51
51
 
52
52
  result = build_violation(context, path)
@@ -19,7 +19,7 @@ module Contrast
19
19
  end
20
20
 
21
21
  def infilter context, sql_query
22
- return false if protect_excluded_by_url?(rule_name, context.request.path)
22
+ return false if protect_excluded_by_url?(rule_name)
23
23
  return unless violated?(sql_query)
24
24
 
25
25
  result = build_violation(context, sql_query)
@@ -42,8 +42,8 @@ module Contrast
42
42
  return false unless context
43
43
  return false unless enabled?
44
44
  return false unless (results = gather_ia_results(context)) && results.any?
45
- return false if protect_excluded_by_url?(rule_name, context.request.path)
46
- return false if protect_excluded_by_input?(results, context.request.path)
45
+ return false if protect_excluded_by_url?(rule_name)
46
+ return false if protect_excluded_by_input?(results)
47
47
 
48
48
  true
49
49
  end
@@ -68,8 +68,8 @@ module Contrast
68
68
  def infilter? context
69
69
  return false unless enabled?
70
70
  return false unless (results = gather_ia_results(context)) && results.any?
71
- return false if protect_excluded_by_url?(rule_name, context.request.path)
72
- return false if protect_excluded_by_input?(results, context.request.path)
71
+ return false if protect_excluded_by_url?(rule_name)
72
+ return false if protect_excluded_by_input?(results)
73
73
 
74
74
  true
75
75
  end
@@ -89,8 +89,8 @@ module Contrast
89
89
  # @raise [Contrast::SecurityException]
90
90
  def postfilter context
91
91
  return unless enabled? && POSTFILTER_MODES.include?(mode)
92
- return false if protect_excluded_by_url?(rule_name, context.request.path)
93
- return if protect_excluded_by_input?(gather_ia_results(context), context.request.path)
92
+ return false if protect_excluded_by_url?(rule_name)
93
+ return if protect_excluded_by_input?(gather_ia_results(context))
94
94
 
95
95
  return if mode == :NO_ACTION || mode == :PERMIT
96
96
 
@@ -37,7 +37,7 @@ module Contrast
37
37
  # @raise [Contrast::SecurityException] Security exception if an XXE
38
38
  # attack is found and the rule is in block mode.
39
39
  def infilter context, framework, xml
40
- return if protect_excluded_by_url?(rule_name, context.request.path)
40
+ return if protect_excluded_by_url?(rule_name)
41
41
 
42
42
  result = find_attacker(context, xml, framework: framework)
43
43
  return unless result