perimeter_x 2.2.1 → 2.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/Gemfile.lock +5 -19
- data/LICENSE.txt +1 -1
- data/changelog.md +24 -0
- data/lib/perimeter_x.rb +10 -5
- data/lib/perimeterx/configuration.rb +4 -2
- data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +1 -0
- data/lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb +1 -1
- data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +16 -8
- data/lib/perimeterx/internal/perimeter_x_context.rb +8 -8
- data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +2 -2
- data/lib/perimeterx/utils/px_constants.rb +2 -1
- data/lib/perimeterx/utils/px_template_factory.rb +2 -3
- data/lib/perimeterx/utils/templates/block_template.mustache +32 -163
- data/lib/perimeterx/version.rb +1 -1
- data/perimeter_x.gemspec +0 -1
- data/px_metadata.json +28 -0
- data/readme.md +14 -8
- metadata +4 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea98817074a862f9cf5dca2d01fd56ab31f3376e92fd695cd9a8a86c0713dea6
|
4
|
+
data.tar.gz: 302bf6d40f9e0e9a4776153a53fc5315bbd4047ccbc54dca5c082ecc6156900d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ab3a9adfcc046a69e9c0c43c7fb603ba62b7bc793919d0560eaeec2fcf52f5521dea5b9665883ecfc9fd667017898aebe73466bebf560af522a141184b13a13
|
7
|
+
data.tar.gz: 6a7ee7cd89f58d34edf035fc7762361738922ead805232bc607450df2479bef4d5545a0e55243b65d355ff36442ff9fd6089fbea3abcf626fdd3b54f9ce7c8ac
|
data/.gitignore
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
perimeter_x (2.
|
5
|
-
activesupport (>= 5.2.4.3)
|
4
|
+
perimeter_x (2.3.1)
|
6
5
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
7
6
|
mustache (~> 1.0, >= 1.0.3)
|
8
7
|
typhoeus (~> 1.1, >= 1.1.2)
|
@@ -10,20 +9,11 @@ PATH
|
|
10
9
|
GEM
|
11
10
|
remote: https://rubygems.org/
|
12
11
|
specs:
|
13
|
-
|
14
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
15
|
-
i18n (>= 0.7, < 2)
|
16
|
-
minitest (~> 5.1)
|
17
|
-
tzinfo (~> 1.1)
|
18
|
-
zeitwerk (~> 2.2, >= 2.2.2)
|
19
|
-
concurrent-ruby (1.1.6)
|
12
|
+
concurrent-ruby (1.1.10)
|
20
13
|
diff-lcs (1.4.4)
|
21
|
-
ethon (0.
|
22
|
-
ffi (>= 1.
|
23
|
-
ffi (1.
|
24
|
-
i18n (1.8.3)
|
25
|
-
concurrent-ruby (~> 1.0)
|
26
|
-
minitest (5.14.1)
|
14
|
+
ethon (0.15.0)
|
15
|
+
ffi (>= 1.15.0)
|
16
|
+
ffi (1.15.5)
|
27
17
|
mocha (1.11.2)
|
28
18
|
mustache (1.1.1)
|
29
19
|
rake (13.0.1)
|
@@ -40,12 +30,8 @@ GEM
|
|
40
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
41
31
|
rspec-support (~> 3.9.0)
|
42
32
|
rspec-support (3.9.3)
|
43
|
-
thread_safe (0.3.6)
|
44
33
|
typhoeus (1.4.0)
|
45
34
|
ethon (>= 0.9.0)
|
46
|
-
tzinfo (1.2.7)
|
47
|
-
thread_safe (~> 0.1)
|
48
|
-
zeitwerk (2.3.1)
|
49
35
|
|
50
36
|
PLATFORMS
|
51
37
|
ruby
|
data/LICENSE.txt
CHANGED
data/changelog.md
CHANGED
@@ -5,6 +5,30 @@ 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
|
+
## [2.3.2] - 2022-xx-xx
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
|
12
|
+
- Removed dependency on Active Support
|
13
|
+
- Replaced `eval()` calls with `JSON.parse()` for improved security
|
14
|
+
- Small spec alignment changes (risk_api and block activities)
|
15
|
+
|
16
|
+
## [2.3.1] - 2022-04-11
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
|
20
|
+
- URLs with query params did not render properly on new block page
|
21
|
+
|
22
|
+
## [2.3.0] - 2022-04-10
|
23
|
+
|
24
|
+
### Added
|
25
|
+
|
26
|
+
- Custom logo in block JSON response
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
|
30
|
+
- Updated block page to use new template
|
31
|
+
|
8
32
|
## [2.2.1] - 2020-09-27
|
9
33
|
### Fixed
|
10
34
|
- bypass_monitor_header type validation
|
data/lib/perimeter_x.rb
CHANGED
@@ -56,19 +56,22 @@ module PxModule
|
|
56
56
|
end
|
57
57
|
|
58
58
|
is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
|
59
|
-
action = px_ctx.context[:block_action][0,1]
|
59
|
+
action = px_ctx.context[:block_action][0,1]
|
60
|
+
block_script_uri = "/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}"
|
60
61
|
|
61
62
|
if px_config[:first_party_enabled]
|
62
63
|
px_template_object = {
|
63
64
|
js_client_src: "/#{px_config[:app_id][2..-1]}/init.js",
|
64
|
-
block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}
|
65
|
-
host_url: "/#{px_config[:app_id][2..-1]}/xhr"
|
65
|
+
block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}#{block_script_uri}",
|
66
|
+
host_url: "/#{px_config[:app_id][2..-1]}/xhr",
|
67
|
+
alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
|
66
68
|
}
|
67
69
|
else
|
68
70
|
px_template_object = {
|
69
71
|
js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
|
70
|
-
block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}
|
71
|
-
host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net"
|
72
|
+
block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}",
|
73
|
+
host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net",
|
74
|
+
alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
|
72
75
|
}
|
73
76
|
end
|
74
77
|
|
@@ -92,6 +95,8 @@ module PxModule
|
|
92
95
|
:vid => px_ctx.context[:vid],
|
93
96
|
:hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
|
94
97
|
:blockScript => px_template_object[:block_script],
|
98
|
+
:altBlockScript => px_template_object[:alt_block_script],
|
99
|
+
:customLogo => px_config[:custom_logo]
|
95
100
|
}
|
96
101
|
|
97
102
|
render :json => hash_json
|
@@ -82,8 +82,10 @@ module PxModule
|
|
82
82
|
# merge request configuration into the basic configuration
|
83
83
|
@configuration = @@basic_config.merge(params)
|
84
84
|
validate_hash_schema(@configuration, CONFIG_SCHEMA)
|
85
|
-
|
86
|
-
|
85
|
+
|
86
|
+
if (! @configuration.key?(:backend_url))
|
87
|
+
@configuration[:backend_url] = "https://sapi-#{@configuration[:app_id].downcase}.perimeterx.net"
|
88
|
+
end
|
87
89
|
@configuration[:logger] = PxLogger.new(@configuration[:debug])
|
88
90
|
end
|
89
91
|
end
|
@@ -63,6 +63,7 @@ module PxModule
|
|
63
63
|
:client_uuid => px_ctx.context[:uuid],
|
64
64
|
:block_score => px_ctx.context[:score],
|
65
65
|
:block_reason => px_ctx.context[:blocking_reason],
|
66
|
+
:block_action => px_ctx.context[:block_action],
|
66
67
|
:simulated_block => @px_config[:module_mode] == PxModule::MONITOR_MODE
|
67
68
|
}
|
68
69
|
|
@@ -22,7 +22,7 @@ module PxModule
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def valid_format?(cookie)
|
25
|
-
return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:
|
25
|
+
return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:a)
|
26
26
|
end
|
27
27
|
|
28
28
|
def cookie_block_action
|
@@ -1,6 +1,6 @@
|
|
1
|
-
require 'active_support/security_utils'
|
2
1
|
require 'base64'
|
3
2
|
require 'openssl'
|
3
|
+
require 'json'
|
4
4
|
require 'perimeterx/internal/exceptions/px_cookie_decryption_exception'
|
5
5
|
|
6
6
|
module PxModule
|
@@ -123,7 +123,7 @@ module PxModule
|
|
123
123
|
cipher.iv = iv
|
124
124
|
plaintext = cipher.update(cipher_text) + cipher.final
|
125
125
|
|
126
|
-
return
|
126
|
+
return JSON.parse(plaintext, symbolize_names: true)
|
127
127
|
rescue Exception => e
|
128
128
|
@logger.debug("PerimeterxCookie[decrypt]: Cookie decrypt fail #{e.message}")
|
129
129
|
raise PxCookieDecryptionException.new("Cookie decrypt fail => #{e.message}");
|
@@ -131,18 +131,26 @@ module PxModule
|
|
131
131
|
end
|
132
132
|
|
133
133
|
def decode(px_cookie)
|
134
|
-
return
|
134
|
+
return JSON.parse(Base64.decode64(px_cookie), symbolize_names: true)
|
135
135
|
end
|
136
136
|
|
137
137
|
|
138
138
|
def hmac_valid?(hmac_str, cookie_hmac)
|
139
139
|
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @cookie_secret, hmac_str)
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
140
|
+
password_correct = secure_compare(hmac, cookie_hmac)
|
141
|
+
end
|
142
|
+
|
143
|
+
def secure_compare(a, b)
|
144
|
+
# https://github.com/rails/rails/blob/main/activesupport/lib/active_support/security_utils.rb
|
145
|
+
if (a.bytesize != b.bytesize)
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
l = a.unpack "C#{a.bytesize}"
|
145
150
|
|
151
|
+
res = 0
|
152
|
+
b.each_byte { |byte| res |= byte ^ l.shift }
|
153
|
+
res == 0
|
146
154
|
end
|
147
155
|
end
|
148
156
|
end
|
@@ -101,17 +101,17 @@ module PxModule
|
|
101
101
|
@context[:sensitive_route] = check_sensitive_route(px_config[:sensitive_routes], @context[:uri])
|
102
102
|
end #end init
|
103
103
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
104
|
+
def check_sensitive_route(sensitive_routes, uri)
|
105
|
+
sensitive_routes.each do |sensitive_route|
|
106
|
+
return true if uri.start_with? sensitive_route
|
107
|
+
end
|
108
|
+
false
|
109
|
+
end
|
110
110
|
|
111
111
|
def set_block_action_type(action)
|
112
112
|
@context[:block_action] = case action
|
113
113
|
when 'c'
|
114
|
-
'captcha'
|
114
|
+
return 'captcha'
|
115
115
|
when 'b'
|
116
116
|
return 'block'
|
117
117
|
when 'j'
|
@@ -119,7 +119,7 @@ module PxModule
|
|
119
119
|
when 'r'
|
120
120
|
return 'rate_limit'
|
121
121
|
else
|
122
|
-
return captcha
|
122
|
+
return 'captcha'
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'json'
|
1
2
|
require 'perimeterx/internal/clients/perimeter_x_risk_client'
|
2
3
|
|
3
4
|
module PxModule
|
@@ -20,7 +21,6 @@ module PxModule
|
|
20
21
|
:request => {
|
21
22
|
:ip => px_ctx.context[:ip],
|
22
23
|
:headers => format_headers(px_ctx),
|
23
|
-
:uri => px_ctx.context[:uri],
|
24
24
|
:url => px_ctx.context[:full_url]
|
25
25
|
},
|
26
26
|
:additional => {
|
@@ -103,7 +103,7 @@ module PxModule
|
|
103
103
|
px_ctx.context[:made_s2s_risk_api_call] = true
|
104
104
|
|
105
105
|
# From here response should be valid, if success or error
|
106
|
-
response_body =
|
106
|
+
response_body = JSON.parse(response.body, symbolize_names: true);
|
107
107
|
# When success
|
108
108
|
if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
|
109
109
|
@logger.debug("PerimeterxS2SValidator[verify]: response ok")
|
@@ -36,11 +36,11 @@ module PxModule
|
|
36
36
|
PROP_APP_ID = :appId
|
37
37
|
PROP_VID = :vid
|
38
38
|
PROP_UUID = :uuid
|
39
|
-
PROP_LOGO_VISIBILITY = :logoVisibility
|
40
39
|
PROP_CUSTOM_LOGO = :customLogo
|
41
40
|
PROP_CSS_REF = :cssRef
|
42
41
|
PROP_JS_REF = :jsRef
|
43
42
|
PROP_BLOCK_SCRIPT = :blockScript
|
43
|
+
PROP_ALT_BLOCK_SCRIPT = :altBlockScript
|
44
44
|
PROP_JS_CLIENT_SRC = :jsClientSrc
|
45
45
|
PROP_HOST_URL = :hostUrl
|
46
46
|
PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
|
@@ -48,6 +48,7 @@ module PxModule
|
|
48
48
|
# Hosts
|
49
49
|
CLIENT_HOST = 'client.perimeterx.net'
|
50
50
|
CAPTCHA_HOST = 'captcha.px-cdn.net'
|
51
|
+
ALT_CAPTCHA_HOST = 'captcha.px-cloud.net'
|
51
52
|
|
52
53
|
VISIBLE = 'visible'
|
53
54
|
HIDDEN = 'hidden'
|
@@ -6,7 +6,7 @@ module PxModule
|
|
6
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
|
-
logger.debug('PxTemplateFactory[get_template]: px
|
9
|
+
logger.debug('PxTemplateFactory[get_template]: px challenge triggered')
|
10
10
|
return px_ctx.context[:block_action_data].html_safe
|
11
11
|
end
|
12
12
|
|
@@ -23,15 +23,14 @@ module PxModule
|
|
23
23
|
Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
|
24
24
|
|
25
25
|
view[PxModule::PROP_APP_ID] = px_config[:app_id]
|
26
|
-
view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
|
27
26
|
view[PxModule::PROP_VID] = px_ctx.context[:vid]
|
28
27
|
view[PxModule::PROP_UUID] = px_ctx.context[:uuid]
|
29
28
|
view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
|
30
29
|
view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
|
31
30
|
view[PxModule::PROP_JS_REF] = px_config[:js_ref]
|
32
31
|
view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
|
33
|
-
view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
|
34
32
|
view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
|
33
|
+
view[PxModule::PROP_ALT_BLOCK_SCRIPT] = px_template_object[:alt_block_script]
|
35
34
|
view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
|
36
35
|
view[PxModule::PROP_FIRST_PARTY_ENABLED] = px_ctx.context[:first_party_enabled]
|
37
36
|
|
@@ -3,173 +3,42 @@
|
|
3
3
|
<head>
|
4
4
|
<meta charset="utf-8">
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
-
<
|
7
|
-
<
|
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 -->
|
6
|
+
<meta name="description" content="px-captcha">
|
7
|
+
<title>Access to this page has been denied</title>
|
89
8
|
{{#cssRef}}
|
90
9
|
<link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
|
91
10
|
{{/cssRef}}
|
92
11
|
</head>
|
93
|
-
|
94
12
|
<body>
|
95
|
-
<
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
<p>
|
112
|
-
Access to this page has been denied because we believe you are using automation tools to browse the
|
113
|
-
website.
|
114
|
-
</p>
|
115
|
-
<p>
|
116
|
-
This may happen as a result of the following:
|
117
|
-
</p>
|
118
|
-
<ul>
|
119
|
-
<li>
|
120
|
-
Javascript is disabled or blocked by an extension (ad blockers for example)
|
121
|
-
</li>
|
122
|
-
<li>
|
123
|
-
Your browser does not support cookies
|
124
|
-
</li>
|
125
|
-
</ul>
|
126
|
-
<p>
|
127
|
-
Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
|
128
|
-
them from loading.
|
129
|
-
</p>
|
130
|
-
<p>
|
131
|
-
Reference ID: #{{refId}}
|
132
|
-
</p>
|
133
|
-
</div>
|
134
|
-
</div>
|
135
|
-
<div class="page-footer-wrapper">
|
136
|
-
<div class="page-footer">
|
137
|
-
<p>
|
138
|
-
Powered by
|
139
|
-
<a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
|
140
|
-
, Inc.
|
141
|
-
</p>
|
142
|
-
</div>
|
143
|
-
</div>
|
144
|
-
</section>
|
145
|
-
<!-- Px -->
|
146
|
-
<script>
|
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);
|
13
|
+
<script>
|
14
|
+
window._pxVid = '{{vid}}';
|
15
|
+
window._pxUuid = '{{uuid}}';
|
16
|
+
window._pxAppId = '{{appId}}';
|
17
|
+
window._pxCustomLogo = '{{{customLogo}}}';
|
18
|
+
window._pxHostUrl = '{{{hostUrl}}}';
|
19
|
+
window._pxJsClientSrc = '{{{jsClientSrc}}}';
|
20
|
+
window._pxFirstPartyEnabled = {{firstPartyEnabled}};
|
21
|
+
var script = document.createElement('script');
|
22
|
+
script.src = '{{{blockScript}}}';
|
23
|
+
document.head.appendChild(script);
|
24
|
+
script.onerror = function () {
|
25
|
+
script = document.createElement('script');
|
26
|
+
script.src = '{{{altBlockScript}}}';
|
27
|
+
script.onerror = window._pxDisplayErrorMessage;
|
28
|
+
document.head.appendChild(script);
|
166
29
|
};
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
30
|
+
window._pxDisplayErrorMessage = function () {
|
31
|
+
var style = document.createElement('style');
|
32
|
+
style.innerText = '@import url(https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap);body{background-color:#fafbfc}@media (max-width:480px){body{background-color:#fff}}.px-captcha-error-container{position:fixed;height:328px;background-color:#fff;font-family:Roboto,sans-serif}.px-captcha-error-header{color:#f0f1f2;font-size:29px;margin:67px 0 33px;font-weight:500;line-height:.83;text-align:center}.px-captcha-error-message{color:#f0f1f2;font-size:18px;margin:0 0 29px;line-height:1.33;text-align:center}div.px-captcha-error-button{text-align:center;line-height:50px;width:253px;margin:auto;border-radius:25px;border:solid 1px #f0f1f2;font-size:20px;color:#f0f1f2}div.px-captcha-error-wrapper{margin:23px 0 0}div.px-captcha-error{margin:auto;text-align:center;width:400px;height:30px;font-size:12px;background-color:#fcf0f2;color:#ce0e2d}img.px-captcha-error{margin:6px 10px -2px 0}@media (min-width:620px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (min-width:481px) and (max-width:620px){.px-captcha-error-container{width:85%;top:50%;left:50%;margin-top:-164px;margin-left:-42.5%;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (max-width:480px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px}}';
|
33
|
+
document.head.appendChild(style);
|
34
|
+
var div = document.createElement('div');
|
35
|
+
div.className = 'px-captcha-error-container';
|
36
|
+
div.innerHTML = '<div class="px-captcha-error-header">Before we continue...</div><div class="px-captcha-error-message">Press & Hold to confirm you are<br>a human (and not a bot).</div><div class="px-captcha-error-button">Press & Hold</div><div class="px-captcha-error-wrapper"><div class="px-captcha-error"><img class="px-captcha-error" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAQCAMAAADDGrRQAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABFUExURUdwTNYELOEGONQILd0AONwALtwEL+AAL9MFLfkJSNQGLdMJLdQJLdQGLdQKLtYFLNcELdUGLdcBL9gFL88OLdUFLNEOLglBhT4AAAAXdFJOUwC8CqgNIRgRoAS1dWWuR4RTjzgryZpYblfkcAAAAI9JREFUGNNdj+sWhCAIhAdvqGVa1r7/oy6RZ7eaH3D4ZACBIed9wlOOMtUnSrEmZ6cHa9YAIfsbCkWrdpi/c50Bk2CO9mNLdMAu03wJA3HpEnfpxbyOg6ruyx8JJi6KNstnslp1dbPd9GnqmuYq7mmcv1zjnbQw8cV0xzkqo+fX1zkjUOO7wnrInUTxJiruC3vtBNRoQQn2AAAAAElFTkSuQmCC">Please check your network connection or disable your ad-blocker.</div></div>';
|
37
|
+
document.body.appendChild(div);
|
38
|
+
};
|
39
|
+
</script>
|
40
|
+
{{#jsRef}}
|
41
|
+
<script src="{{{jsRef}}}"></script>
|
42
|
+
{{/jsRef}}
|
174
43
|
</body>
|
175
|
-
</html>
|
44
|
+
</html>
|
data/lib/perimeterx/version.rb
CHANGED
data/perimeter_x.gemspec
CHANGED
@@ -33,7 +33,6 @@ 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', '>= 5.2.4.3')
|
37
36
|
|
38
37
|
gem.add_development_dependency 'rspec', '~> 3.0'
|
39
38
|
gem.add_development_dependency 'mocha', '~> 1.2', '>= 1.2.1'
|
data/px_metadata.json
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"version": "2.3.2",
|
3
|
+
"supported_features": [
|
4
|
+
"additional_activity_handler",
|
5
|
+
"advanced_blocking_response",
|
6
|
+
"batched_activities",
|
7
|
+
"block_activity",
|
8
|
+
"block_page_captcha",
|
9
|
+
"block_page_rate_limit",
|
10
|
+
"bypass_monitor_header",
|
11
|
+
"client_ip_extraction",
|
12
|
+
"cookie_v3",
|
13
|
+
"css_ref",
|
14
|
+
"custom_logo",
|
15
|
+
"logger",
|
16
|
+
"filter_by_route",
|
17
|
+
"first_party",
|
18
|
+
"js_ref",
|
19
|
+
"mobile_support",
|
20
|
+
"module_enable",
|
21
|
+
"module_mode",
|
22
|
+
"page_requested_activity",
|
23
|
+
"vid_extraction",
|
24
|
+
"risk_api",
|
25
|
+
"sensitive_headers",
|
26
|
+
"sensitive_routes"
|
27
|
+
]
|
28
|
+
}
|
data/readme.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
[PerimeterX](http://www.perimeterx.com) Ruby SDK
|
6
6
|
=============================================================
|
7
7
|
|
8
|
-
> Latest stable version: [v2.2
|
8
|
+
> Latest stable version: [v2.3.2](https://rubygems.org/gems/perimeter_x)
|
9
9
|
|
10
10
|
Table of Contents
|
11
11
|
-----------------
|
@@ -31,6 +31,7 @@ Table of Contents
|
|
31
31
|
* [Update Configuration on Runtime](#update-config)
|
32
32
|
* [First Party](#first-party)
|
33
33
|
|
34
|
+
**[Additional Information](#additional-information)**
|
34
35
|
**[Contributing](#contributing)**
|
35
36
|
|
36
37
|
<a name="Usage"></a>
|
@@ -160,14 +161,15 @@ params = {
|
|
160
161
|
|
161
162
|
> Note: IP extraction, according to your network setup, is very important. It is common to have a load balancer/proxy on top of your applications, in which case the PerimeterX module will send the system's internal IP as the user's. In order to properly perform processing and detection on server-to-server calls, PerimeterX module needs the real user's IP.
|
162
163
|
|
163
|
-
By default the clients IP is taken from the
|
164
|
+
By default the clients IP is taken from the `REMOTE_ADDR` header, in case the user decides to use different header or custom function that extract the header the following key should be added to the configuration.
|
164
165
|
|
165
166
|
***Custom header***
|
167
|
+
> Note: If there are multiple headers configured, the module will evaluate the headers in order and use the first value it finds.
|
166
168
|
```ruby
|
167
169
|
configuration = {
|
168
170
|
"app_id" => <APP_ID>,
|
169
171
|
"auth_token" => <AUTH_TOKEN>,
|
170
|
-
"
|
172
|
+
"ip_headers" => [<HTTP_HEADER_NAME>, <BACKUP_HTTP_HEADER_NAME>],
|
171
173
|
```
|
172
174
|
|
173
175
|
***Custom Function***
|
@@ -177,7 +179,7 @@ configuration = {
|
|
177
179
|
configuration = {
|
178
180
|
"app_id" => <APP_ID>,
|
179
181
|
"auth_token" => <AUTH_TOKEN>,
|
180
|
-
"
|
182
|
+
"ip_header_function" => -> (req) {
|
181
183
|
# Method body
|
182
184
|
return "1.2.3.4"
|
183
185
|
}
|
@@ -213,14 +215,14 @@ params = [
|
|
213
215
|
> Note: Custom logo/js/css can be added together
|
214
216
|
|
215
217
|
<a name="logging"></a>**No Blocking, Monitor Only**
|
216
|
-
Default mode: PxModule::
|
218
|
+
Default mode: PxModule::MONITOR_MODE
|
217
219
|
|
218
220
|
- PxModule::ACTIVE_MODE - Module blocks users crossing the predefined block threshold. Server-to-server requests are sent synchronously.
|
219
221
|
|
220
|
-
- PxModule
|
222
|
+
- PxModule::MONITOR_MODE - Module does not block users crossing the predefined block threshold. The `custom_block_handler` function will be eval'd in case one is supplied, upon crossing the defined block threshold.
|
221
223
|
|
222
224
|
```ruby
|
223
|
-
params[:module_mode] = PxModule::
|
225
|
+
params[:module_mode] = PxModule::ACTIVE_MODE
|
224
226
|
```
|
225
227
|
|
226
228
|
<a name="custom-uri"></a>**Custom URI**
|
@@ -350,8 +352,12 @@ Default: true
|
|
350
352
|
params[:first_party_enabled] = false
|
351
353
|
```
|
352
354
|
|
355
|
+
<a name="additional_information"></a> Additional Information
|
356
|
+
------------------------------
|
357
|
+
### URI Delimiters
|
358
|
+
PerimeterX processes URI paths with general- and sub-delimiters according to RFC 3986. General delimiters (e.g., `?`, `#`) are used to separate parts of the URI. Sub-delimiters (e.g., `$`, `&`) are not used to split the URI as they are considered valid characters in the URI path.
|
353
359
|
|
354
|
-
<a name="contributing"></a
|
360
|
+
<a name="contributing"></a> Contributing
|
355
361
|
------------------------------
|
356
362
|
The following steps are welcome when contributing to our project.
|
357
363
|
###Fork/Clone
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perimeter_x
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.2
|
4
|
+
version: 2.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nitzan Goldfeder
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -98,20 +98,6 @@ dependencies:
|
|
98
98
|
- - ">="
|
99
99
|
- !ruby/object:Gem::Version
|
100
100
|
version: 1.0.3
|
101
|
-
- !ruby/object:Gem::Dependency
|
102
|
-
name: activesupport
|
103
|
-
requirement: !ruby/object:Gem::Requirement
|
104
|
-
requirements:
|
105
|
-
- - ">="
|
106
|
-
- !ruby/object:Gem::Version
|
107
|
-
version: 5.2.4.3
|
108
|
-
type: :runtime
|
109
|
-
prerelease: false
|
110
|
-
version_requirements: !ruby/object:Gem::Requirement
|
111
|
-
requirements:
|
112
|
-
- - ">="
|
113
|
-
- !ruby/object:Gem::Version
|
114
|
-
version: 5.2.4.3
|
115
101
|
- !ruby/object:Gem::Dependency
|
116
102
|
name: rspec
|
117
103
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,6 +177,7 @@ files:
|
|
191
177
|
- lib/perimeterx/utils/templates/ratelimit.mustache
|
192
178
|
- lib/perimeterx/version.rb
|
193
179
|
- perimeter_x.gemspec
|
180
|
+
- px_metadata.json
|
194
181
|
- readme.md
|
195
182
|
homepage: https://www.perimeterx.com
|
196
183
|
licenses:
|
@@ -215,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
215
202
|
- !ruby/object:Gem::Version
|
216
203
|
version: '0'
|
217
204
|
requirements: []
|
218
|
-
rubygems_version: 3.0.3
|
205
|
+
rubygems_version: 3.0.3.1
|
219
206
|
signing_key:
|
220
207
|
specification_version: 4
|
221
208
|
summary: PerimeterX ruby implmentation
|