perimeter_x 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1f3a1b696f592b2f1478cc376a96475a5d5f0778
4
- data.tar.gz: e859865af132477c953d6aed1a7c70bb927b432e
3
+ metadata.gz: 3dca9964af5aa39c20643f7e5d6f00271862f0de
4
+ data.tar.gz: e0f2a81bce3aa7c8b8eb0b0b73d70cc29f272dbc
5
5
  SHA512:
6
- metadata.gz: 1f67c50466d0a347d7d5e3dc6c30f66332a3dabb27a34152e49c36a6614fa06ce81bc02d8f50ba2e061f9fb4cef8b21e9fb684d2d2b8b138ffab87298d0e1f81
7
- data.tar.gz: 35d39968545b296e04f02bed0bb2c67b40ca1ff6773e352533fbe06e2795ad630a838d159fe517a12702e79d5054d81ef828fa966e9278dec7fd6eaa37dd2e06
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.3.0] - 2017-06-04
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
- verified, px_ctx = PerimeterX.instance.verify(request.env)
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 custon block handler exists
25
- if (PerimeterX.instance.px_config.key?(:custom_block_handler))
26
- PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: custom_block_handler triggered')
27
- return instance_exec(px_ctx, &PerimeterX.instance.px_config[:custom_block_handler])
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
- PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: sending default block page')
31
- html = PxTemplateFactory.get_template(px_ctx, PerimeterX.instance.px_config)
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
- PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: web block')
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
- PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: mobile sdk block')
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 => PerimeterX.instance.px_config[:app_id],
56
+ :appId => px_config[:app_id],
47
57
  :page => Base64.strict_encode64(html),
48
- :collectorUrl => "https://collector-#{PerimeterX.instance.px_config[:app_id]}.perimeterx.net"
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 (!@px_config[:module_enabled])
103
+ if !@px_config[:module_enabled]
94
104
  @logger.warn('Module is disabled')
95
- return true
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 (captcha_verified)
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 (!cookie_verified)
118
+ if !cookie_verified
109
119
  @px_s2s_validator.verify(px_ctx)
110
120
  end
111
121
 
112
- if (@px_config.key?(:custom_verification_handler))
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 true
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 (score < @px_config[:blocking_score])
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 true
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 (@px_config[:module_mode] == PxModule::MONITOR_MODE)
160
+ if @px_config[:module_mode] == PxModule::MONITOR_MODE
154
161
  @logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
155
- return true
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 false, px_ctx
167
+ return px_ctx
161
168
  end
162
169
 
163
170
  private_class_method :new
@@ -12,7 +12,7 @@ module PxModule
12
12
  :cookie_key => nil,
13
13
  :auth_token => nil,
14
14
  :module_enabled => true,
15
- :captcha_enabled => true,
15
+ :captcha_provider => "reCaptcha",
16
16
  :challenge_enabled => true,
17
17
  :encryption_enabled => true,
18
18
  :blocking_score => 70,
@@ -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(vid, uuid, captcha, px_ctx)
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::API_V1_CAPTCHA, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
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(!px_ctx.context.key?(:px_captcha))
39
+ if !px_ctx.context.key?(:px_captcha)
38
40
  return captcha_validated, px_ctx
39
41
  end
40
- captcha, vid, uuid = px_ctx.context[:px_captcha].split(':', 3)
41
- if captcha.nil? || vid.nil? || uuid.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
- px_ctx.context[:vid] = vid
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 (response.status_code == 200)
49
+ if response.success?
50
50
  response_body = eval(response.body)
51
- if ( response_body[:code] == 0 )
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
- # Case no cookie
22
- if px_ctx.context[:px_cookie].empty?
23
- @logger.warn("PerimeterxCookieValidator:[verify]: cookie not found")
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 (!cookie.deserialize())
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 (cookie.expired?)
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 (cookie.high_score?)
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 (!cookie.secured?)
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 (px_ctx.context[:sensitive_route])
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
- API_V1_CAPTCHA = '/api/v1/risk/captcha'
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' ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
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>
@@ -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>
@@ -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>