perimeter_x 1.4.0 → 2.0.0
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 +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.
|