perimeter_x 1.1.0 → 2.1.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.
Files changed (33) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -0
  4. data/Dockerfile +19 -43
  5. data/Gemfile.lock +36 -30
  6. data/Rakefile +1 -0
  7. data/changelog.md +63 -0
  8. data/examples/app/controllers/home_controller.rb +1 -1
  9. data/lib/perimeter_x.rb +152 -70
  10. data/lib/perimeterx/configuration.rb +71 -22
  11. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +36 -15
  12. data/lib/perimeterx/internal/exceptions/px_config_exception.rb +6 -0
  13. data/lib/perimeterx/internal/{perimeter_x_cookie_v1.rb → payload/perimeter_x_cookie_v1.rb} +1 -1
  14. data/lib/perimeterx/internal/{perimeter_x_cookie_v3.rb → payload/perimeter_x_cookie_v3.rb} +1 -1
  15. data/lib/perimeterx/internal/{perimeter_x_cookie.rb → payload/perimeter_x_payload.rb} +12 -4
  16. data/lib/perimeterx/internal/payload/perimeter_x_token_v1.rb +38 -0
  17. data/lib/perimeterx/internal/payload/perimeter_x_token_v3.rb +36 -0
  18. data/lib/perimeterx/internal/perimeter_x_context.rb +66 -31
  19. data/lib/perimeterx/internal/validators/hash_schema_validator.rb +26 -0
  20. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +29 -21
  21. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +34 -10
  22. data/lib/perimeterx/utils/px_constants.rb +35 -17
  23. data/lib/perimeterx/utils/px_http_client.rb +29 -35
  24. data/lib/perimeterx/utils/px_template_factory.rb +18 -8
  25. data/lib/perimeterx/utils/templates/block_template.mustache +175 -0
  26. data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
  27. data/lib/perimeterx/version.rb +1 -1
  28. data/perimeter_x.gemspec +5 -4
  29. data/readme.md +95 -32
  30. metadata +53 -24
  31. data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +0 -65
  32. data/lib/perimeterx/utils/templates/block.mustache +0 -146
  33. data/lib/perimeterx/utils/templates/captcha.mustache +0 -185
@@ -0,0 +1,26 @@
1
+ def validate_hash_schema(hash, schema)
2
+ hash.each do |key, value|
3
+ if schema.key?(key) && value != nil
4
+ # validate value types in hash are according to schema
5
+ if !schema[key][:types].include?(value.class)
6
+ raise PxConfigurationException.new("PerimeterX: Type of #{key} should be one of #{schema[key][:types]} but instead is #{value.class}")
7
+ end
8
+
9
+ # validate arrays elments types are according to schema
10
+ if value.class == Array
11
+ value.each do |element|
12
+ if !schema[key][:allowed_element_types].include?(element.class)
13
+ raise PxConfigurationException.new("PerimeterX: #{key} may only contain elements of the following types: #{schema[key][:allowed_element_types]} but includes element of type #{element.class}")
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # validate required fields exist in hash
21
+ schema.each do |key, value|
22
+ if value[:required] && hash[key].nil?
23
+ raise PxConfigurationException.new("PerimeterX: #{key} configuration is missing")
24
+ end
25
+ end
26
+ end
@@ -1,7 +1,9 @@
1
1
  require 'perimeterx/utils/px_constants'
2
- require 'perimeterx/internal/perimeter_x_cookie'
3
- require 'perimeterx/internal/perimeter_x_cookie_v1'
4
- require 'perimeterx/internal/perimeter_x_cookie_v3'
2
+ require 'perimeterx/internal/payload/perimeter_x_payload'
3
+ require 'perimeterx/internal/payload/perimeter_x_token_v1'
4
+ require 'perimeterx/internal/payload/perimeter_x_token_v3'
5
+ require 'perimeterx/internal/payload/perimeter_x_cookie_v1'
6
+ require 'perimeterx/internal/payload/perimeter_x_cookie_v3'
5
7
 
6
8
  module PxModule
7
9
  class PerimeterxCookieValidator
@@ -16,57 +18,63 @@ module PxModule
16
18
 
17
19
  def verify(px_ctx)
18
20
  begin
19
- # Case no cookie
20
21
  if px_ctx.context[:px_cookie].empty?
21
- @logger.warn("PerimeterxCookieValidator:[verify]: cookie not found")
22
+ @logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
22
23
  px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
23
24
  return false, px_ctx
24
25
  end
25
-
26
+
26
27
  # Deserialize cookie start
27
- cookie = PerimeterxCookie.px_cookie_factory(px_ctx, @px_config)
28
- if (!cookie.deserialize())
28
+ cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
29
+ if !cookie.deserialize()
29
30
  @logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
31
+ px_ctx.context[:px_orig_cookie] = px_ctx.get_px_cookie
30
32
  px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
31
33
  return false, px_ctx
32
34
  end
33
35
  px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
34
36
  px_ctx.context[:score] = cookie.cookie_score()
35
37
  px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
36
- px_ctx.context[:vid] = cookie.decoded_cookie[:v]
37
38
  px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
38
39
  px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
39
40
 
40
- if (cookie.expired?)
41
+ vid = cookie.decoded_cookie[:v]
42
+ if vid.is_a?(String) && vid.match(PxModule::VID_REGEX)
43
+ px_ctx.context[:vid_source] = "risk_cookie"
44
+ px_ctx.context[:vid] = vid
45
+ end
46
+
47
+ if cookie.expired?
41
48
  @logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
42
49
  px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
43
50
  return false, px_ctx
44
51
  end
45
52
 
46
- if (cookie.high_score?)
53
+ if cookie.high_score?
47
54
  @logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
48
- px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
55
+ px_ctx.context[:blocking_reason] = 'cookie_high_score'
49
56
  return true, px_ctx
50
57
  end
51
58
 
52
- if (!cookie.secured?)
59
+ if !cookie.secured?
53
60
  @logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
54
- px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
61
+ px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
62
+ return false, px_ctx
63
+ end
64
+
65
+ if px_ctx.context[:sensitive_route]
66
+ @logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
67
+ px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
55
68
  return false, px_ctx
56
69
  end
57
-
58
- if (px_ctx.context[:sensitive_route])
59
- @logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
60
- px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
61
- return false, px_ctx
62
- end
63
70
 
64
71
  @logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
65
72
 
73
+ px_ctx.context[:pass_reason] = 'cookie'
66
74
  return true, px_ctx
67
75
  rescue Exception => e
68
76
  @logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
69
- px_ctx.context[:px_orig_cookie] = cookie.px_cookie
77
+ px_ctx.context[:px_orig_cookie] = px_ctx.context[:px_cookie]
70
78
  px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
71
79
  return false, px_ctx
72
80
  end
@@ -5,11 +5,11 @@ module PxModule
5
5
 
6
6
  def initialize(px_config, http_client)
7
7
  super(px_config, http_client)
8
- @logger.debug("PerimeterxS2SValidator[initialize]")
8
+ @logger.debug('PerimeterxS2SValidator[initialize]')
9
9
  end
10
10
 
11
11
  def send_risk_request(px_ctx)
12
- @logger.debug("PerimeterxS2SValidator[send_risk_request]: send_risk_request")
12
+ @logger.debug('PerimeterxS2SValidator[send_risk_request]: send_risk_request')
13
13
 
14
14
  risk_mode = PxModule::RISK_MODE_ACTIVE
15
15
  if @px_config[:module_mode] == PxModule::MONITOR_MODE
@@ -31,6 +31,17 @@ module PxModule
31
31
  :risk_mode => risk_mode
32
32
  }
33
33
  }
34
+
35
+ #Check for cookie_origin
36
+ if !px_ctx.context[:px_cookie].empty?
37
+ request_body[:additional][:cookie_origin] = px_ctx.context[:cookie_origin]
38
+ end
39
+
40
+ #Override s2s_call_reason in case of mobile error
41
+ if !px_ctx.context[:mobile_error].nil?
42
+ request_body[:additional][:s2s_call_reason] = "mobile_error_#{px_ctx.context[:mobile_error]}"
43
+ end
44
+
34
45
  #Check for hmac
35
46
  @logger.debug("px_ctx cookie_hmac key = #{px_ctx.context.key?(:cookie_hmac)}, value is: #{px_ctx.context[:cookie_hmac]}")
36
47
  if px_ctx.context.key?(:cookie_hmac)
@@ -66,11 +77,19 @@ module PxModule
66
77
  };
67
78
 
68
79
  # Custom risk handler
80
+ risk_start = Time.now
69
81
  if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
70
- response = @px_config[:custom_risk_handler].call(PxModule::API_V2_RISK, request_body, headers, @px_config[:api_timeout])
82
+ response = @px_config[:custom_risk_handler].call(PxModule::API_V3_RISK, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
71
83
  else
72
- response = @http_client.post(PxModule::API_V2_RISK , request_body, headers)
84
+ response = @http_client.post(PxModule::API_V3_RISK , request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
85
+ end
86
+
87
+ # Set risk_rtt
88
+ if(response)
89
+ risk_end = Time.now
90
+ px_ctx.context[:risk_rtt] = ((risk_end-risk_start)*1000).round
73
91
  end
92
+
74
93
  return response
75
94
  end
76
95
 
@@ -78,14 +97,15 @@ module PxModule
78
97
  @logger.debug("PerimeterxS2SValidator[verify]")
79
98
  response = send_risk_request(px_ctx)
80
99
  if (!response)
100
+ px_ctx.context[:pass_reason] = "s2s_timeout"
81
101
  return px_ctx
82
102
  end
83
103
  px_ctx.context[:made_s2s_risk_api_call] = true
84
104
 
85
105
  # From here response should be valid, if success or error
86
- response_body = eval(response.content);
106
+ response_body = eval(response.body);
87
107
  # When success
88
- if (response.status == 200 && response_body.key?(:score) && response_body.key?(:action))
108
+ if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
89
109
  @logger.debug("PerimeterxS2SValidator[verify]: response ok")
90
110
  score = response_body[:score]
91
111
  px_ctx.context[:score] = score
@@ -96,14 +116,18 @@ module PxModule
96
116
  px_ctx.context[:blocking_reason] = 'challenge'
97
117
  elsif (score >= @px_config[:blocking_score])
98
118
  px_ctx.context[:blocking_reason] = 's2s_high_score'
119
+ else
120
+ px_ctx.context[:pass_reason] = 's2s'
99
121
  end #end challange or blocking score
100
122
  end #end success response
101
123
 
102
124
  # When error
103
- if(response.status != 200)
104
- @logger.warn("PerimeterxS2SValidator[verify]: bad response, return code #{response.code}")
105
- px_ctx.context[:uuid] = ""
106
- px_ctx.context[:s2s_error_msg] = response_body[:message]
125
+ risk_error_status = response_body && response_body.key?(:status) && response_body[:status] == -1
126
+ if(response.code != 200 || risk_error_status)
127
+ @logger.warn("PerimeterxS2SValidator[verify]: bad response, returned code #{response.code} #{risk_error_status ? "risk status: -1" : ""}")
128
+ px_ctx.context[:pass_reason] = 'request_failed'
129
+ px_ctx.context[:uuid] = (!response_body || response_body[:uuid].nil?)? "" : response_body[:uuid]
130
+ px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
107
131
  end
108
132
 
109
133
  @logger.debug("PerimeterxS2SValidator[verify]: done")
@@ -4,33 +4,34 @@ module PxModule
4
4
  # Misc
5
5
  MONITOR_MODE = 1
6
6
  ACTIVE_MODE = 2
7
- RISK_MODE_ACTIVE = "active_blocking"
8
- RISK_MODE_MONITOR = "monitor"
7
+ RISK_MODE_ACTIVE = 'active_blocking'
8
+ RISK_MODE_MONITOR = 'monitor'
9
9
  SDK_NAME = "RUBY SDK v#{PxModule::VERSION}"
10
10
 
11
11
  # Routes
12
- API_V1_S2S = "/api/v1/collector/s2s"
13
- API_V1_CAPTCHA = "/api/v1/risk/captcha"
14
- API_V2_RISK = "/api/v2/risk"
12
+ API_V1_S2S = '/api/v1/collector/s2s'
13
+ API_V3_RISK = '/api/v3/risk'
15
14
 
16
15
  # Activity Types
17
- BLOCK_ACTIVITY = "block"
18
- PAGE_REQUESTED_ACTIVITY = "page_requested"
16
+ BLOCK_ACTIVITY = 'block'
17
+ PAGE_REQUESTED_ACTIVITY = 'page_requested'
19
18
 
20
19
  # PxContext
21
- NO_COOKIE = "no_cookie"
22
- INVALID_COOKIE = "invalid_cookie"
23
- EXPIRED_COOKIE = "cookie_expired"
24
- COOKIE_HIGH_SCORE = "cookie_high_score"
25
- COOKIE_VALIDATION_FAILED = "cookie_validation_failed"
26
- COOKIE_DECRYPTION_FAILED = "cookie_decryption_failed"
27
- SENSITIVE_ROUTE = "sensitive_route"
20
+ NO_COOKIE = 'no_cookie'
21
+ INVALID_COOKIE = 'invalid_cookie'
22
+ EXPIRED_COOKIE = 'cookie_expired'
23
+ COOKIE_HIGH_SCORE = 'cookie_high_score'
24
+ COOKIE_VALIDATION_FAILED = 'cookie_validation_failed'
25
+ COOKIE_DECRYPTION_FAILED = 'cookie_decryption_failed'
26
+ SENSITIVE_ROUTE = 'sensitive_route'
28
27
 
29
28
  # Templates
30
- BLOCK_TEMPLATE = "block.mustache"
31
- CAPTCHA_TEMPLATE = "captcha.mustache"
29
+ CHALLENGE_TEMPLATE = 'block_template'
30
+ TEMPLATE_EXT = '.mustache'
31
+ RATELIMIT_TEMPLATE = 'ratelimit'
32
32
 
33
- # Tempalte Props
33
+
34
+ # Template Props
34
35
  PROP_REF_ID = :refId
35
36
  PROP_APP_ID = :appId
36
37
  PROP_VID = :vid
@@ -39,7 +40,24 @@ module PxModule
39
40
  PROP_CUSTOM_LOGO = :customLogo
40
41
  PROP_CSS_REF = :cssRef
41
42
  PROP_JS_REF = :jsRef
43
+ PROP_BLOCK_SCRIPT = :blockScript
44
+ PROP_JS_CLIENT_SRC = :jsClientSrc
45
+ PROP_HOST_URL = :hostUrl
46
+ PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
47
+
48
+ # Hosts
49
+ CLIENT_HOST = 'client.px-cloud.net'
50
+ CAPTCHA_HOST = 'captcha.px-cloud.net'
42
51
 
43
52
  VISIBLE = 'visible'
44
53
  HIDDEN = 'hidden'
54
+
55
+ # Mobile SDK
56
+ TOKEN_HEADER = 'X-PX-AUTHORIZATION'
57
+ ORIGINAL_TOKEN_HEADER = 'X-PX-ORIGINAL-TOKEN'
58
+
59
+ # Regular Expressions
60
+ VID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
61
+ MOBILE_TOKEN_V3_REGEX = /\A3:(.*)\z/
62
+ MOBILE_ERROR_REGEX = /\A[0-9]\z/
45
63
  end
@@ -1,53 +1,47 @@
1
- require "perimeterx/utils/px_logger"
2
- require "httpclient"
1
+ require 'perimeterx/utils/px_logger'
2
+ require 'typhoeus'
3
+ require 'concurrent'
3
4
 
4
5
  module PxModule
5
6
  class PxHttpClient
7
+ include Concurrent::Async
8
+
6
9
  attr_accessor :px_config
7
- attr_accessor :BASE_URL
8
- attr_accessor :http_client
10
+ attr_accessor :px_client
9
11
 
10
12
  def initialize(px_config)
11
13
  @px_config = px_config
12
- @http_client = HTTPClient.new(:base_url => px_config[:perimeterx_server_host])
13
14
  @logger = px_config[:logger]
14
- @logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:perimeterx_server_host]}")
15
+ @logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:backend_url]}")
15
16
  end
16
17
 
17
- def post(path, body, headers, api_timeout = 0, timeoute = 0)
18
- s = Time.now
19
- begin
20
- @logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
21
- response = @http_client.post(path,
22
- :header => headers,
23
- :body => body.to_json(),
24
- :timeout => api_timeout
25
- )
26
- rescue Net::OpenTimeout, Net::ReadTimeout => error
27
- @logger.warn("PerimeterxS2SValidator[verify]: request timedout")
28
- return false
29
- end
30
- e = Time.now
31
- @logger.debug("PxHttpClient[post]: runtime: #{e-s}")
32
- return response
33
- end
18
+ # Runs a POST command to Perimeter X servers
19
+ # Params:
20
+ # +path+:: string containing uri
21
+ # +body+:: hash object, containing the request body, must be converted to json format
22
+ # +headers+:: hash object, hold headers
23
+ # +api_timeout+:: int, sets the timeout for a request
24
+ # +connection_timeout+:: int, sets the timeout for opening a connection
34
25
 
35
- def async_post(path, body, headers, api_timeout = 0, timeoute = 0)
36
- @logger.debug("PxHttpClient[async_post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
26
+ def post(path, body, headers, api_timeout = 1, connection_timeout = 1)
37
27
  s = Time.now
38
28
  begin
39
29
  @logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
40
- response = @http_client.post_async(path,
41
- :header => headers,
42
- :body => body.to_json(),
43
- :timeout => api_timeout
44
- )
45
- rescue Net::OpenTimeout, Net::ReadTimeout => error
46
- @logger.warn("PerimeterxS2SValidator[verify]: request timedout")
47
- return false
30
+ response = Typhoeus.post(
31
+ "#{px_config[:backend_url]}#{path}",
32
+ headers: headers,
33
+ body: body.to_json,
34
+ timeout: api_timeout,
35
+ connecttimeout: connection_timeout
36
+ )
37
+ if response.timed_out?
38
+ @logger.warn('PerimeterxS2SValidator[verify]: request timed out')
39
+ return false
40
+ end
41
+ ensure
42
+ e = Time.now
43
+ @logger.debug("PxHttpClient[post]: runtime: #{(e-s) * 1000.0}")
48
44
  end
49
- e = Time.now
50
- @logger.debug("PxHttpClient[post]: runtime: #{e-s}")
51
45
  return response
52
46
  end
53
47
 
@@ -3,19 +3,25 @@ require 'perimeterx/utils/px_constants'
3
3
  module PxModule
4
4
  module PxTemplateFactory
5
5
 
6
- def self.get_template(px_ctx, px_config)
6
+ def self.get_template(px_ctx, px_config, px_template_object)
7
7
  logger = px_config[:logger]
8
- if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == "challenge")
9
- logger.debug("PxTemplateFactory[get_template]: px challange triggered")
8
+ if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
9
+ logger.debug('PxTemplateFactory[get_template]: px challange triggered')
10
10
  return px_ctx.context[:block_action_data].html_safe
11
11
  end
12
12
 
13
- logger.debug("PxTemplateFactory[get_template]: rendering template")
14
- template_type = px_config[:captcha_enabled] ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
15
-
16
- Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}"
17
13
  view = Mustache.new
18
14
 
15
+ if (px_ctx.context[:block_action] == 'rate_limit')
16
+ logger.debug('PxTemplateFactory[get_template]: rendering ratelimit template')
17
+ template_type = RATELIMIT_TEMPLATE
18
+ else
19
+ logger.debug('PxTemplateFactory[get_template]: rendering template')
20
+ template_type = CHALLENGE_TEMPLATE
21
+ end
22
+
23
+ Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
24
+
19
25
  view[PxModule::PROP_APP_ID] = px_config[:app_id]
20
26
  view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
21
27
  view[PxModule::PROP_VID] = px_ctx.context[:vid]
@@ -23,9 +29,13 @@ module PxModule
23
29
  view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
24
30
  view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
25
31
  view[PxModule::PROP_JS_REF] = px_config[:js_ref]
32
+ view[PxModule::PROP_HOST_URL] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
26
33
  view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
34
+ view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
35
+ view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
36
+ view[PxModule::PROP_FIRST_PARTY_ENABLED] = false
27
37
 
28
38
  return view.render.html_safe
29
39
  end
30
40
  end #end class
31
- end #end module
41
+ end #end module
@@ -0,0 +1,175 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Access to this page has been denied.</title>
7
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
8
+ <style>
9
+ html, body {
10
+ margin: 0;
11
+ padding: 0;
12
+ font-family: 'Open Sans', sans-serif;
13
+ color: #000;
14
+ }
15
+
16
+ a {
17
+ color: #c5c5c5;
18
+ text-decoration: none;
19
+ }
20
+
21
+ .container {
22
+ align-items: center;
23
+ display: flex;
24
+ flex: 1;
25
+ justify-content: space-between;
26
+ flex-direction: column;
27
+ height: 100%;
28
+ }
29
+
30
+ .container > div {
31
+ width: 100%;
32
+ display: flex;
33
+ justify-content: center;
34
+ }
35
+
36
+ .container > div > div {
37
+ display: flex;
38
+ width: 80%;
39
+ }
40
+
41
+ .customer-logo-wrapper {
42
+ padding-top: 2rem;
43
+ flex-grow: 0;
44
+ background-color: #fff;
45
+ visibility: {{logoVisibility}};
46
+ }
47
+
48
+ .customer-logo {
49
+ border-bottom: 1px solid #000;
50
+ }
51
+
52
+ .customer-logo > img {
53
+ padding-bottom: 1rem;
54
+ max-height: 50px;
55
+ max-width: 100%;
56
+ }
57
+
58
+ .page-title-wrapper {
59
+ flex-grow: 2;
60
+ }
61
+
62
+ .page-title {
63
+ flex-direction: column-reverse;
64
+ }
65
+
66
+ .content-wrapper {
67
+ flex-grow: 5;
68
+ }
69
+
70
+ .content {
71
+ flex-direction: column;
72
+ }
73
+
74
+ .page-footer-wrapper {
75
+ align-items: center;
76
+ flex-grow: 0.2;
77
+ background-color: #000;
78
+ color: #c5c5c5;
79
+ font-size: 70%;
80
+ }
81
+
82
+ @media (min-width: 768px) {
83
+ html, body {
84
+ height: 100%;
85
+ }
86
+ }
87
+ </style>
88
+ <!-- Custom CSS -->
89
+ {{#cssRef}}
90
+ <link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
91
+ {{/cssRef}}
92
+ </head>
93
+
94
+ <body>
95
+ <section class="container">
96
+ <div class="customer-logo-wrapper">
97
+ <div class="customer-logo">
98
+ <img src="{{customLogo}}" alt="Logo"/>
99
+ </div>
100
+ </div>
101
+ <div class="page-title-wrapper">
102
+ <div class="page-title">
103
+ <h1>Please verify you are a human</h1>
104
+ </div>
105
+ </div>
106
+ <div class="content-wrapper">
107
+ <div class="content">
108
+
109
+ <div id="px-captcha">
110
+ </div>
111
+ <p>
112
+ Access to this page has been denied because we believe you are using automation tools to browse the
113
+ website.
114
+ </p>
115
+ <p>
116
+ This may happen as a result of the following:
117
+ </p>
118
+ <ul>
119
+ <li>
120
+ Javascript is disabled or blocked by an extension (ad blockers for example)
121
+ </li>
122
+ <li>
123
+ Your browser does not support cookies
124
+ </li>
125
+ </ul>
126
+ <p>
127
+ Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
128
+ them from loading.
129
+ </p>
130
+ <p>
131
+ Reference ID: #{{refId}}
132
+ </p>
133
+ </div>
134
+ </div>
135
+ <div class="page-footer-wrapper">
136
+ <div class="page-footer">
137
+ <p>
138
+ Powered by
139
+ <a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
140
+ , Inc.
141
+ </p>
142
+ </div>
143
+ </div>
144
+ </section>
145
+ <!-- Px -->
146
+ <script>
147
+ window._pxAppId = '{{appId}}';
148
+ window._pxJsClientSrc = '{{{jsClientSrc}}}';
149
+ window._pxFirstPartyEnabled = {{firstPartyEnabled}};
150
+ window._pxVid = '{{vid}}';
151
+ window._pxUuid = '{{uuid}}';
152
+ window._pxHostUrl = '{{{hostUrl}}}';
153
+ </script>
154
+ <script>
155
+ var s = document.createElement('script');
156
+ s.src = '{{{blockScript}}}';
157
+ var p = document.getElementsByTagName('head')[0];
158
+ p.insertBefore(s, null);
159
+ if ({{firstPartyEnabled}}) {
160
+ s.onerror = function () {
161
+ s = document.createElement('script');
162
+ var suffixIndex = '{{{blockScript}}}'.indexOf('captcha.js');
163
+ var temperedBlockScript = '{{{blockScript}}}'.substring(suffixIndex);
164
+ s.src = '//captcha.px-cdn.net/{{appId}}/' + temperedBlockScript;
165
+ p.parentNode.insertBefore(s, p);
166
+ };
167
+ }
168
+ </script>
169
+
170
+ <!-- Custom Script -->
171
+ {{#jsRef}}
172
+ <script src="{{{jsRef}}}"></script>
173
+ {{/jsRef}}
174
+ </body>
175
+ </html>