perimeter_x 2.1.0 → 2.2.0

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