perimeter_x 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/changelog.md +11 -1
- data/lib/perimeter_x.rb +35 -28
- data/lib/perimeterx/configuration.rb +1 -1
- data/lib/perimeterx/internal/perimeter_x_context.rb +3 -0
- data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +15 -15
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +27 -10
- data/lib/perimeterx/utils/px_constants.rb +3 -2
- data/lib/perimeterx/utils/px_template_factory.rb +1 -1
- data/lib/perimeterx/utils/templates/block.mobile.mustache +2 -2
- data/lib/perimeterx/utils/templates/block.mustache +1 -1
- data/lib/perimeterx/utils/templates/funcaptcha.mobile.mustache +178 -0
- data/lib/perimeterx/utils/templates/funcaptcha.mustache +191 -0
- data/lib/perimeterx/utils/templates/{captcha.mobile.mustache → recaptcha.mobile.mustache} +2 -2
- data/lib/perimeterx/utils/templates/recaptcha.mustache +176 -0
- data/lib/perimeterx/version.rb +1 -1
- data/readme.md +47 -26
- metadata +6 -4
- data/lib/perimeterx/utils/templates/captcha.mustache +0 -185
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3dca9964af5aa39c20643f7e5d6f00271862f0de
|
4
|
+
data.tar.gz: e0f2a81bce3aa7c8b8eb0b0b73d70cc29f272dbc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 257d51330a1dc51e1ca2559559d7eb4e0ca6ebc891fd8bfcdd0171b4e31a5a3b4455f4c3b4227b6db7d403eeecb3bb1bb4b62e4e7cf72024d6efa89f5636bd3f
|
7
|
+
data.tar.gz: 4cd74c725090a46718716b782551e1e5bae7efb939818c1b34970bde070897d6d53334b5c54bb6df7f4a24291dd5ed608e2e39723e008647a07c4e2da8de6b07
|
data/changelog.md
CHANGED
@@ -5,7 +5,17 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
6
6
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
7
7
|
|
8
|
-
## [1.
|
8
|
+
## [1.4.0] - 2018-03-18
|
9
|
+
### Fixed
|
10
|
+
- Incorrect assigment for s2s_call_reason
|
11
|
+
- Fixed empty token result correct s2s reason
|
12
|
+
|
13
|
+
### Added
|
14
|
+
- Added support to captcha api v2
|
15
|
+
- Mobile sdk support for special tokens 1/2/3
|
16
|
+
|
17
|
+
|
18
|
+
## [1.3.0] - 2017-07-27
|
9
19
|
### Added
|
10
20
|
- Sending client_uuid on page_requested activities
|
11
21
|
- Supporting mobile sdk
|
data/lib/perimeter_x.rb
CHANGED
@@ -15,37 +15,47 @@ require 'perimeterx/internal/validators/perimeter_x_captcha_validator'
|
|
15
15
|
module PxModule
|
16
16
|
# Module expose API
|
17
17
|
def px_verify_request
|
18
|
-
|
18
|
+
px_ctx = PerimeterX.instance.verify(request.env)
|
19
|
+
px_config = PerimeterX.instance.px_config
|
20
|
+
msg_title = 'PxModule[px_verify_request]'
|
21
|
+
|
22
|
+
# In case custom verification handler is in use
|
23
|
+
if px_config.key?(:custom_verification_handler)
|
24
|
+
px_config[:logger].debug("#{msg_title}: custom_verification_handler triggered")
|
25
|
+
return instance_exec(px_ctx, &px_config[:custom_verification_handler])
|
26
|
+
end
|
19
27
|
|
20
28
|
# Invalidate _pxCaptcha, can be done only on the controller level
|
21
29
|
cookies[:_pxCaptcha] = {value: "", expires: -1.minutes.from_now}
|
22
30
|
|
23
|
-
unless verified
|
24
|
-
# In case
|
25
|
-
if
|
26
|
-
|
27
|
-
|
31
|
+
unless px_ctx.nil? || px_ctx.context[:verified]
|
32
|
+
# In case custom block handler exists (soon to be deprecated)
|
33
|
+
if px_config.key?(:custom_block_handler)
|
34
|
+
px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
|
35
|
+
px_config[:logger].debug(
|
36
|
+
"#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
|
37
|
+
return instance_exec(px_ctx, &px_config[:custom_block_handler])
|
28
38
|
else
|
29
39
|
# Generate template
|
30
|
-
|
31
|
-
html = PxTemplateFactory.get_template(px_ctx,
|
40
|
+
px_config[:logger].debug("#{msg_title}: sending default block page")
|
41
|
+
html = PxTemplateFactory.get_template(px_ctx, px_config)
|
32
42
|
response.headers['Content-Type'] = 'text/html'
|
33
43
|
response.status = 403
|
34
44
|
# Web handler
|
35
45
|
if px_ctx.context[:cookie_origin] == 'cookie'
|
36
|
-
|
46
|
+
px_config[:logger].debug('#{msg_title}: web block')
|
37
47
|
response.headers['Content-Type'] = 'text/html'
|
38
48
|
render :html => html
|
39
49
|
else # Mobile SDK
|
40
|
-
|
50
|
+
px_config[:logger].debug("#{msg_title}: mobile sdk block")
|
41
51
|
response.headers['Content-Type'] = 'application/json'
|
42
52
|
hash_json = {
|
43
53
|
:action => px_ctx.context[:block_action],
|
44
54
|
:uuid => px_ctx.context[:uuid],
|
45
55
|
:vid => px_ctx.context[:vid],
|
46
|
-
:appId =>
|
56
|
+
:appId => px_config[:app_id],
|
47
57
|
:page => Base64.strict_encode64(html),
|
48
|
-
:collectorUrl => "https://collector-#{
|
58
|
+
:collectorUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
49
59
|
}
|
50
60
|
render :json => hash_json
|
51
61
|
end
|
@@ -53,7 +63,7 @@ module PxModule
|
|
53
63
|
end
|
54
64
|
|
55
65
|
# Request was verified
|
56
|
-
return verified
|
66
|
+
return px_ctx.nil? ? true : px_ctx.context[:verified]
|
57
67
|
end
|
58
68
|
|
59
69
|
def self.configure(params)
|
@@ -90,34 +100,30 @@ module PxModule
|
|
90
100
|
def verify(env)
|
91
101
|
begin
|
92
102
|
@logger.debug('PerimeterX[pxVerify]')
|
93
|
-
if
|
103
|
+
if !@px_config[:module_enabled]
|
94
104
|
@logger.warn('Module is disabled')
|
95
|
-
return
|
105
|
+
return nil
|
96
106
|
end
|
97
107
|
req = ActionDispatch::Request.new(env)
|
98
108
|
px_ctx = PerimeterXContext.new(@px_config, req)
|
99
109
|
|
100
110
|
# Captcha phase
|
101
111
|
captcha_verified, px_ctx = @px_captcha_validator.verify(px_ctx)
|
102
|
-
if
|
112
|
+
if captcha_verified
|
103
113
|
return handle_verification(px_ctx)
|
104
114
|
end
|
105
115
|
|
106
116
|
# Cookie phase
|
107
117
|
cookie_verified, px_ctx = @px_cookie_validator.verify(px_ctx)
|
108
|
-
if
|
118
|
+
if !cookie_verified
|
109
119
|
@px_s2s_validator.verify(px_ctx)
|
110
120
|
end
|
111
121
|
|
112
|
-
|
113
|
-
return @px_config[:custom_verification_handler].call(px_ctx.context)
|
114
|
-
else
|
115
|
-
return handle_verification(px_ctx)
|
116
|
-
end
|
122
|
+
return handle_verification(px_ctx)
|
117
123
|
rescue Exception => e
|
118
124
|
@logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
|
119
125
|
e.backtrace.drop(1).map {|s| @logger.error("\t#{s}")}
|
120
|
-
return
|
126
|
+
return nil
|
121
127
|
end
|
122
128
|
end
|
123
129
|
|
@@ -139,25 +145,26 @@ module PxModule
|
|
139
145
|
@logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
|
140
146
|
|
141
147
|
score = px_ctx.context[:score]
|
148
|
+
px_ctx.context[:verified] = score < @px_config[:blocking_score]
|
142
149
|
# Case PASS request
|
143
|
-
if
|
150
|
+
if px_ctx.context[:verified]
|
144
151
|
@logger.debug("PerimeterX[handle_verification]: score:#{score} < blocking score, passing request")
|
145
152
|
@px_activity_client.send_page_requested_activity(px_ctx)
|
146
|
-
return
|
153
|
+
return px_ctx
|
147
154
|
end
|
148
155
|
|
149
156
|
# Case blocking activity
|
150
157
|
@px_activity_client.send_block_activity(px_ctx)
|
151
158
|
|
152
159
|
# In case were in monitor mode, end here
|
153
|
-
if
|
160
|
+
if @px_config[:module_mode] == PxModule::MONITOR_MODE
|
154
161
|
@logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
|
155
|
-
return
|
162
|
+
return px_ctx
|
156
163
|
end
|
157
164
|
|
158
165
|
@logger.debug('PerimeterX[handle_verification]: verification ended, the request should be blocked')
|
159
166
|
|
160
|
-
return
|
167
|
+
return px_ctx
|
161
168
|
end
|
162
169
|
|
163
170
|
private_class_method :new
|
@@ -14,6 +14,7 @@ module PxModule
|
|
14
14
|
@context[:px_cookie] = Hash.new
|
15
15
|
@context[:headers] = Hash.new
|
16
16
|
@context[:cookie_origin] = 'cookie'
|
17
|
+
@context[:made_s2s_risk_api_call] = false
|
17
18
|
cookies = req.cookies
|
18
19
|
|
19
20
|
# Get token from header
|
@@ -24,6 +25,8 @@ module PxModule
|
|
24
25
|
exploded_token = token.split(':', 2)
|
25
26
|
cookie_sym = "v#{exploded_token[0]}".to_sym
|
26
27
|
@context[:px_cookie][cookie_sym] = exploded_token[1]
|
28
|
+
else # TOKEN_HEADER exists yet there's no ':' delimiter - may indicate an error (storing original value)
|
29
|
+
@context[:px_cookie] = req.headers[PxModule::TOKEN_HEADER]
|
27
30
|
end
|
28
31
|
elsif !cookies.empty? # Get cookie from jar
|
29
32
|
# Prepare hashed cookies
|
@@ -7,17 +7,19 @@ module PxModule
|
|
7
7
|
super(px_config, http_client)
|
8
8
|
end
|
9
9
|
|
10
|
-
def send_captcha_request(
|
10
|
+
def send_captcha_request(captcha, px_ctx)
|
11
11
|
|
12
12
|
request_body = {
|
13
13
|
:request => {
|
14
14
|
:ip => px_ctx.context[:ip],
|
15
15
|
:headers => format_headers(px_ctx),
|
16
|
-
:uri => px_ctx.context[:uri]
|
16
|
+
:uri => px_ctx.context[:uri],
|
17
|
+
:captchaType => @px_config[:captcha_provider]
|
18
|
+
},
|
19
|
+
:additional => {
|
20
|
+
:module_version => @px_config[:sdk_name]
|
17
21
|
},
|
18
22
|
:pxCaptcha => captcha,
|
19
|
-
:vid => vid,
|
20
|
-
:uuid => uuid,
|
21
23
|
:hostname => px_ctx.context[:hostname]
|
22
24
|
}
|
23
25
|
|
@@ -25,30 +27,28 @@ module PxModule
|
|
25
27
|
headers = {
|
26
28
|
"Authorization" => "Bearer #{@px_config[:auth_token]}" ,
|
27
29
|
"Content-Type" => "application/json"
|
28
|
-
}
|
30
|
+
}
|
29
31
|
|
30
|
-
return @http_client.post(PxModule::
|
32
|
+
return @http_client.post(PxModule::API_CAPTCHA, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
31
33
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def verify(px_ctx)
|
35
37
|
captcha_validated = false
|
36
38
|
begin
|
37
|
-
if
|
39
|
+
if !px_ctx.context.key?(:px_captcha)
|
38
40
|
return captcha_validated, px_ctx
|
39
41
|
end
|
40
|
-
captcha
|
41
|
-
if captcha.nil?
|
42
|
+
captcha = px_ctx.context[:px_captcha]
|
43
|
+
if captcha.nil?
|
42
44
|
return captcha_validated, px_ctx
|
43
45
|
end
|
44
46
|
|
45
|
-
|
46
|
-
px_ctx.context[:uuid] = uuid
|
47
|
-
response = send_captcha_request(vid, uuid, captcha, px_ctx)
|
47
|
+
response = send_captcha_request(captcha, px_ctx)
|
48
48
|
|
49
|
-
if
|
49
|
+
if response.success?
|
50
50
|
response_body = eval(response.body)
|
51
|
-
if
|
51
|
+
if response_body[:status] == 0
|
52
52
|
captcha_validated = true
|
53
53
|
end
|
54
54
|
end
|
@@ -56,7 +56,7 @@ module PxModule
|
|
56
56
|
return captcha_validated, px_ctx
|
57
57
|
|
58
58
|
rescue Exception => e
|
59
|
-
@logger.error("PerimeterxCaptchaValidator[verify]: failed, returning false")
|
59
|
+
@logger.error("PerimeterxCaptchaValidator[verify]: failed, returning false => #{e.message}")
|
60
60
|
return captcha_validated, px_ctx
|
61
61
|
end
|
62
62
|
end
|
@@ -18,16 +18,34 @@ module PxModule
|
|
18
18
|
|
19
19
|
def verify(px_ctx)
|
20
20
|
begin
|
21
|
-
#
|
22
|
-
if px_ctx.context[:
|
23
|
-
|
21
|
+
# Mobile Error cases
|
22
|
+
if px_ctx.context[:cookie_origin] == 'header'
|
23
|
+
if px_ctx.context[:px_cookie].to_s.empty?
|
24
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: Empty token value - decryption failed")
|
25
|
+
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
26
|
+
return false, px_ctx
|
27
|
+
elsif px_ctx.context[:px_cookie] == "1"
|
28
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
29
|
+
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
30
|
+
return false, px_ctx
|
31
|
+
elsif px_ctx.context[:px_cookie] == "2" # Mobile SDK connection error
|
32
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk connection error")
|
33
|
+
px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_CONNECTION_ERROR
|
34
|
+
return false, px_ctx
|
35
|
+
elsif px_ctx.context[:px_cookie] == "3" # Mobile SDK pinning error
|
36
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk pinning error")
|
37
|
+
px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_PINNING_ERROR
|
38
|
+
return false, px_ctx
|
39
|
+
end
|
40
|
+
elsif px_ctx.context[:px_cookie].empty?
|
41
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
24
42
|
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
25
43
|
return false, px_ctx
|
26
44
|
end
|
27
45
|
|
28
46
|
# Deserialize cookie start
|
29
47
|
cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
|
30
|
-
if
|
48
|
+
if !cookie.deserialize()
|
31
49
|
@logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
|
32
50
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
33
51
|
return false, px_ctx
|
@@ -39,26 +57,25 @@ module PxModule
|
|
39
57
|
px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
|
40
58
|
px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
|
41
59
|
|
42
|
-
if
|
60
|
+
if cookie.expired?
|
43
61
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
|
44
62
|
px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
|
45
63
|
return false, px_ctx
|
46
64
|
end
|
47
65
|
|
48
|
-
if
|
66
|
+
if cookie.high_score?
|
49
67
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
|
50
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
|
51
68
|
px_ctx.context[:blocking_reason] = 'cookie_high_score'
|
52
69
|
return true, px_ctx
|
53
70
|
end
|
54
71
|
|
55
|
-
if
|
72
|
+
if !cookie.secured?
|
56
73
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
|
57
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
|
74
|
+
px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
|
58
75
|
return false, px_ctx
|
59
76
|
end
|
60
77
|
|
61
|
-
if
|
78
|
+
if px_ctx.context[:sensitive_route]
|
62
79
|
@logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
|
63
80
|
px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
|
64
81
|
return false, px_ctx
|
@@ -10,7 +10,7 @@ module PxModule
|
|
10
10
|
|
11
11
|
# Routes
|
12
12
|
API_V1_S2S = '/api/v1/collector/s2s'
|
13
|
-
|
13
|
+
API_CAPTCHA = '/api/v2/risk/captcha'
|
14
14
|
API_V2_RISK = '/api/v2/risk'
|
15
15
|
|
16
16
|
# Activity Types
|
@@ -28,7 +28,6 @@ module PxModule
|
|
28
28
|
|
29
29
|
# Templates
|
30
30
|
BLOCK_TEMPLATE = 'block'
|
31
|
-
CAPTCHA_TEMPLATE = 'captcha'
|
32
31
|
TEMPLATE_EXT = '.mustache'
|
33
32
|
|
34
33
|
|
@@ -48,4 +47,6 @@ module PxModule
|
|
48
47
|
|
49
48
|
# Mobile SDK
|
50
49
|
TOKEN_HEADER = 'X-PX-AUTHORIZATION'
|
50
|
+
MOBILE_SDK_CONNECTION_ERROR = 'mobile_sdk_connection_error'
|
51
|
+
MOBILE_SDK_PINNING_ERROR = 'mobile_sdk_pinning_error'
|
51
52
|
end
|
@@ -11,7 +11,7 @@ module PxModule
|
|
11
11
|
end
|
12
12
|
|
13
13
|
logger.debug('PxTemplateFactory[get_template]: rendering template')
|
14
|
-
template_type = px_ctx.context[:block_action] == 'captcha' ?
|
14
|
+
template_type = px_ctx.context[:block_action] == 'captcha' ? px_config[:captcha_provider].downcase : BLOCK_TEMPLATE
|
15
15
|
|
16
16
|
template_postfix = ''
|
17
17
|
if px_ctx.context[:cookie_origin] == 'header'
|
@@ -119,7 +119,7 @@
|
|
119
119
|
<div class="page-footer">
|
120
120
|
<p>
|
121
121
|
Powered by
|
122
|
-
<a href="https://www.perimeterx.com">PerimeterX</a>
|
122
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
123
123
|
, Inc.
|
124
124
|
</p>
|
125
125
|
</div>
|
@@ -130,4 +130,4 @@
|
|
130
130
|
<script src="{{jsRef}}"></script>
|
131
131
|
{{/ jsRef }}
|
132
132
|
</body>
|
133
|
-
</html>
|
133
|
+
</html>
|
@@ -0,0 +1,178 @@
|
|
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="{{ . }}"/>
|
91
|
+
{{/ cssRef }}
|
92
|
+
<script src="https://funcaptcha.com/fc/api/?onload=loadFunCaptcha" async defer></script>
|
93
|
+
</head>
|
94
|
+
|
95
|
+
<body>
|
96
|
+
<section class="container">
|
97
|
+
<div class="customer-logo-wrapper">
|
98
|
+
<div class="customer-logo">
|
99
|
+
<img src="{{customLogo}}" alt="Logo"/>
|
100
|
+
</div>
|
101
|
+
</div>
|
102
|
+
<div class="page-title-wrapper">
|
103
|
+
<div class="page-title">
|
104
|
+
<h1>Please verify you are a human</h1>
|
105
|
+
</div>
|
106
|
+
</div>
|
107
|
+
<div class="content-wrapper">
|
108
|
+
<div class="content">
|
109
|
+
<p>
|
110
|
+
Please click "Verify" to continue
|
111
|
+
</p>
|
112
|
+
<div id="CAPTCHA"></div>
|
113
|
+
<p>
|
114
|
+
Access to this page has been denied because we believe you are using automation tools to browse the
|
115
|
+
website.
|
116
|
+
</p>
|
117
|
+
<p>
|
118
|
+
This may happen as a result of the following:
|
119
|
+
</p>
|
120
|
+
<ul>
|
121
|
+
<li>
|
122
|
+
Javascript is disabled or blocked by an extension (ad blockers for example)
|
123
|
+
</li>
|
124
|
+
<li>
|
125
|
+
Your browser does not support cookies
|
126
|
+
</li>
|
127
|
+
</ul>
|
128
|
+
<p>
|
129
|
+
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
130
|
+
them from loading.
|
131
|
+
</p>
|
132
|
+
<p>
|
133
|
+
Reference ID: #{{refId}}
|
134
|
+
</p>
|
135
|
+
</div>
|
136
|
+
</div>
|
137
|
+
<div class="page-footer-wrapper">
|
138
|
+
<div class="page-footer">
|
139
|
+
<p>
|
140
|
+
Powered by
|
141
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
142
|
+
, Inc.
|
143
|
+
</p>
|
144
|
+
</div>
|
145
|
+
</div>
|
146
|
+
</section>
|
147
|
+
<!-- Captcha -->
|
148
|
+
<script>
|
149
|
+
|
150
|
+
function loadFunCaptcha() {
|
151
|
+
var vid = '{{vid}}';
|
152
|
+
var uuid = '{{uuid}}';
|
153
|
+
|
154
|
+
new FunCaptcha({
|
155
|
+
public_key: "19E4B3B8-6CBE-35CC-4205-FC79ECDDA765",
|
156
|
+
target_html: "CAPTCHA",
|
157
|
+
callback: function () {
|
158
|
+
var expiryUtc = new Date(Date.now() + 1000 * 10).toUTCString();
|
159
|
+
var pxCaptcha = "_pxCaptcha=" + btoa(JSON.stringify({r: document.getElementById("FunCaptcha-Token").value, u: uuid, v: vid}));
|
160
|
+
var cookieParts = [
|
161
|
+
pxCaptcha,
|
162
|
+
"; expires=",
|
163
|
+
expiryUtc,
|
164
|
+
"; path=/"
|
165
|
+
];
|
166
|
+
|
167
|
+
document.cookie = cookieParts.join("");
|
168
|
+
location.reload();
|
169
|
+
}
|
170
|
+
});
|
171
|
+
}
|
172
|
+
</script>
|
173
|
+
<!-- Custom Script -->
|
174
|
+
{{# jsRef }}
|
175
|
+
<script src="{{ . }}"></script>
|
176
|
+
{{/ jsRef }}
|
177
|
+
</body>
|
178
|
+
</html>
|