11require 'concurrent'
22require 'json'
33require 'base64'
4+ require 'uri'
45require 'perimeterx/configuration'
56require 'perimeterx/utils/px_logger'
67require 'perimeterx/utils/px_constants'
1011require 'perimeterx/internal/clients/perimeter_x_activity_client'
1112require 'perimeterx/internal/validators/perimeter_x_s2s_validator'
1213require 'perimeterx/internal/validators/perimeter_x_cookie_validator'
13- require 'perimeterx/internal/validators/perimeter_x_captcha_validator'
1414
1515module PxModule
1616 # Module expose API
@@ -25,27 +25,58 @@ def px_verify_request
2525 return instance_exec ( px_ctx , &px_config [ :custom_verification_handler ] )
2626 end
2727
28- # Invalidate _pxCaptcha, can be done only on the controller level
29- cookies [ :_pxCaptcha ] = { value : "" , expires : -1 . minutes . from_now }
30-
31- unless px_ctx . nil? || px_ctx . context [ :verified ]
28+ unless px_ctx . nil? || px_ctx . context [ :verified ] || ( px_config [ :module_mode ] == PxModule ::MONITOR_MODE && !px_ctx . context [ :should_bypass_monitor ] )
3229 # In case custom block handler exists (soon to be deprecated)
3330 if px_config . key? ( :custom_block_handler )
3431 px_config [ :logger ] . debug ( "#{ msg_title } : custom_block_handler triggered" )
3532 px_config [ :logger ] . debug (
3633 "#{ msg_title } : Please note that custom_block_handler is deprecated. Use custom_verification_handler instead." )
3734 return instance_exec ( px_ctx , &px_config [ :custom_block_handler ] )
3835 else
39- # Generate template
40- px_config [ :logger ] . debug ( "#{ msg_title } : sending default block page" )
41- html = PxTemplateFactory . get_template ( px_ctx , px_config )
42- response . headers [ 'Content-Type' ] = 'text/html'
43- response . status = 403
36+ if px_ctx . context [ :block_action ] == 'rate_limit'
37+ px_config [ :logger ] . debug ( "#{ msg_title } : sending rate limit page" )
38+ response . status = 429
39+ else
40+ px_config [ :logger ] . debug ( "#{ msg_title } : sending default block page" )
41+ response . status = 403
42+ end
43+
44+ is_mobile = px_ctx . context [ :cookie_origin ] == 'header' ? '1' : '0'
45+ action = px_ctx . context [ :block_action ] [ 0 , 1 ]
46+
47+ px_template_object = {
48+ block_script : "//#{ PxModule ::CAPTCHA_HOST } /#{ px_config [ :app_id ] } /captcha.js?a=#{ action } &u=#{ px_ctx . context [ :uuid ] } &v=#{ px_ctx . context [ :vid ] } &m=#{ is_mobile } " ,
49+ js_client_src : "//#{ PxModule ::CLIENT_HOST } /#{ px_config [ :app_id ] } /main.min.js"
50+ }
51+
52+ html = PxTemplateFactory . get_template ( px_ctx , px_config , px_template_object )
53+
4454 # Web handler
4555 if px_ctx . context [ :cookie_origin ] == 'cookie'
46- px_config [ :logger ] . debug ( '#{msg_title}: web block' )
47- response . headers [ 'Content-Type' ] = 'text/html'
48- render :html => html
56+
57+ accept_header_value = request . headers [ 'accept' ] || request . headers [ 'content-type' ] ;
58+ is_json_response = px_ctx . context [ :block_action ] != 'rate_limit' && accept_header_value && accept_header_value . split ( ',' ) . select { |e | e . downcase . include? 'application/json' } . length > 0 ;
59+
60+ if ( is_json_response )
61+ px_config [ :logger ] . debug ( "#{ msg_title } : advanced blocking response response" )
62+ response . headers [ 'Content-Type' ] = 'application/json'
63+
64+ hash_json = {
65+ :appId => px_config [ :app_id ] ,
66+ :jsClientSrc => px_template_object [ :js_client_src ] ,
67+ :firstPartyEnabled => false ,
68+ :uuid => px_ctx . context [ :uuid ] ,
69+ :vid => px_ctx . context [ :vid ] ,
70+ :hostUrl => "https://collector-#{ px_config [ :app_id ] } .perimeterx.net" ,
71+ :blockScript => px_template_object [ :block_script ] ,
72+ }
73+
74+ render :json => hash_json
75+ else
76+ px_config [ :logger ] . debug ( '#{msg_title}: web block' )
77+ response . headers [ 'Content-Type' ] = 'text/html'
78+ render :html => html
79+ end
4980 else # Mobile SDK
5081 px_config [ :logger ] . debug ( "#{ msg_title } : mobile sdk block" )
5182 response . headers [ 'Content-Type' ] = 'application/json'
@@ -99,19 +130,27 @@ def self.instance
99130 #Instance Methods
100131 def verify ( env )
101132 begin
133+
134+ # check module_enabled
102135 @logger . debug ( 'PerimeterX[pxVerify]' )
103136 if !@px_config [ :module_enabled ]
104137 @logger . warn ( 'Module is disabled' )
105138 return nil
106139 end
107- req = ActionDispatch ::Request . new ( env )
108- px_ctx = PerimeterXContext . new ( @px_config , req )
109140
110- # Captcha phase
111- captcha_verified , px_ctx = @px_captcha_validator . verify ( px_ctx )
112- if captcha_verified
113- return handle_verification ( px_ctx )
141+ req = ActionDispatch ::Request . new ( env )
142+
143+ # filter whitelist routes
144+ url_path = URI . parse ( req . original_url ) . path
145+ if url_path && !url_path . empty?
146+ if check_whitelist_routes ( px_config [ :whitelist_routes ] , url_path )
147+ @logger . debug ( "PerimeterX[pxVerify]: whitelist route: #{ url_path } " )
148+ return nil
149+ end
114150 end
151+
152+ # create context
153+ px_ctx = PerimeterXContext . new ( @px_config , req )
115154
116155 # Cookie phase
117156 cookie_verified , px_ctx = @px_cookie_validator . verify ( px_ctx )
@@ -136,15 +175,16 @@ def verify(env)
136175
137176 @px_cookie_validator = PerimeterxCookieValidator . new ( @px_config )
138177 @px_s2s_validator = PerimeterxS2SValidator . new ( @px_config , @px_http_client )
139- @px_captcha_validator = PerimeterxCaptchaValidator . new ( @px_config , @px_http_client )
140- @logger . debug ( 'PerimeterX[initialize]Z' )
178+ @logger . debug ( 'PerimeterX[initialize]' )
141179 end
142180
143181 private def handle_verification ( px_ctx )
144182 @logger . debug ( 'PerimeterX[handle_verification]' )
145183 @logger . debug ( "PerimeterX[handle_verification]: processing ended - score:#{ px_ctx . context [ :score ] } , uuid:#{ px_ctx . context [ :uuid ] } " )
146184
147185 score = px_ctx . context [ :score ]
186+ px_ctx . context [ :should_bypass_monitor ] = @px_config [ :bypass_monitor_header ] && px_ctx . context [ :headers ] [ @px_config [ :bypass_monitor_header ] . to_sym ] == '1' ;
187+
148188 px_ctx . context [ :verified ] = score < @px_config [ :blocking_score ]
149189 # Case PASS request
150190 if px_ctx . context [ :verified ]
@@ -157,8 +197,8 @@ def verify(env)
157197 @px_activity_client . send_block_activity ( px_ctx )
158198
159199 # In case were in monitor mode, end here
160- if @px_config [ :module_mode ] == PxModule ::MONITOR_MODE
161- @logger . debug ( ' PerimeterX[handle_verification]: monitor mode is on, passing request' )
200+ if @px_config [ :module_mode ] == PxModule ::MONITOR_MODE && ! px_ctx . context [ :should_bypass_monitor ]
201+ @logger . debug ( " PerimeterX[handle_verification]: monitor mode is on, passing request" )
162202 return px_ctx
163203 end
164204
@@ -167,6 +207,18 @@ def verify(env)
167207 return px_ctx
168208 end
169209
210+ private def check_whitelist_routes ( whitelist_routes , path )
211+ whitelist_routes . each do |whitelist_route |
212+ if whitelist_route . is_a? ( Regexp ) && path . match ( whitelist_route )
213+ return true
214+ end
215+ if whitelist_route . is_a? ( String ) && path . start_with? ( whitelist_route )
216+ return true
217+ end
218+ end
219+ false
220+ end
221+
170222 private_class_method :new
171223 end
172224end
0 commit comments