perimeter_x 1.3.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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>
|