perimeter_x 1.3.0 → 2.2.1
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 +5 -5
- data/.travis.yml +1 -1
- data/Dockerfile +12 -7
- data/Gemfile.lock +32 -31
- data/changelog.md +55 -1
- data/lib/perimeter_x.rb +186 -82
- data/lib/perimeterx/configuration.rb +74 -22
- data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +29 -6
- data/lib/perimeterx/internal/exceptions/px_config_exception.rb +6 -0
- data/lib/perimeterx/internal/first_party/px_first_party.rb +124 -0
- data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +3 -0
- data/lib/perimeterx/internal/perimeter_x_context.rb +50 -19
- data/lib/perimeterx/internal/validators/hash_schema_validator.rb +26 -0
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +17 -12
- data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +30 -7
- data/lib/perimeterx/utils/px_constants.rb +17 -5
- data/lib/perimeterx/utils/px_http_client.rb +59 -2
- data/lib/perimeterx/utils/px_template_factory.rb +13 -9
- data/lib/perimeterx/utils/templates/{captcha.mobile.mustache → block_template.mustache} +48 -69
- data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
- data/lib/perimeterx/version.rb +1 -1
- data/perimeter_x.gemspec +3 -3
- data/readme.md +106 -40
- metadata +18 -19
- data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +0 -65
- data/lib/perimeterx/utils/templates/block.mobile.mustache +0 -133
- data/lib/perimeterx/utils/templates/block.mustache +0 -146
- 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
|
@@ -18,47 +18,51 @@ module PxModule
|
|
18
18
|
|
19
19
|
def verify(px_ctx)
|
20
20
|
begin
|
21
|
-
# Case no cookie
|
22
21
|
if px_ctx.context[:px_cookie].empty?
|
23
|
-
@logger.warn("PerimeterxCookieValidator:[verify]: cookie
|
22
|
+
@logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
|
24
23
|
px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
|
25
24
|
return false, px_ctx
|
26
25
|
end
|
27
|
-
|
26
|
+
|
28
27
|
# Deserialize cookie start
|
29
28
|
cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
|
30
|
-
if
|
29
|
+
if !cookie.deserialize()
|
31
30
|
@logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
|
31
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.get_px_cookie
|
32
32
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
33
33
|
return false, px_ctx
|
34
34
|
end
|
35
35
|
px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
|
36
36
|
px_ctx.context[:score] = cookie.cookie_score()
|
37
37
|
px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
|
38
|
-
px_ctx.context[:vid] = cookie.decoded_cookie[:v]
|
39
38
|
px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
|
40
39
|
px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
|
41
40
|
|
42
|
-
|
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?
|
43
48
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
|
44
49
|
px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
|
45
50
|
return false, px_ctx
|
46
51
|
end
|
47
52
|
|
48
|
-
if
|
53
|
+
if cookie.high_score?
|
49
54
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
|
50
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
|
51
55
|
px_ctx.context[:blocking_reason] = 'cookie_high_score'
|
52
56
|
return true, px_ctx
|
53
57
|
end
|
54
58
|
|
55
|
-
if
|
59
|
+
if !cookie.secured?
|
56
60
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
|
57
|
-
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
|
61
|
+
px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
|
58
62
|
return false, px_ctx
|
59
63
|
end
|
60
64
|
|
61
|
-
if
|
65
|
+
if px_ctx.context[:sensitive_route]
|
62
66
|
@logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
|
63
67
|
px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
|
64
68
|
return false, px_ctx
|
@@ -66,10 +70,11 @@ module PxModule
|
|
66
70
|
|
67
71
|
@logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
|
68
72
|
|
73
|
+
px_ctx.context[:pass_reason] = 'cookie'
|
69
74
|
return true, px_ctx
|
70
75
|
rescue Exception => e
|
71
76
|
@logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
|
72
|
-
px_ctx.context[:px_orig_cookie] =
|
77
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.context[:px_cookie]
|
73
78
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
74
79
|
return false, px_ctx
|
75
80
|
end
|
@@ -26,12 +26,22 @@ module PxModule
|
|
26
26
|
:additional => {
|
27
27
|
:s2s_call_reason => px_ctx.context[:s2s_call_reason],
|
28
28
|
:module_version => @px_config[:sdk_name],
|
29
|
-
:cookie_origin => px_ctx.context[:cookie_origin],
|
30
29
|
:http_method => px_ctx.context[:http_method],
|
31
30
|
:http_version => px_ctx.context[:http_version],
|
32
31
|
:risk_mode => risk_mode
|
33
32
|
}
|
34
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
|
+
|
35
45
|
#Check for hmac
|
36
46
|
@logger.debug("px_ctx cookie_hmac key = #{px_ctx.context.key?(:cookie_hmac)}, value is: #{px_ctx.context[:cookie_hmac]}")
|
37
47
|
if px_ctx.context.key?(:cookie_hmac)
|
@@ -67,11 +77,19 @@ module PxModule
|
|
67
77
|
};
|
68
78
|
|
69
79
|
# Custom risk handler
|
80
|
+
risk_start = Time.now
|
70
81
|
if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
|
71
|
-
response = @px_config[:custom_risk_handler].call(PxModule::
|
82
|
+
response = @px_config[:custom_risk_handler].call(PxModule::API_V3_RISK, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
72
83
|
else
|
73
|
-
response = @http_client.post(PxModule::
|
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
|
74
91
|
end
|
92
|
+
|
75
93
|
return response
|
76
94
|
end
|
77
95
|
|
@@ -79,6 +97,7 @@ module PxModule
|
|
79
97
|
@logger.debug("PerimeterxS2SValidator[verify]")
|
80
98
|
response = send_risk_request(px_ctx)
|
81
99
|
if (!response)
|
100
|
+
px_ctx.context[:pass_reason] = "s2s_timeout"
|
82
101
|
return px_ctx
|
83
102
|
end
|
84
103
|
px_ctx.context[:made_s2s_risk_api_call] = true
|
@@ -86,7 +105,7 @@ module PxModule
|
|
86
105
|
# From here response should be valid, if success or error
|
87
106
|
response_body = eval(response.body);
|
88
107
|
# When success
|
89
|
-
if (response.code == 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 )
|
90
109
|
@logger.debug("PerimeterxS2SValidator[verify]: response ok")
|
91
110
|
score = response_body[:score]
|
92
111
|
px_ctx.context[:score] = score
|
@@ -97,13 +116,17 @@ module PxModule
|
|
97
116
|
px_ctx.context[:blocking_reason] = 'challenge'
|
98
117
|
elsif (score >= @px_config[:blocking_score])
|
99
118
|
px_ctx.context[:blocking_reason] = 's2s_high_score'
|
119
|
+
else
|
120
|
+
px_ctx.context[:pass_reason] = 's2s'
|
100
121
|
end #end challange or blocking score
|
101
122
|
end #end success response
|
102
123
|
|
103
124
|
# When error
|
104
|
-
|
105
|
-
|
106
|
-
|
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]
|
107
130
|
px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
|
108
131
|
end
|
109
132
|
|
@@ -10,8 +10,7 @@ module PxModule
|
|
10
10
|
|
11
11
|
# Routes
|
12
12
|
API_V1_S2S = '/api/v1/collector/s2s'
|
13
|
-
|
14
|
-
API_V2_RISK = '/api/v2/risk'
|
13
|
+
API_V3_RISK = '/api/v3/risk'
|
15
14
|
|
16
15
|
# Activity Types
|
17
16
|
BLOCK_ACTIVITY = 'block'
|
@@ -27,9 +26,9 @@ module PxModule
|
|
27
26
|
SENSITIVE_ROUTE = 'sensitive_route'
|
28
27
|
|
29
28
|
# Templates
|
30
|
-
|
31
|
-
CAPTCHA_TEMPLATE = 'captcha'
|
29
|
+
CHALLENGE_TEMPLATE = 'block_template'
|
32
30
|
TEMPLATE_EXT = '.mustache'
|
31
|
+
RATELIMIT_TEMPLATE = 'ratelimit'
|
33
32
|
|
34
33
|
|
35
34
|
# Template Props
|
@@ -41,11 +40,24 @@ module PxModule
|
|
41
40
|
PROP_CUSTOM_LOGO = :customLogo
|
42
41
|
PROP_CSS_REF = :cssRef
|
43
42
|
PROP_JS_REF = :jsRef
|
44
|
-
|
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.perimeterx.net'
|
50
|
+
CAPTCHA_HOST = 'captcha.px-cdn.net'
|
45
51
|
|
46
52
|
VISIBLE = 'visible'
|
47
53
|
HIDDEN = 'hidden'
|
48
54
|
|
49
55
|
# Mobile SDK
|
50
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/
|
51
63
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'perimeterx/utils/px_logger'
|
2
2
|
require 'typhoeus'
|
3
3
|
require 'concurrent'
|
4
|
+
require 'net/http'
|
4
5
|
|
5
6
|
module PxModule
|
6
7
|
class PxHttpClient
|
@@ -12,7 +13,7 @@ module PxModule
|
|
12
13
|
def initialize(px_config)
|
13
14
|
@px_config = px_config
|
14
15
|
@logger = px_config[:logger]
|
15
|
-
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:
|
16
|
+
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:backend_url]}")
|
16
17
|
end
|
17
18
|
|
18
19
|
# Runs a POST command to Perimeter X servers
|
@@ -28,7 +29,7 @@ module PxModule
|
|
28
29
|
begin
|
29
30
|
@logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
|
30
31
|
response = Typhoeus.post(
|
31
|
-
"#{px_config[:
|
32
|
+
"#{px_config[:backend_url]}#{path}",
|
32
33
|
headers: headers,
|
33
34
|
body: body.to_json,
|
34
35
|
timeout: api_timeout,
|
@@ -45,5 +46,61 @@ module PxModule
|
|
45
46
|
return response
|
46
47
|
end
|
47
48
|
|
49
|
+
|
50
|
+
def post_xhr(url, body, headers)
|
51
|
+
s = Time.now
|
52
|
+
begin
|
53
|
+
@logger.debug("PxHttpClient[post]: sending xhr post request to #{url} with headers {#{headers.to_json()}}")
|
54
|
+
|
55
|
+
#set url
|
56
|
+
uri = URI(url)
|
57
|
+
req = Net::HTTP::Post.new(uri)
|
58
|
+
|
59
|
+
# set body
|
60
|
+
req.body=body
|
61
|
+
|
62
|
+
# set headers
|
63
|
+
headers.each do |key, value|
|
64
|
+
req[key] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
# send request
|
68
|
+
response = Net::HTTP.start(uri.hostname, uri.port) {|http|
|
69
|
+
http.request(req)
|
70
|
+
}
|
71
|
+
|
72
|
+
ensure
|
73
|
+
e = Time.now
|
74
|
+
@logger.debug("PxHttpClient[get]: runtime: #{(e-s) * 1000.0}")
|
75
|
+
end
|
76
|
+
return response
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def get(url, headers)
|
81
|
+
s = Time.now
|
82
|
+
begin
|
83
|
+
@logger.debug("PxHttpClient[get]: sending get request to #{url} with headers {#{headers.to_json()}}")
|
84
|
+
|
85
|
+
#set url
|
86
|
+
uri = URI(url)
|
87
|
+
req = Net::HTTP::Get.new(uri)
|
88
|
+
|
89
|
+
# set headers
|
90
|
+
headers.each do |key, value|
|
91
|
+
req[key] = value
|
92
|
+
end
|
93
|
+
|
94
|
+
# send request
|
95
|
+
response = Net::HTTP.start(uri.hostname, uri.port) {|http|
|
96
|
+
http.request(req)
|
97
|
+
}
|
98
|
+
|
99
|
+
ensure
|
100
|
+
e = Time.now
|
101
|
+
@logger.debug("PxHttpClient[get]: runtime: #{(e-s) * 1000.0}")
|
102
|
+
end
|
103
|
+
return response
|
104
|
+
end
|
48
105
|
end
|
49
106
|
end
|
@@ -3,23 +3,24 @@ 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
8
|
if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
|
9
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
|
-
|
14
|
-
template_type = px_ctx.context[:block_action] == 'captcha' ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
|
13
|
+
view = Mustache.new
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
19
21
|
end
|
20
22
|
|
21
|
-
Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{
|
22
|
-
view = Mustache.new
|
23
|
+
Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
|
23
24
|
|
24
25
|
view[PxModule::PROP_APP_ID] = px_config[:app_id]
|
25
26
|
view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
|
@@ -28,8 +29,11 @@ module PxModule
|
|
28
29
|
view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
|
29
30
|
view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
|
30
31
|
view[PxModule::PROP_JS_REF] = px_config[:js_ref]
|
31
|
-
view[PxModule::
|
32
|
+
view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
|
32
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] = px_ctx.context[:first_party_enabled]
|
33
37
|
|
34
38
|
return view.render.html_safe
|
35
39
|
end
|
@@ -6,19 +6,19 @@
|
|
6
6
|
<title>Access to this page has been denied.</title>
|
7
7
|
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
|
8
8
|
<style>
|
9
|
-
html,body{
|
9
|
+
html, body {
|
10
10
|
margin: 0;
|
11
11
|
padding: 0;
|
12
12
|
font-family: 'Open Sans', sans-serif;
|
13
13
|
color: #000;
|
14
14
|
}
|
15
15
|
|
16
|
-
a{
|
16
|
+
a {
|
17
17
|
color: #c5c5c5;
|
18
18
|
text-decoration: none;
|
19
19
|
}
|
20
20
|
|
21
|
-
.container{
|
21
|
+
.container {
|
22
22
|
align-items: center;
|
23
23
|
display: flex;
|
24
24
|
flex: 1;
|
@@ -30,7 +30,7 @@
|
|
30
30
|
.container > div {
|
31
31
|
width: 100%;
|
32
32
|
display: flex;
|
33
|
-
justify-content:center;
|
33
|
+
justify-content: center;
|
34
34
|
}
|
35
35
|
|
36
36
|
.container > div > div {
|
@@ -38,38 +38,40 @@
|
|
38
38
|
width: 80%;
|
39
39
|
}
|
40
40
|
|
41
|
-
.customer-logo-wrapper{
|
41
|
+
.customer-logo-wrapper {
|
42
42
|
padding-top: 2rem;
|
43
43
|
flex-grow: 0;
|
44
44
|
background-color: #fff;
|
45
45
|
visibility: {{logoVisibility}};
|
46
46
|
}
|
47
47
|
|
48
|
-
.customer-logo{
|
48
|
+
.customer-logo {
|
49
49
|
border-bottom: 1px solid #000;
|
50
50
|
}
|
51
51
|
|
52
|
-
.customer-logo > img{
|
52
|
+
.customer-logo > img {
|
53
53
|
padding-bottom: 1rem;
|
54
54
|
max-height: 50px;
|
55
|
-
max-width:
|
55
|
+
max-width: 100%;
|
56
56
|
}
|
57
57
|
|
58
|
-
.page-title-wrapper{
|
58
|
+
.page-title-wrapper {
|
59
59
|
flex-grow: 2;
|
60
60
|
}
|
61
|
+
|
61
62
|
.page-title {
|
62
63
|
flex-direction: column-reverse;
|
63
64
|
}
|
64
65
|
|
65
|
-
.content-wrapper{
|
66
|
+
.content-wrapper {
|
66
67
|
flex-grow: 5;
|
67
68
|
}
|
68
|
-
|
69
|
+
|
70
|
+
.content {
|
69
71
|
flex-direction: column;
|
70
72
|
}
|
71
73
|
|
72
|
-
.page-footer-wrapper{
|
74
|
+
.page-footer-wrapper {
|
73
75
|
align-items: center;
|
74
76
|
flex-grow: 0.2;
|
75
77
|
background-color: #000;
|
@@ -77,17 +79,16 @@
|
|
77
79
|
font-size: 70%;
|
78
80
|
}
|
79
81
|
|
80
|
-
@media (min-width:768px){
|
81
|
-
html,body{
|
82
|
+
@media (min-width: 768px) {
|
83
|
+
html, body {
|
82
84
|
height: 100%;
|
83
85
|
}
|
84
86
|
}
|
85
87
|
</style>
|
86
88
|
<!-- Custom CSS -->
|
87
89
|
{{#cssRef}}
|
88
|
-
<link rel="stylesheet" type="text/css" href="{{cssRef}}"
|
90
|
+
<link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
|
89
91
|
{{/cssRef}}
|
90
|
-
<script src="https://www.google.com/recaptcha/api.js" async defer></script>
|
91
92
|
</head>
|
92
93
|
|
93
94
|
<body>
|
@@ -104,13 +105,12 @@
|
|
104
105
|
</div>
|
105
106
|
<div class="content-wrapper">
|
106
107
|
<div class="content">
|
107
|
-
|
108
|
-
|
109
|
-
</p>
|
110
|
-
<div class="g-recaptcha" data-sitekey="6Lcj-R8TAAAAABs3FrRPuQhLMbp5QrHsHufzLf7b" data-callback="handleCaptcha" data-theme="dark">
|
108
|
+
|
109
|
+
<div id="px-captcha">
|
111
110
|
</div>
|
112
111
|
<p>
|
113
|
-
Access to this page has been denied because we believe you are using automation tools to browse the
|
112
|
+
Access to this page has been denied because we believe you are using automation tools to browse the
|
113
|
+
website.
|
114
114
|
</p>
|
115
115
|
<p>
|
116
116
|
This may happen as a result of the following:
|
@@ -124,7 +124,8 @@
|
|
124
124
|
</li>
|
125
125
|
</ul>
|
126
126
|
<p>
|
127
|
-
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
127
|
+
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
128
|
+
them from loading.
|
128
129
|
</p>
|
129
130
|
<p>
|
130
131
|
Reference ID: #{{refId}}
|
@@ -135,62 +136,40 @@
|
|
135
136
|
<div class="page-footer">
|
136
137
|
<p>
|
137
138
|
Powered by
|
138
|
-
<a href="https://www.perimeterx.com">PerimeterX</a>
|
139
|
+
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
139
140
|
, Inc.
|
140
141
|
</p>
|
141
142
|
</div>
|
142
143
|
</div>
|
143
144
|
</section>
|
144
|
-
<!--
|
145
|
+
<!-- Px -->
|
145
146
|
<script>
|
146
|
-
|
147
|
-
|
148
|
-
}
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
});
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
});
|
167
|
-
});
|
168
|
-
req.addEventListener('load', function() {
|
169
|
-
if (req.status == 200) {
|
170
|
-
try {
|
171
|
-
var responseJSON = JSON.parse(req.responseText);
|
172
|
-
return captchaSolved(responseJSON);
|
173
|
-
} catch (ex) {}
|
174
|
-
}
|
175
|
-
captchaSolved({
|
176
|
-
status: 3
|
177
|
-
});
|
178
|
-
});
|
179
|
-
req.send(JSON.stringify({
|
180
|
-
appId: appId,
|
181
|
-
uuid: uuid,
|
182
|
-
vid: vid,
|
183
|
-
pxCaptcha: response,
|
184
|
-
hostname: window.location.hostname,
|
185
|
-
request: {
|
186
|
-
url: window.location.href
|
187
|
-
}
|
188
|
-
}));
|
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
|
+
};
|
189
167
|
}
|
190
168
|
</script>
|
169
|
+
|
191
170
|
<!-- Custom Script -->
|
192
171
|
{{#jsRef}}
|
193
|
-
<script src="{{jsRef}}"></script>
|
172
|
+
<script src="{{{jsRef}}}"></script>
|
194
173
|
{{/jsRef}}
|
195
174
|
</body>
|
196
|
-
</html>
|
175
|
+
</html>
|