perimeter_x 2.1.0 → 2.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d65b91dbf3bdf1829dbfe7b1d44677d86ecde223bc0bd614618d1a9b1a188e84
4
- data.tar.gz: 473dc3f4a54232d59c2f89dcc6a1bddbb6a95de0268fddf53cfa28b47e8653d6
3
+ metadata.gz: 475e2577bfe9b12bb7edbe3286fb4f1861a9c508791f38b0196dea0f493f355e
4
+ data.tar.gz: b0c61698741743a62ad0a7c9fb67dc6d5ed9f9b78c01d08b764cb1297ee37c98
5
5
  SHA512:
6
- metadata.gz: a89194401d9a60cd41fdb524724329bee547285742c218ffd157337203090ab66f4992fed517389868c639fea175a9c3561663b81b2463a9b1f3fb0011391e7b
7
- data.tar.gz: 428757d1a7dafc96543a125db63a8a2c7639ba27673f96bd5222b1628bafcd117f26d1fefb8041179f30e89f5ea286454ebbfb897b0d81d54e5be2be24d6fb8e
6
+ metadata.gz: 3bae8b3e45c8ee8aba86a56ed977158adec12bccfa2542e1a4e54344b6b6fb0503182585816c1b55640bd49ea4d6323831e93ad6c8d3335703e33d695df362ba
7
+ data.tar.gz: d7a64faa2f03fd4bc467cdea545a2f530001431f37281c2a9561dd83c28c00006fe827e8cd510560fbbbd1a69d382993ae513edd214a18a277bcca57dd561fdc
@@ -5,6 +5,10 @@ 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.2.0] - 2020-09-15
9
+ ### Added
10
+ - First Party
11
+
8
12
  ## [2.1.0] - 2020-09-01
9
13
  ### Added
10
14
  - Added option to set a different px configuration on each request
@@ -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
- px_ctx = px_instance.verify(request.env)
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]'
@@ -46,12 +56,21 @@ module PxModule
46
56
  end
47
57
 
48
58
  is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
49
- action = px_ctx.context[:block_action][0,1]
59
+ action = px_ctx.context[:block_action][0,1]
50
60
 
51
- px_template_object = {
61
+ if px_config[:first_party_enabled]
62
+ px_template_object = {
63
+ 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]}/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}",
65
+ host_url: "/#{px_config[:app_id][2..-1]}/xhr"
66
+ }
67
+ else
68
+ px_template_object = {
69
+ js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
52
70
  block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}",
53
- js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js"
54
- }
71
+ host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net"
72
+ }
73
+ end
55
74
 
56
75
  html = PxTemplateFactory.get_template(px_ctx, px_config, px_template_object)
57
76
 
@@ -68,7 +87,7 @@ module PxModule
68
87
  hash_json = {
69
88
  :appId => px_config[:app_id],
70
89
  :jsClientSrc => px_template_object[:js_client_src],
71
- :firstPartyEnabled => false,
90
+ :firstPartyEnabled => px_ctx.context[:first_party_enabled],
72
91
  :uuid => px_ctx.context[:uuid],
73
92
  :vid => px_ctx.context[:vid],
74
93
  :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
@@ -110,6 +129,29 @@ module PxModule
110
129
  end
111
130
  end
112
131
 
132
+ def render_first_party_response(req, px_instance)
133
+ fp = px_instance.first_party
134
+ px_config = px_instance.px_config
135
+
136
+ if px_config[:first_party_enabled]
137
+ # first party enabled - proxy response
138
+ fp_response = fp.send_first_party_request(req)
139
+ response.status = fp_response.code
140
+ fp_response.to_hash.each do |header_name, header_value_arr|
141
+ if header_name!="content-length"
142
+ response.headers[header_name] = header_value_arr[0]
143
+ end
144
+ end
145
+ res_type = fp.get_response_content_type(req)
146
+ render res_type => fp_response.body
147
+ else
148
+ # first party disabled - return empty response
149
+ response.status = 200
150
+ res_type = fp.get_response_content_type(req)
151
+ render res_type => ""
152
+ end
153
+ end
154
+
113
155
  def self.configure(basic_config)
114
156
  PerimeterX.set_basic_config(basic_config)
115
157
  end
@@ -119,6 +161,7 @@ module PxModule
119
161
  class PerimeterX
120
162
 
121
163
  attr_reader :px_config
164
+ attr_reader :first_party
122
165
  attr_accessor :px_http_client
123
166
  attr_accessor :px_activity_client
124
167
 
@@ -128,7 +171,7 @@ module PxModule
128
171
  end
129
172
 
130
173
  #Instance Methods
131
- def verify(env)
174
+ def verify(req)
132
175
  begin
133
176
 
134
177
  # check module_enabled
@@ -137,13 +180,11 @@ module PxModule
137
180
  @logger.warn('Module is disabled')
138
181
  return nil
139
182
  end
140
-
141
- req = ActionDispatch::Request.new(env)
142
183
 
143
184
  # filter whitelist routes
144
185
  url_path = URI.parse(req.original_url).path
145
186
  if url_path && !url_path.empty?
146
- if check_whitelist_routes(px_config[:whitelist_routes], url_path)
187
+ if check_whitelist_routes(px_config[:whitelist_routes], url_path)
147
188
  @logger.debug("PerimeterX[pxVerify]: whitelist route: #{url_path}")
148
189
  return nil
149
190
  end
@@ -176,6 +217,7 @@ module PxModule
176
217
  @px_http_client = PxHttpClient.new(@px_config)
177
218
 
178
219
  @px_activity_client = PerimeterxActivitiesClient.new(@px_config, @px_http_client)
220
+ @first_party = FirstPartyManager.new(@px_config, @px_http_client, @logger)
179
221
 
180
222
  @px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
181
223
  @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 = {
@@ -60,7 +61,9 @@ module PxModule
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
- # Get IP from header/custom function
22
- if px_config[:ip_headers].length() > 0
23
- px_config[:ip_headers].each do |ip_header|
24
- if req.headers[ip_header]
25
- @context[:ip] = force_utf8(req.headers[ip_header])
26
- end
27
- end
28
- elsif px_config[:ip_header_function] != nil
29
- @context[:ip] = px_config[:ip_header_function].call(req)
30
- end
31
-
32
- if @context[:ip] == nil
33
- @context[:ip] = req.ip
34
- end
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'
@@ -46,8 +46,8 @@ module PxModule
46
46
  PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
47
47
 
48
48
  # Hosts
49
- CLIENT_HOST = 'client.px-cloud.net'
50
- CAPTCHA_HOST = 'captcha.px-cloud.net'
49
+ CLIENT_HOST = 'client.perimeterx.net'
50
+ CAPTCHA_HOST = 'captcha.px-cdn.net'
51
51
 
52
52
  VISIBLE = 'visible'
53
53
  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
@@ -29,11 +29,11 @@ module PxModule
29
29
  view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
30
30
  view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
31
31
  view[PxModule::PROP_JS_REF] = px_config[:js_ref]
32
- view[PxModule::PROP_HOST_URL] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
32
+ view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
33
33
  view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
34
34
  view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
35
35
  view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
36
- view[PxModule::PROP_FIRST_PARTY_ENABLED] = false
36
+ view[PxModule::PROP_FIRST_PARTY_ENABLED] = px_ctx.context[:first_party_enabled]
37
37
 
38
38
  return view.render.html_safe
39
39
  end
@@ -1,3 +1,3 @@
1
1
  module PxModule
2
- VERSION = '2.1.0'
2
+ VERSION = '2.2.0'
3
3
  end
data/readme.md CHANGED
@@ -31,6 +31,7 @@ Table of Contents
31
31
  * [Debug Mode](#debug-mode)
32
32
  * [Whitelist Routes](#whitelist-routes)
33
33
  * [Update Configuration on Runtime](#update-config)
34
+ * [First Party](#first-party)
34
35
 
35
36
  **[Contributing](#contributing)**
36
37
 
@@ -347,6 +348,27 @@ class HomeController < ApplicationController
347
348
  end
348
349
  ```
349
350
 
351
+ <a name="first-party"></a>**First Party**
352
+ To enable first party on your enforcer, add the following routes to your `config/routes.rb` file:
353
+
354
+ ```ruby
355
+ get '/:appid_postfix/init.js', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/ }
356
+ get '/:appid_postfix/captcha/:all', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/, all:/.*/ }
357
+ post '/:appid_postfix/xhr/:all', to: 'home#index', constraints: { appid_postfix: /XXXXXXXX/, all:/.*/ }
358
+ ```
359
+
360
+ 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`, reeplace `XXXXXXXX` with `2H4seK9L`.
361
+ 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
362
+
363
+
364
+ First Party configuration:
365
+ Default: true
366
+
367
+ ```ruby
368
+ params[:first_party_enabled] = false
369
+ ```
370
+
371
+
350
372
  <a name="contributing"></a># Contributing #
351
373
  ------------------------------
352
374
  The following steps are welcome when contributing to our project.
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.1.0
4
+ version: 2.2.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: 2020-09-01 00:00:00.000000000 Z
11
+ date: 2020-09-15 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