perimeter_x 1.4.0 → 2.0.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 +5 -5
- data/.travis.yml +1 -1
- data/Dockerfile +12 -7
- data/Gemfile.lock +32 -31
- data/changelog.md +25 -0
- data/lib/perimeter_x.rb +75 -23
- data/lib/perimeterx/configuration.rb +24 -20
- data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +25 -5
- data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +3 -0
- data/lib/perimeterx/internal/perimeter_x_context.rb +32 -15
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +9 -2
- data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +19 -6
- data/lib/perimeterx/utils/px_constants.rb +14 -4
- data/lib/perimeterx/utils/px_http_client.rb +2 -2
- data/lib/perimeterx/utils/px_template_factory.rb +13 -9
- data/lib/perimeterx/utils/templates/{funcaptcha.mobile.mustache → block_template.mustache} +31 -34
- 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 +15 -5
- metadata +15 -21
- 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/funcaptcha.mustache +0 -191
- data/lib/perimeterx/utils/templates/recaptcha.mobile.mustache +0 -196
- data/lib/perimeterx/utils/templates/recaptcha.mustache +0 -176
@@ -108,6 +108,9 @@ module PxModule
|
|
108
108
|
px_cookie = px_cookie.gsub(' ', '+')
|
109
109
|
salt, iterations, cipher_text = px_cookie.split(':')
|
110
110
|
iterations = iterations.to_i
|
111
|
+
if (iterations > @px_config[:risk_cookie_max_iterations] || iterations < 500)
|
112
|
+
return
|
113
|
+
end
|
111
114
|
salt = Base64.decode64(salt)
|
112
115
|
cipher_text = Base64.decode64(cipher_text)
|
113
116
|
digest = OpenSSL::Digest::SHA256.new
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'perimeterx/utils/px_logger'
|
2
|
+
require 'perimeterx/utils/px_constants'
|
2
3
|
|
3
4
|
module PxModule
|
4
5
|
class PerimeterXContext
|
@@ -17,27 +18,45 @@ module PxModule
|
|
17
18
|
@context[:made_s2s_risk_api_call] = false
|
18
19
|
cookies = req.cookies
|
19
20
|
|
21
|
+
# Get IP from header/custom function
|
22
|
+
if px_config[:ip_headers].length() > 0
|
23
|
+
px_config[:ip_headers].each do |ip_header|
|
24
|
+
if req.headers[ip_header]
|
25
|
+
@context[:ip] = force_utf8(req.headers[ip_header])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
elsif px_config[:ip_header_function] != nil
|
29
|
+
@context[:ip] = px_config[:ip_header_function].call(req)
|
30
|
+
end
|
31
|
+
|
32
|
+
if @context[:ip] == nil
|
33
|
+
@context[:ip] = req.ip
|
34
|
+
end
|
35
|
+
|
20
36
|
# Get token from header
|
21
37
|
if req.headers[PxModule::TOKEN_HEADER]
|
22
38
|
@context[:cookie_origin] = 'header'
|
23
|
-
token = req.headers[PxModule::TOKEN_HEADER]
|
39
|
+
token = force_utf8(req.headers[PxModule::TOKEN_HEADER])
|
24
40
|
if token.include? ':'
|
25
41
|
exploded_token = token.split(':', 2)
|
26
42
|
cookie_sym = "v#{exploded_token[0]}".to_sym
|
27
43
|
@context[:px_cookie][cookie_sym] = exploded_token[1]
|
28
44
|
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]
|
45
|
+
@context[:px_cookie] = force_utf8(req.headers[PxModule::TOKEN_HEADER])
|
30
46
|
end
|
31
47
|
elsif !cookies.empty? # Get cookie from jar
|
32
48
|
# Prepare hashed cookies
|
33
49
|
cookies.each do |k, v|
|
34
50
|
case k.to_s
|
35
51
|
when '_px3'
|
36
|
-
@context[:px_cookie][:v3] = v
|
52
|
+
@context[:px_cookie][:v3] = force_utf8(v)
|
37
53
|
when '_px'
|
38
|
-
@context[:px_cookie][:v1] = v
|
39
|
-
when '
|
40
|
-
|
54
|
+
@context[:px_cookie][:v1] = force_utf8(v)
|
55
|
+
when '_pxvid'
|
56
|
+
if v.is_a?(String) && v.match(PxModule::VID_REGEX)
|
57
|
+
@context[:vid_source] = "vid_cookie"
|
58
|
+
@context[:vid] = force_utf8(v)
|
59
|
+
end
|
41
60
|
end
|
42
61
|
end #end case
|
43
62
|
end #end empty cookies
|
@@ -46,7 +65,7 @@ module PxModule
|
|
46
65
|
if (k.start_with? 'HTTP_')
|
47
66
|
header = k.to_s.gsub('HTTP_', '')
|
48
67
|
header = header.gsub('_', '-').downcase
|
49
|
-
@context[:headers][header.to_sym] = v
|
68
|
+
@context[:headers][header.to_sym] = force_utf8(v)
|
50
69
|
end
|
51
70
|
end #end headers foreach
|
52
71
|
|
@@ -57,14 +76,6 @@ module PxModule
|
|
57
76
|
@context[:format] = req.format.symbol
|
58
77
|
@context[:score] = 0
|
59
78
|
|
60
|
-
if px_config.key?(:custom_user_ip)
|
61
|
-
@context[:ip] = req.headers[px_config[:custom_user_ip]]
|
62
|
-
elsif px_config.key?(:px_custom_user_ip_method)
|
63
|
-
@context[:ip] = px_config[:px_custom_user_ip_method].call(req)
|
64
|
-
else
|
65
|
-
@context[:ip] = req.ip
|
66
|
-
end
|
67
|
-
|
68
79
|
if req.server_protocol
|
69
80
|
httpVer = req.server_protocol.split('/')
|
70
81
|
if httpVer.size > 0
|
@@ -82,6 +93,10 @@ module PxModule
|
|
82
93
|
false
|
83
94
|
end
|
84
95
|
|
96
|
+
def force_utf8(str)
|
97
|
+
return str.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
98
|
+
end
|
99
|
+
|
85
100
|
def set_block_action_type(action)
|
86
101
|
@context[:block_action] = case action
|
87
102
|
when 'c'
|
@@ -90,6 +105,8 @@ module PxModule
|
|
90
105
|
return 'block'
|
91
106
|
when 'j'
|
92
107
|
return 'challenge'
|
108
|
+
when 'r'
|
109
|
+
return 'rate_limit'
|
93
110
|
else
|
94
111
|
return captcha
|
95
112
|
end
|
@@ -47,16 +47,22 @@ module PxModule
|
|
47
47
|
cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
|
48
48
|
if !cookie.deserialize()
|
49
49
|
@logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
|
50
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.get_px_cookie
|
50
51
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
51
52
|
return false, px_ctx
|
52
53
|
end
|
53
54
|
px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
|
54
55
|
px_ctx.context[:score] = cookie.cookie_score()
|
55
56
|
px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
|
56
|
-
px_ctx.context[:vid] = cookie.decoded_cookie[:v]
|
57
57
|
px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
|
58
58
|
px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
|
59
59
|
|
60
|
+
vid = cookie.decoded_cookie[:v]
|
61
|
+
if vid.is_a?(String) && vid.match(PxModule::VID_REGEX)
|
62
|
+
px_ctx.context[:vid_source] = "risk_cookie"
|
63
|
+
px_ctx.context[:vid] = vid
|
64
|
+
end
|
65
|
+
|
60
66
|
if cookie.expired?
|
61
67
|
@logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
|
62
68
|
px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
|
@@ -83,10 +89,11 @@ module PxModule
|
|
83
89
|
|
84
90
|
@logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
|
85
91
|
|
92
|
+
px_ctx.context[:pass_reason] = 'cookie'
|
86
93
|
return true, px_ctx
|
87
94
|
rescue Exception => e
|
88
95
|
@logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
|
89
|
-
px_ctx.context[:px_orig_cookie] =
|
96
|
+
px_ctx.context[:px_orig_cookie] = px_ctx.context[:px_cookie]
|
90
97
|
px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
|
91
98
|
return false, px_ctx
|
92
99
|
end
|
@@ -67,11 +67,19 @@ module PxModule
|
|
67
67
|
};
|
68
68
|
|
69
69
|
# Custom risk handler
|
70
|
+
risk_start = Time.now
|
70
71
|
if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
|
71
|
-
response = @px_config[:custom_risk_handler].call(PxModule::
|
72
|
+
response = @px_config[:custom_risk_handler].call(PxModule::API_V3_RISK, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
72
73
|
else
|
73
|
-
response = @http_client.post(PxModule::
|
74
|
+
response = @http_client.post(PxModule::API_V3_RISK , request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
|
74
75
|
end
|
76
|
+
|
77
|
+
# Set risk_rtt
|
78
|
+
if(response)
|
79
|
+
risk_end = Time.now
|
80
|
+
px_ctx.context[:risk_rtt] = ((risk_end-risk_start)*1000).round
|
81
|
+
end
|
82
|
+
|
75
83
|
return response
|
76
84
|
end
|
77
85
|
|
@@ -79,6 +87,7 @@ module PxModule
|
|
79
87
|
@logger.debug("PerimeterxS2SValidator[verify]")
|
80
88
|
response = send_risk_request(px_ctx)
|
81
89
|
if (!response)
|
90
|
+
px_ctx.context[:pass_reason] = "s2s_timeout"
|
82
91
|
return px_ctx
|
83
92
|
end
|
84
93
|
px_ctx.context[:made_s2s_risk_api_call] = true
|
@@ -86,7 +95,7 @@ module PxModule
|
|
86
95
|
# From here response should be valid, if success or error
|
87
96
|
response_body = eval(response.body);
|
88
97
|
# When success
|
89
|
-
if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action))
|
98
|
+
if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
|
90
99
|
@logger.debug("PerimeterxS2SValidator[verify]: response ok")
|
91
100
|
score = response_body[:score]
|
92
101
|
px_ctx.context[:score] = score
|
@@ -97,13 +106,17 @@ module PxModule
|
|
97
106
|
px_ctx.context[:blocking_reason] = 'challenge'
|
98
107
|
elsif (score >= @px_config[:blocking_score])
|
99
108
|
px_ctx.context[:blocking_reason] = 's2s_high_score'
|
109
|
+
else
|
110
|
+
px_ctx.context[:pass_reason] = 's2s'
|
100
111
|
end #end challange or blocking score
|
101
112
|
end #end success response
|
102
113
|
|
103
114
|
# When error
|
104
|
-
|
105
|
-
|
106
|
-
|
115
|
+
risk_error_status = response_body && response_body.key?(:status) && response_body[:status] == -1
|
116
|
+
if(response.code != 200 || risk_error_status)
|
117
|
+
@logger.warn("PerimeterxS2SValidator[verify]: bad response, returned code #{response.code} #{risk_error_status ? "risk status: -1" : ""}")
|
118
|
+
px_ctx.context[:pass_reason] = 'request_failed'
|
119
|
+
px_ctx.context[:uuid] = (!response_body || response_body[:uuid].nil?)? "" : response_body[:uuid]
|
107
120
|
px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
|
108
121
|
end
|
109
122
|
|
@@ -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,8 +26,9 @@ module PxModule
|
|
27
26
|
SENSITIVE_ROUTE = 'sensitive_route'
|
28
27
|
|
29
28
|
# Templates
|
30
|
-
|
29
|
+
CHALLENGE_TEMPLATE = 'block_template'
|
31
30
|
TEMPLATE_EXT = '.mustache'
|
31
|
+
RATELIMIT_TEMPLATE = 'ratelimit'
|
32
32
|
|
33
33
|
|
34
34
|
# Template Props
|
@@ -40,7 +40,14 @@ module PxModule
|
|
40
40
|
PROP_CUSTOM_LOGO = :customLogo
|
41
41
|
PROP_CSS_REF = :cssRef
|
42
42
|
PROP_JS_REF = :jsRef
|
43
|
-
|
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'
|
44
51
|
|
45
52
|
VISIBLE = 'visible'
|
46
53
|
HIDDEN = 'hidden'
|
@@ -49,4 +56,7 @@ module PxModule
|
|
49
56
|
TOKEN_HEADER = 'X-PX-AUTHORIZATION'
|
50
57
|
MOBILE_SDK_CONNECTION_ERROR = 'mobile_sdk_connection_error'
|
51
58
|
MOBILE_SDK_PINNING_ERROR = 'mobile_sdk_pinning_error'
|
59
|
+
|
60
|
+
# Regular Expressions
|
61
|
+
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}$/
|
52
62
|
end
|
@@ -12,7 +12,7 @@ module PxModule
|
|
12
12
|
def initialize(px_config)
|
13
13
|
@px_config = px_config
|
14
14
|
@logger = px_config[:logger]
|
15
|
-
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:
|
15
|
+
@logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:backend_url]}")
|
16
16
|
end
|
17
17
|
|
18
18
|
# Runs a POST command to Perimeter X servers
|
@@ -28,7 +28,7 @@ module PxModule
|
|
28
28
|
begin
|
29
29
|
@logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
|
30
30
|
response = Typhoeus.post(
|
31
|
-
"#{px_config[:
|
31
|
+
"#{px_config[:backend_url]}#{path}",
|
32
32
|
headers: headers,
|
33
33
|
body: body.to_json,
|
34
34
|
timeout: api_timeout,
|
@@ -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' ? px_config[:captcha_provider].downcase : 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] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
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] = false
|
33
37
|
|
34
38
|
return view.render.html_safe
|
35
39
|
end
|
@@ -86,10 +86,9 @@
|
|
86
86
|
}
|
87
87
|
</style>
|
88
88
|
<!-- Custom CSS -->
|
89
|
-
{{#
|
90
|
-
<link rel="stylesheet" type="text/css" href="{{
|
91
|
-
{{/
|
92
|
-
<script src="https://funcaptcha.com/fc/api/?onload=loadFunCaptcha" async defer></script>
|
89
|
+
{{#cssRef}}
|
90
|
+
<link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
|
91
|
+
{{/cssRef}}
|
93
92
|
</head>
|
94
93
|
|
95
94
|
<body>
|
@@ -106,10 +105,9 @@
|
|
106
105
|
</div>
|
107
106
|
<div class="content-wrapper">
|
108
107
|
<div class="content">
|
109
|
-
|
110
|
-
|
111
|
-
</
|
112
|
-
<div id="CAPTCHA"></div>
|
108
|
+
|
109
|
+
<div id="px-captcha">
|
110
|
+
</div>
|
113
111
|
<p>
|
114
112
|
Access to this page has been denied because we believe you are using automation tools to browse the
|
115
113
|
website.
|
@@ -144,35 +142,34 @@
|
|
144
142
|
</div>
|
145
143
|
</div>
|
146
144
|
</section>
|
147
|
-
<!--
|
145
|
+
<!-- Px -->
|
148
146
|
<script>
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
}
|
170
|
-
});
|
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
|
+
};
|
171
167
|
}
|
172
168
|
</script>
|
169
|
+
|
173
170
|
<!-- Custom Script -->
|
174
|
-
{{#
|
175
|
-
<script src="{{
|
176
|
-
{{/
|
171
|
+
{{#jsRef}}
|
172
|
+
<script src="{{{jsRef}}}"></script>
|
173
|
+
{{/jsRef}}
|
177
174
|
</body>
|
178
175
|
</html>
|
data/lib/perimeterx/version.rb
CHANGED
data/perimeter_x.gemspec
CHANGED
@@ -22,8 +22,8 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.bindir = "exe"
|
23
23
|
gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
24
|
gem.require_paths = ["lib"]
|
25
|
-
gem.add_development_dependency "bundler", "
|
26
|
-
gem.add_development_dependency "rake", "
|
25
|
+
gem.add_development_dependency "bundler", ">= 2.1"
|
26
|
+
gem.add_development_dependency "rake", ">= 12.3"
|
27
27
|
|
28
28
|
gem.extra_rdoc_files = ["readme.md", "changelog.md"]
|
29
29
|
gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "PerimeterX"]
|
@@ -33,7 +33,7 @@ Gem::Specification.new do |gem|
|
|
33
33
|
gem.add_dependency('concurrent-ruby', '~> 1.0', '>= 1.0.5')
|
34
34
|
gem.add_dependency('typhoeus', '~> 1.1', '>= 1.1.2')
|
35
35
|
gem.add_dependency('mustache', '~> 1.0', '>= 1.0.3')
|
36
|
-
gem.add_dependency('activesupport', '>=
|
36
|
+
gem.add_dependency('activesupport', '>= 5.2.4.3')
|
37
37
|
|
38
38
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
39
39
|
gem.add_development_dependency 'mocha', '~> 1.2', '>= 1.2.1'
|
data/readme.md
CHANGED
@@ -29,6 +29,7 @@ Table of Contents
|
|
29
29
|
* [Additional Page Activity Handler](#additional-page-activity-handler)
|
30
30
|
* [Monitor Only](#logging)
|
31
31
|
* [Debug Mode](#debug-mode)
|
32
|
+
* [Whitelist Routes](#whitelist-routes)
|
32
33
|
|
33
34
|
**[Contributing](#contributing)**
|
34
35
|
|
@@ -87,7 +88,7 @@ All parameters are obtainable via the PerimeterX Portal. (Applications and Polic
|
|
87
88
|
|
88
89
|
<a name="blocking-score"></a>**Changing the Minimum Score for Blocking**
|
89
90
|
|
90
|
-
>Note: Default blocking value:
|
91
|
+
>Note: Default blocking value: 100
|
91
92
|
|
92
93
|
```ruby
|
93
94
|
params = {
|
@@ -138,13 +139,13 @@ params = {
|
|
138
139
|
...
|
139
140
|
:custom_verification_handler => -> (px_ctx) {
|
140
141
|
block_score = px_ctx.context[:score];
|
141
|
-
|
142
|
+
client_uuid = px_ctx.context[:uuid];
|
142
143
|
full_url = px_ctx.context[:full_url];
|
143
144
|
|
144
145
|
html = "<html>
|
145
146
|
<body>
|
146
147
|
<div>Access to #{full_url} has been blocked.</div>
|
147
|
-
<div>Block reference - #{
|
148
|
+
<div>Block reference - #{client_uuid} </div>
|
148
149
|
<div>Block score - #{block_score} </div>
|
149
150
|
</body>
|
150
151
|
</html>".html_safe
|
@@ -235,12 +236,11 @@ params[:captcha_enabled] = false
|
|
235
236
|
|
236
237
|
The CAPTCHA part of the block page can use one of the following:
|
237
238
|
* [reCAPTCHA](https://www.google.com/recaptcha)
|
238
|
-
* [FunCaptcha](https://www.funcaptcha.com/)
|
239
239
|
|
240
240
|
Default: 'reCaptcha'
|
241
241
|
|
242
242
|
```ruby
|
243
|
-
captchaProvider = "
|
243
|
+
captchaProvider = "reCaptcha"
|
244
244
|
```
|
245
245
|
|
246
246
|
<a name="custom-uri"></a>**Custom URI**
|
@@ -306,6 +306,16 @@ Enables debug logging mode to STDOUT
|
|
306
306
|
params[:debug] = true
|
307
307
|
```
|
308
308
|
|
309
|
+
<a name="whitelist-routes"></a>**Whitelist Routes**
|
310
|
+
Default: []
|
311
|
+
An array of route prefixes and/or regular expressions that are always whitelisted and not validated by PerimeterX.
|
312
|
+
A string value of a path will be treated as a prefix.
|
313
|
+
A regexp value of a path will be treated as is.
|
314
|
+
|
315
|
+
```ruby
|
316
|
+
params[:whitelist_routes] = ["/example", /\A\/example\z/]
|
317
|
+
```
|
318
|
+
|
309
319
|
<a name="contributing"></a># Contributing #
|
310
320
|
------------------------------
|
311
321
|
The following steps are welcome when contributing to our project.
|