perimeter_x 2.1.0 → 2.3.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 +4 -4
- data/LICENSE.txt +1 -1
- data/changelog.md +18 -0
- data/lib/perimeter_x.rb +58 -11
- data/lib/perimeterx/configuration.rb +6 -3
- data/lib/perimeterx/internal/first_party/px_first_party.rb +124 -0
- data/lib/perimeterx/internal/perimeter_x_context.rb +32 -25
- data/lib/perimeterx/utils/px_constants.rb +4 -3
- data/lib/perimeterx/utils/px_http_client.rb +57 -0
- data/lib/perimeterx/utils/px_template_factory.rb +4 -5
- data/lib/perimeterx/utils/templates/block_template.mustache +32 -163
- data/lib/perimeterx/version.rb +1 -1
- data/px_metadata.json +28 -0
- data/readme.md +34 -25
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7321ac61fcf5c1b2b8a573eb23744d807e0af5e430a3468cb4704e8d36c7f8a9
|
4
|
+
data.tar.gz: 5bd64b4340535a52d2f91db99cabb673693dec8dbf1526dd00d2aa200743a444
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3037218a40b007fd7a0aaf93c6fe2e367b622042f2653c2e08e3c9a5d53d12d0107374d81e841d46e9320562d95c2ac4957040d7cb3a932c971ab2a1ff16aed2
|
7
|
+
data.tar.gz: 5de6563d962cee1d9ba90ac370ecb4a0a8679dfab9a1ab69b2dbcfaafe54d3ce05b12f31b8c57f1a5b129299e215dd0e8f7e488bbadf03308099104ec7f2ab60
|
data/LICENSE.txt
CHANGED
data/changelog.md
CHANGED
@@ -5,6 +5,24 @@ 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.0] - 2022-04-10
|
9
|
+
|
10
|
+
### Added
|
11
|
+
|
12
|
+
- Custom logo in block JSON response
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
- Updated block page to use new template
|
17
|
+
|
18
|
+
## [2.2.1] - 2020-09-27
|
19
|
+
### Fixed
|
20
|
+
- bypass_monitor_header type validation
|
21
|
+
|
22
|
+
## [2.2.0] - 2020-09-15
|
23
|
+
### Added
|
24
|
+
- First Party
|
25
|
+
|
8
26
|
## [2.1.0] - 2020-09-01
|
9
27
|
### Added
|
10
28
|
- Added option to set a different px configuration on each request
|
data/lib/perimeter_x.rb
CHANGED
@@ -12,13 +12,23 @@ require 'perimeterx/internal/clients/perimeter_x_activity_client'
|
|
12
12
|
require 'perimeterx/internal/validators/perimeter_x_s2s_validator'
|
13
13
|
require 'perimeterx/internal/validators/perimeter_x_cookie_validator'
|
14
14
|
require 'perimeterx/internal/exceptions/px_config_exception'
|
15
|
+
require 'perimeterx/internal/first_party/px_first_party'
|
15
16
|
|
16
17
|
module PxModule
|
17
18
|
# Module expose API
|
18
19
|
def px_verify_request(request_config={})
|
19
20
|
begin
|
20
21
|
px_instance = PerimeterX.new(request_config)
|
21
|
-
|
22
|
+
req = ActionDispatch::Request.new(request.env)
|
23
|
+
|
24
|
+
# handle first party requests
|
25
|
+
if px_instance.first_party.is_first_party_request(req)
|
26
|
+
render_first_party_response(req, px_instance)
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
|
30
|
+
# verify request
|
31
|
+
px_ctx = px_instance.verify(req)
|
22
32
|
px_config = px_instance.px_config
|
23
33
|
|
24
34
|
msg_title = 'PxModule[px_verify_request]'
|
@@ -47,11 +57,23 @@ module PxModule
|
|
47
57
|
|
48
58
|
is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
|
49
59
|
action = px_ctx.context[:block_action][0,1]
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
60
|
+
block_script_uri = "/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}"
|
61
|
+
|
62
|
+
if px_config[:first_party_enabled]
|
63
|
+
px_template_object = {
|
64
|
+
js_client_src: "/#{px_config[:app_id][2..-1]}/init.js",
|
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}"
|
68
|
+
}
|
69
|
+
else
|
70
|
+
px_template_object = {
|
71
|
+
js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
|
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}"
|
75
|
+
}
|
76
|
+
end
|
55
77
|
|
56
78
|
html = PxTemplateFactory.get_template(px_ctx, px_config, px_template_object)
|
57
79
|
|
@@ -68,11 +90,13 @@ module PxModule
|
|
68
90
|
hash_json = {
|
69
91
|
:appId => px_config[:app_id],
|
70
92
|
:jsClientSrc => px_template_object[:js_client_src],
|
71
|
-
:firstPartyEnabled =>
|
93
|
+
:firstPartyEnabled => px_ctx.context[:first_party_enabled],
|
72
94
|
:uuid => px_ctx.context[:uuid],
|
73
95
|
:vid => px_ctx.context[:vid],
|
74
96
|
:hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
|
75
97
|
:blockScript => px_template_object[:block_script],
|
98
|
+
:altBlockScript => px_template_object[:alt_block_script],
|
99
|
+
:customLogo => px_config[:custom_logo]
|
76
100
|
}
|
77
101
|
|
78
102
|
render :json => hash_json
|
@@ -110,6 +134,29 @@ module PxModule
|
|
110
134
|
end
|
111
135
|
end
|
112
136
|
|
137
|
+
def render_first_party_response(req, px_instance)
|
138
|
+
fp = px_instance.first_party
|
139
|
+
px_config = px_instance.px_config
|
140
|
+
|
141
|
+
if px_config[:first_party_enabled]
|
142
|
+
# first party enabled - proxy response
|
143
|
+
fp_response = fp.send_first_party_request(req)
|
144
|
+
response.status = fp_response.code
|
145
|
+
fp_response.to_hash.each do |header_name, header_value_arr|
|
146
|
+
if header_name!="content-length"
|
147
|
+
response.headers[header_name] = header_value_arr[0]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
res_type = fp.get_response_content_type(req)
|
151
|
+
render res_type => fp_response.body
|
152
|
+
else
|
153
|
+
# first party disabled - return empty response
|
154
|
+
response.status = 200
|
155
|
+
res_type = fp.get_response_content_type(req)
|
156
|
+
render res_type => ""
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
113
160
|
def self.configure(basic_config)
|
114
161
|
PerimeterX.set_basic_config(basic_config)
|
115
162
|
end
|
@@ -119,6 +166,7 @@ module PxModule
|
|
119
166
|
class PerimeterX
|
120
167
|
|
121
168
|
attr_reader :px_config
|
169
|
+
attr_reader :first_party
|
122
170
|
attr_accessor :px_http_client
|
123
171
|
attr_accessor :px_activity_client
|
124
172
|
|
@@ -128,7 +176,7 @@ module PxModule
|
|
128
176
|
end
|
129
177
|
|
130
178
|
#Instance Methods
|
131
|
-
def verify(
|
179
|
+
def verify(req)
|
132
180
|
begin
|
133
181
|
|
134
182
|
# check module_enabled
|
@@ -137,13 +185,11 @@ module PxModule
|
|
137
185
|
@logger.warn('Module is disabled')
|
138
186
|
return nil
|
139
187
|
end
|
140
|
-
|
141
|
-
req = ActionDispatch::Request.new(env)
|
142
188
|
|
143
189
|
# filter whitelist routes
|
144
190
|
url_path = URI.parse(req.original_url).path
|
145
191
|
if url_path && !url_path.empty?
|
146
|
-
if check_whitelist_routes(px_config[:whitelist_routes], url_path)
|
192
|
+
if check_whitelist_routes(px_config[:whitelist_routes], url_path)
|
147
193
|
@logger.debug("PerimeterX[pxVerify]: whitelist route: #{url_path}")
|
148
194
|
return nil
|
149
195
|
end
|
@@ -176,6 +222,7 @@ module PxModule
|
|
176
222
|
@px_http_client = PxHttpClient.new(@px_config)
|
177
223
|
|
178
224
|
@px_activity_client = PerimeterxActivitiesClient.new(@px_config, @px_http_client)
|
225
|
+
@first_party = FirstPartyManager.new(@px_config, @px_http_client, @logger)
|
179
226
|
|
180
227
|
@px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
|
181
228
|
@px_s2s_validator = PerimeterxS2SValidator.new(@px_config, @px_http_client)
|
@@ -30,7 +30,8 @@ module PxModule
|
|
30
30
|
:ip_headers => [],
|
31
31
|
:ip_header_function => nil,
|
32
32
|
:bypass_monitor_header => nil,
|
33
|
-
:risk_cookie_max_iterations => 5000
|
33
|
+
:risk_cookie_max_iterations => 5000,
|
34
|
+
:first_party_enabled => true
|
34
35
|
}
|
35
36
|
|
36
37
|
CONFIG_SCHEMA = {
|
@@ -53,14 +54,16 @@ module PxModule
|
|
53
54
|
:whitelist_routes => {types: [Array], allowed_element_types: [String, Regexp], required: false},
|
54
55
|
:ip_headers => {types: [Array], allowed_element_types: [String], required: false},
|
55
56
|
:ip_header_function => {types: [Proc], required: false},
|
56
|
-
:bypass_monitor_header => {types: [
|
57
|
+
:bypass_monitor_header => {types: [String], required: false},
|
57
58
|
:risk_cookie_max_iterations => {types: [Integer], required: false},
|
58
59
|
:custom_verification_handler => {types: [Proc], required: false},
|
59
60
|
:additional_activity_handler => {types: [Proc], required: false},
|
60
61
|
:custom_logo => {types: [String], required: false},
|
61
62
|
:css_ref => {types: [String], required: false},
|
62
63
|
:js_ref => {types: [String], required: false},
|
63
|
-
:custom_uri => {types: [Proc], required: false}
|
64
|
+
:custom_uri => {types: [Proc], required: false},
|
65
|
+
:first_party_enabled => {types: [FalseClass, TrueClass], required: false}
|
66
|
+
|
64
67
|
}
|
65
68
|
|
66
69
|
def self.set_basic_config(basic_config)
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'perimeterx/internal/perimeter_x_context'
|
2
|
+
module PxModule
|
3
|
+
class FirstPartyManager
|
4
|
+
def initialize(px_config, px_http_client, logger)
|
5
|
+
@px_config = px_config
|
6
|
+
@app_id = px_config[:app_id]
|
7
|
+
@px_http_client = px_http_client
|
8
|
+
@logger = logger
|
9
|
+
@from = [
|
10
|
+
"/#{@app_id[2..-1]}/init.js",
|
11
|
+
"/#{@app_id[2..-1]}/captcha",
|
12
|
+
"/#{@app_id[2..-1]}/xhr"
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def send_first_party_request(req)
|
17
|
+
uri = URI.parse(req.original_url)
|
18
|
+
url_path = uri.path
|
19
|
+
|
20
|
+
headers = extract_headers(req)
|
21
|
+
headers["x-px-first-party"] = "1"
|
22
|
+
headers["x-px-enforcer-true-ip"] = PerimeterXContext.extract_ip(req, @px_config)
|
23
|
+
|
24
|
+
if url_path.start_with?(@from[0])
|
25
|
+
return get_client(req, uri, headers)
|
26
|
+
elsif url_path.start_with?(@from[1])
|
27
|
+
return get_captcha(req, uri, headers)
|
28
|
+
elsif url_path.start_with?(@from[2])
|
29
|
+
return send_xhr(req, uri, headers)
|
30
|
+
else
|
31
|
+
return nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def get_client(req, uri, headers)
|
36
|
+
@logger.debug("FirstPartyManager[get_client]")
|
37
|
+
|
38
|
+
# define host
|
39
|
+
headers["host"] = PxModule::CLIENT_HOST
|
40
|
+
|
41
|
+
# define request url
|
42
|
+
url = "#{uri.scheme}://#{PxModule::CLIENT_HOST}/#{@app_id}/main.min.js"
|
43
|
+
|
44
|
+
# send request
|
45
|
+
return @px_http_client.get(url, headers)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get_captcha(req, uri, headers)
|
49
|
+
@logger.debug("FirstPartyManager[get_captcha]")
|
50
|
+
|
51
|
+
# define host
|
52
|
+
headers["host"] = PxModule::CAPTCHA_HOST
|
53
|
+
|
54
|
+
# define request url
|
55
|
+
path_and_query = uri.request_uri
|
56
|
+
uri_suffix = path_and_query.sub "/#{@app_id[2..-1]}/captcha", ""
|
57
|
+
url = "#{uri.scheme}://#{PxModule::CAPTCHA_HOST}#{uri_suffix}"
|
58
|
+
|
59
|
+
# send request
|
60
|
+
return @px_http_client.get(url, headers)
|
61
|
+
end
|
62
|
+
|
63
|
+
def send_xhr(req, uri, headers)
|
64
|
+
@logger.debug("FirstPartyManager[send_xhr]")
|
65
|
+
|
66
|
+
# handle vid cookies
|
67
|
+
if !req.cookies.nil?
|
68
|
+
if req.cookies.key?("_pxvid")
|
69
|
+
vid = PerimeterXContext.force_utf8(req.cookies["_pxvid"])
|
70
|
+
if headers.key?('cookie')
|
71
|
+
headers['cookie'] += "; pxvid=#{vid}";
|
72
|
+
else
|
73
|
+
headers['cookie'] = "pxvid=#{vid}";
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# define host
|
79
|
+
headers["host"] = "collector-#{@app_id.downcase}.perimeterx.net"
|
80
|
+
|
81
|
+
# define request url
|
82
|
+
path_and_query = uri.request_uri
|
83
|
+
path_suffix = path_and_query.sub "/#{@app_id[2..-1]}/xhr", ""
|
84
|
+
url = "#{uri.scheme}://collector-#{@app_id.downcase}.perimeterx.net#{path_suffix}"
|
85
|
+
|
86
|
+
# send request
|
87
|
+
return @px_http_client.post_xhr(url, req.body.string, headers)
|
88
|
+
end
|
89
|
+
|
90
|
+
def extract_headers(req)
|
91
|
+
headers = Hash.new
|
92
|
+
req.headers.each do |k, v|
|
93
|
+
if (k.start_with? 'HTTP_') && (!@px_config[:sensitive_headers].include? k)
|
94
|
+
header = k.to_s.gsub('HTTP_', '')
|
95
|
+
header = header.gsub('_', '-').downcase
|
96
|
+
headers[header] = PerimeterXContext.force_utf8(v)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
return headers
|
100
|
+
end
|
101
|
+
|
102
|
+
# -1 - not first party request
|
103
|
+
# 0 - /init.js
|
104
|
+
# 1 - /captcha
|
105
|
+
# 2 - /xhr
|
106
|
+
def get_first_party_request_type(req)
|
107
|
+
url_path = URI.parse(req.original_url).path
|
108
|
+
@from.each_with_index do |val,index|
|
109
|
+
if url_path.start_with?(val)
|
110
|
+
return index
|
111
|
+
end
|
112
|
+
end
|
113
|
+
return -1
|
114
|
+
end
|
115
|
+
|
116
|
+
def is_first_party_request(req)
|
117
|
+
return get_first_party_request_type(req) != -1
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_response_content_type(req)
|
121
|
+
return get_first_party_request_type(req) == 2 ? :json : :js
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -6,6 +6,28 @@ module PxModule
|
|
6
6
|
|
7
7
|
attr_accessor :context
|
8
8
|
attr_accessor :px_config
|
9
|
+
|
10
|
+
# class methods
|
11
|
+
|
12
|
+
def self.extract_ip(req, px_config)
|
13
|
+
# Get IP from header/custom function
|
14
|
+
if px_config[:ip_headers].length() > 0
|
15
|
+
px_config[:ip_headers].each do |ip_header|
|
16
|
+
if req.headers[ip_header]
|
17
|
+
return PerimeterXContext.force_utf8(req.headers[ip_header])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
elsif px_config[:ip_header_function] != nil
|
21
|
+
return px_config[:ip_header_function].call(req)
|
22
|
+
end
|
23
|
+
return req.ip
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.force_utf8(str)
|
27
|
+
return str.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
28
|
+
end
|
29
|
+
|
30
|
+
# instance methods
|
9
31
|
|
10
32
|
def initialize(px_config, req)
|
11
33
|
@logger = px_config[:logger]
|
@@ -16,33 +38,22 @@ module PxModule
|
|
16
38
|
@context[:headers] = Hash.new
|
17
39
|
@context[:cookie_origin] = 'cookie'
|
18
40
|
@context[:made_s2s_risk_api_call] = false
|
41
|
+
@context[:first_party_enabled] = px_config[:first_party_enabled]
|
42
|
+
|
19
43
|
cookies = req.cookies
|
20
44
|
|
21
|
-
|
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
|
45
|
+
@context[:ip] = PerimeterXContext.extract_ip(req, px_config)
|
35
46
|
|
36
47
|
# Get token from header
|
37
48
|
if req.headers[PxModule::TOKEN_HEADER]
|
38
49
|
@context[:cookie_origin] = 'header'
|
39
|
-
token = force_utf8(req.headers[PxModule::TOKEN_HEADER])
|
50
|
+
token = PerimeterXContext.force_utf8(req.headers[PxModule::TOKEN_HEADER])
|
40
51
|
if token.match(PxModule::MOBILE_TOKEN_V3_REGEX)
|
41
52
|
@context[:px_cookie][:v3] = token[2..-1]
|
42
53
|
elsif token.match(PxModule::MOBILE_ERROR_REGEX)
|
43
54
|
@context[:mobile_error] = token
|
44
55
|
if req.headers[PxModule::ORIGINAL_TOKEN_HEADER]
|
45
|
-
token = force_utf8(req.headers[PxModule::ORIGINAL_TOKEN_HEADER])
|
56
|
+
token = PerimeterXContext.force_utf8(req.headers[PxModule::ORIGINAL_TOKEN_HEADER])
|
46
57
|
if token.match(PxModule::MOBILE_TOKEN_V3_REGEX)
|
47
58
|
@context[:px_cookie][:v3] = token[2..-1]
|
48
59
|
end
|
@@ -53,13 +64,13 @@ module PxModule
|
|
53
64
|
cookies.each do |k, v|
|
54
65
|
case k.to_s
|
55
66
|
when '_px3'
|
56
|
-
@context[:px_cookie][:v3] = force_utf8(v)
|
67
|
+
@context[:px_cookie][:v3] = PerimeterXContext.force_utf8(v)
|
57
68
|
when '_px'
|
58
|
-
@context[:px_cookie][:v1] = force_utf8(v)
|
69
|
+
@context[:px_cookie][:v1] = PerimeterXContext.force_utf8(v)
|
59
70
|
when '_pxvid'
|
60
71
|
if v.is_a?(String) && v.match(PxModule::VID_REGEX)
|
61
72
|
@context[:vid_source] = "vid_cookie"
|
62
|
-
@context[:vid] = force_utf8(v)
|
73
|
+
@context[:vid] = PerimeterXContext.force_utf8(v)
|
63
74
|
end
|
64
75
|
end
|
65
76
|
end #end case
|
@@ -69,10 +80,10 @@ module PxModule
|
|
69
80
|
if (k.start_with? 'HTTP_')
|
70
81
|
header = k.to_s.gsub('HTTP_', '')
|
71
82
|
header = header.gsub('_', '-').downcase
|
72
|
-
@context[:headers][header.to_sym] = force_utf8(v)
|
83
|
+
@context[:headers][header.to_sym] = PerimeterXContext.force_utf8(v)
|
73
84
|
end
|
74
85
|
end #end headers foreach
|
75
|
-
|
86
|
+
|
76
87
|
@context[:hostname]= req.server_name
|
77
88
|
@context[:user_agent] = req.user_agent ? req.user_agent : ''
|
78
89
|
@context[:uri] = px_config[:custom_uri] ? px_config[:custom_uri].call(req) : req.fullpath
|
@@ -97,10 +108,6 @@ module PxModule
|
|
97
108
|
false
|
98
109
|
end
|
99
110
|
|
100
|
-
def force_utf8(str)
|
101
|
-
return str.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
|
102
|
-
end
|
103
|
-
|
104
111
|
def set_block_action_type(action)
|
105
112
|
@context[:block_action] = case action
|
106
113
|
when 'c'
|
@@ -36,18 +36,19 @@ 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
|
47
47
|
|
48
48
|
# Hosts
|
49
|
-
CLIENT_HOST = 'client.
|
50
|
-
CAPTCHA_HOST = 'captcha.px-
|
49
|
+
CLIENT_HOST = 'client.perimeterx.net'
|
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'
|
@@ -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
|
@@ -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
|
@@ -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,17 +23,16 @@ 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
|
-
view[PxModule::PROP_HOST_URL] =
|
33
|
-
view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
|
31
|
+
view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
|
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
|
-
view[PxModule::PROP_FIRST_PARTY_ENABLED] =
|
35
|
+
view[PxModule::PROP_FIRST_PARTY_ENABLED] = px_ctx.context[:first_party_enabled]
|
37
36
|
|
38
37
|
return view.render.html_safe
|
39
38
|
end
|
@@ -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/px_metadata.json
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"version": "2.3.0",
|
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.
|
8
|
+
> Latest stable version: [v2.3.0](https://rubygems.org/gems/perimeter_x)
|
9
9
|
|
10
10
|
Table of Contents
|
11
11
|
-----------------
|
@@ -19,8 +19,6 @@ Table of Contents
|
|
19
19
|
* [Blocking Score](#blocking-score)
|
20
20
|
* [Custom Verification Action](#custom-verification-action)
|
21
21
|
* [Custom Block Page](#custom-block-page)
|
22
|
-
* [Enable/Disable Captcha](#captcha-support)
|
23
|
-
* [Select Captcha Provider](#captcha-provider)
|
24
22
|
* [Extracting Real IP Address](#real-ip)
|
25
23
|
* [Custom URI](#custom-uri)
|
26
24
|
* [Filter Sensitive Headers](#sensitive-headers)
|
@@ -31,7 +29,9 @@ Table of Contents
|
|
31
29
|
* [Debug Mode](#debug-mode)
|
32
30
|
* [Whitelist Routes](#whitelist-routes)
|
33
31
|
* [Update Configuration on Runtime](#update-config)
|
32
|
+
* [First Party](#first-party)
|
34
33
|
|
34
|
+
**[Additional Information](#additional-information)**
|
35
35
|
**[Contributing](#contributing)**
|
36
36
|
|
37
37
|
<a name="Usage"></a>
|
@@ -224,26 +224,6 @@ Default mode: PxModule::ACTIVE_MODE
|
|
224
224
|
params[:module_mode] = PxModule::MONITOR_MODE
|
225
225
|
```
|
226
226
|
|
227
|
-
<a name="captcha-support"></a>**Enable/Disable CAPTCHA on the block page**
|
228
|
-
Default mode: enabled
|
229
|
-
|
230
|
-
By enabling CAPTCHA support, a CAPTCHA will be served as part of the block page, giving real users the ability to identify as a human. By solving the CAPTCHA, the user's score is then cleaned up and the user is allowed to continue normal use.
|
231
|
-
|
232
|
-
```ruby
|
233
|
-
params[:captcha_enabled] = false
|
234
|
-
```
|
235
|
-
|
236
|
-
<a name="captcha-provider"></a>**Select CAPTCHA Provider**
|
237
|
-
|
238
|
-
The CAPTCHA part of the block page can use one of the following:
|
239
|
-
* [reCAPTCHA](https://www.google.com/recaptcha)
|
240
|
-
|
241
|
-
Default: 'reCaptcha'
|
242
|
-
|
243
|
-
```ruby
|
244
|
-
captchaProvider = "reCaptcha"
|
245
|
-
```
|
246
|
-
|
247
227
|
<a name="custom-uri"></a>**Custom URI**
|
248
228
|
|
249
229
|
Default: 'REQUEST_URI'
|
@@ -325,11 +305,12 @@ However, it is possible to override configuration options on each request.
|
|
325
305
|
To do so, send the configuration options as an argument when calling to `px_verify_request` as described in the following example.
|
326
306
|
Notice that in case of an invalid argument, the module will raise an error. Therefore, when using this feature, make sure to wrap the call to `px_verify_request` with begin and rescue. It is highly recommended to log the error message to follow such errors.
|
327
307
|
|
308
|
+
Usage example:
|
309
|
+
|
328
310
|
```ruby
|
329
311
|
class HomeController < ApplicationController
|
330
312
|
include PxModule
|
331
313
|
|
332
|
-
|
333
314
|
before_action do call_perimeterx_verify_request end
|
334
315
|
|
335
316
|
def call_perimeterx_verify_request
|
@@ -347,7 +328,35 @@ class HomeController < ApplicationController
|
|
347
328
|
end
|
348
329
|
```
|
349
330
|
|
350
|
-
<a name="
|
331
|
+
<a name="first-party"></a>**First Party**
|
332
|
+
|
333
|
+
To enable first party on your enforcer, add the following routes to your `config/routes.rb` file:
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
get '/:appid_postfix/init.js', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/ }
|
337
|
+
get '/:appid_postfix/captcha/:all', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/, all:/.*/ }
|
338
|
+
post '/:appid_postfix/xhr/:all', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/, all:/.*/ }
|
339
|
+
```
|
340
|
+
|
341
|
+
Notice that all occurences of `XXXXXXXX` should be replaced with your px_app_id without the "PX" prefix. For example, if your px_app_id is `PX2H4seK9L`, replace `XXXXXXXX` with `2H4seK9L`.
|
342
|
+
|
343
|
+
In case you are using more than one px_app_id, provide all of them with a `|` sign between them. For example: 2H4seK9L|9bMs6K94|Lc5kPMNx
|
344
|
+
|
345
|
+
|
346
|
+
First Party configuration:
|
347
|
+
|
348
|
+
Default: true
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
params[:first_party_enabled] = false
|
352
|
+
```
|
353
|
+
|
354
|
+
<a name="additional_information"></a> Additional Information
|
355
|
+
------------------------------
|
356
|
+
### URI Delimiters
|
357
|
+
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.
|
358
|
+
|
359
|
+
<a name="contributing"></a> Contributing
|
351
360
|
------------------------------
|
352
361
|
The following steps are welcome when contributing to our project.
|
353
362
|
###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.
|
4
|
+
version: 2.3.0
|
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-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -173,6 +173,7 @@ files:
|
|
173
173
|
- lib/perimeterx/internal/clients/perimeter_x_risk_client.rb
|
174
174
|
- lib/perimeterx/internal/exceptions/px_config_exception.rb
|
175
175
|
- lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb
|
176
|
+
- lib/perimeterx/internal/first_party/px_first_party.rb
|
176
177
|
- lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb
|
177
178
|
- lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb
|
178
179
|
- lib/perimeterx/internal/payload/perimeter_x_payload.rb
|
@@ -190,6 +191,7 @@ files:
|
|
190
191
|
- lib/perimeterx/utils/templates/ratelimit.mustache
|
191
192
|
- lib/perimeterx/version.rb
|
192
193
|
- perimeter_x.gemspec
|
194
|
+
- px_metadata.json
|
193
195
|
- readme.md
|
194
196
|
homepage: https://www.perimeterx.com
|
195
197
|
licenses:
|
@@ -214,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
214
216
|
- !ruby/object:Gem::Version
|
215
217
|
version: '0'
|
216
218
|
requirements: []
|
217
|
-
rubygems_version: 3.0.3
|
219
|
+
rubygems_version: 3.0.3.1
|
218
220
|
signing_key:
|
219
221
|
specification_version: 4
|
220
222
|
summary: PerimeterX ruby implmentation
|