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