perimeter_x 2.0.0 → 2.1.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: a06a83f955ae265238a23df8471c062428cf82ba37612160a439c52af3568e79
4
- data.tar.gz: 0eaeb8c8b424a219d155f933494c0baec382b96e707cb7af22e676ea83a1fd4f
3
+ metadata.gz: d65b91dbf3bdf1829dbfe7b1d44677d86ecde223bc0bd614618d1a9b1a188e84
4
+ data.tar.gz: 473dc3f4a54232d59c2f89dcc6a1bddbb6a95de0268fddf53cfa28b47e8653d6
5
5
  SHA512:
6
- metadata.gz: c615587bc9e1203636e0a74284aa9faadd3027dc50056929b4bb8fc2cdd6a86bdc0df10c94f3e262f0f313e518fc0397ac2b0b76669fe3e1b5afc85ad589567d
7
- data.tar.gz: 22faae3132f9fce873829a0feec4b8f9bfcda420960a380d40a5101b4dae793969b8225a5376f2937954519a8c8cb1c20eb252fca096af77bff7980d5c223f71
6
+ metadata.gz: a89194401d9a60cd41fdb524724329bee547285742c218ffd157337203090ab66f4992fed517389868c639fea175a9c3561663b81b2463a9b1f3fb0011391e7b
7
+ data.tar.gz: 428757d1a7dafc96543a125db63a8a2c7639ba27673f96bd5222b1628bafcd117f26d1fefb8041179f30e89f5ea286454ebbfb897b0d81d54e5be2be24d6fb8e
@@ -5,6 +5,17 @@ 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.1.0] - 2020-09-01
9
+ ### Added
10
+ - Added option to set a different px configuration on each request
11
+ - Added types validation on configuration fields
12
+
13
+ ### Fixed
14
+ - New cookie logic for mobile requests
15
+ - Renamed api_connect_timeout to api_timeout_conncection on default configuration
16
+ - Removed unsapported configuration fields: max_buffer_len and local_proxy
17
+ - Send cookie_origin only if there is a cookie
18
+
8
19
  ## [2.0.0] - 2020-07-24
9
20
  ### Added
10
21
  - Added fields to Block Activity: simulated_block, http_version, http_method, risk_rtt, px_orig_cookie
@@ -11,122 +11,122 @@ require 'perimeterx/internal/perimeter_x_context'
11
11
  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
+ require 'perimeterx/internal/exceptions/px_config_exception'
14
15
 
15
16
  module PxModule
16
17
  # Module expose API
17
- def px_verify_request
18
- px_ctx = PerimeterX.instance.verify(request.env)
19
- px_config = PerimeterX.instance.px_config
20
- msg_title = 'PxModule[px_verify_request]'
21
-
22
- # In case custom verification handler is in use
23
- if px_config.key?(:custom_verification_handler)
24
- px_config[:logger].debug("#{msg_title}: custom_verification_handler triggered")
25
- return instance_exec(px_ctx, &px_config[:custom_verification_handler])
26
- end
18
+ def px_verify_request(request_config={})
19
+ begin
20
+ px_instance = PerimeterX.new(request_config)
21
+ px_ctx = px_instance.verify(request.env)
22
+ px_config = px_instance.px_config
23
+
24
+ msg_title = 'PxModule[px_verify_request]'
25
+
26
+ # In case custom verification handler is in use
27
+ if px_config.key?(:custom_verification_handler)
28
+ px_config[:logger].debug("#{msg_title}: custom_verification_handler triggered")
29
+ return instance_exec(px_ctx, &px_config[:custom_verification_handler])
30
+ end
27
31
 
28
- unless px_ctx.nil? || px_ctx.context[:verified] || (px_config[:module_mode] == PxModule::MONITOR_MODE && !px_ctx.context[:should_bypass_monitor])
29
- # In case custom block handler exists (soon to be deprecated)
30
- if px_config.key?(:custom_block_handler)
31
- px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
32
- px_config[:logger].debug(
33
- "#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
34
- return instance_exec(px_ctx, &px_config[:custom_block_handler])
35
- else
36
- if px_ctx.context[:block_action]== 'rate_limit'
37
- px_config[:logger].debug("#{msg_title}: sending rate limit page")
38
- response.status = 429
32
+ unless px_ctx.nil? || px_ctx.context[:verified] || (px_config[:module_mode] == PxModule::MONITOR_MODE && !px_ctx.context[:should_bypass_monitor])
33
+ # In case custom block handler exists (soon to be deprecated)
34
+ if px_config.key?(:custom_block_handler)
35
+ px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
36
+ px_config[:logger].debug(
37
+ "#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
38
+ return instance_exec(px_ctx, &px_config[:custom_block_handler])
39
39
  else
40
- px_config[:logger].debug("#{msg_title}: sending default block page")
41
- response.status = 403
42
- end
43
-
44
- is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
45
- action = px_ctx.context[:block_action][0,1]
46
-
47
- px_template_object = {
48
- 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}",
49
- js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js"
50
- }
51
-
52
- html = PxTemplateFactory.get_template(px_ctx, px_config, px_template_object)
40
+ if px_ctx.context[:block_action]== 'rate_limit'
41
+ px_config[:logger].debug("#{msg_title}: sending rate limit page")
42
+ response.status = 429
43
+ else
44
+ px_config[:logger].debug("#{msg_title}: sending default block page")
45
+ response.status = 403
46
+ end
53
47
 
54
- # Web handler
55
- if px_ctx.context[:cookie_origin] == 'cookie'
48
+ is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
49
+ action = px_ctx.context[:block_action][0,1]
56
50
 
57
- accept_header_value = request.headers['accept'] || request.headers['content-type'];
58
- is_json_response = px_ctx.context[:block_action] != 'rate_limit' && accept_header_value && accept_header_value.split(',').select {|e| e.downcase.include? 'application/json'}.length > 0;
51
+ px_template_object = {
52
+ 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
+ }
59
55
 
60
- if (is_json_response)
61
- px_config[:logger].debug("#{msg_title}: advanced blocking response response")
56
+ html = PxTemplateFactory.get_template(px_ctx, px_config, px_template_object)
57
+
58
+ # Web handler
59
+ if px_ctx.context[:cookie_origin] == 'cookie'
60
+
61
+ accept_header_value = request.headers['accept'] || request.headers['content-type'];
62
+ is_json_response = px_ctx.context[:block_action] != 'rate_limit' && accept_header_value && accept_header_value.split(',').select {|e| e.downcase.include? 'application/json'}.length > 0;
63
+
64
+ if (is_json_response)
65
+ px_config[:logger].debug("#{msg_title}: advanced blocking response response")
66
+ response.headers['Content-Type'] = 'application/json'
67
+
68
+ hash_json = {
69
+ :appId => px_config[:app_id],
70
+ :jsClientSrc => px_template_object[:js_client_src],
71
+ :firstPartyEnabled => false,
72
+ :uuid => px_ctx.context[:uuid],
73
+ :vid => px_ctx.context[:vid],
74
+ :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
75
+ :blockScript => px_template_object[:block_script],
76
+ }
77
+
78
+ render :json => hash_json
79
+ else
80
+ px_config[:logger].debug('#{msg_title}: web block')
81
+ response.headers['Content-Type'] = 'text/html'
82
+ render :html => html
83
+ end
84
+ else # Mobile SDK
85
+ px_config[:logger].debug("#{msg_title}: mobile sdk block")
62
86
  response.headers['Content-Type'] = 'application/json'
63
-
64
87
  hash_json = {
65
- :appId => px_config[:app_id],
66
- :jsClientSrc => px_template_object[:js_client_src],
67
- :firstPartyEnabled => false,
88
+ :action => px_ctx.context[:block_action],
68
89
  :uuid => px_ctx.context[:uuid],
69
90
  :vid => px_ctx.context[:vid],
70
- :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
71
- :blockScript => px_template_object[:block_script],
91
+ :appId => px_config[:app_id],
92
+ :page => Base64.strict_encode64(html),
93
+ :collectorUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net"
72
94
  }
73
-
74
95
  render :json => hash_json
75
- else
76
- px_config[:logger].debug('#{msg_title}: web block')
77
- response.headers['Content-Type'] = 'text/html'
78
- render :html => html
79
96
  end
80
- else # Mobile SDK
81
- px_config[:logger].debug("#{msg_title}: mobile sdk block")
82
- response.headers['Content-Type'] = 'application/json'
83
- hash_json = {
84
- :action => px_ctx.context[:block_action],
85
- :uuid => px_ctx.context[:uuid],
86
- :vid => px_ctx.context[:vid],
87
- :appId => px_config[:app_id],
88
- :page => Base64.strict_encode64(html),
89
- :collectorUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net"
90
- }
91
- render :json => hash_json
92
97
  end
93
98
  end
94
- end
95
99
 
96
- # Request was verified
97
- return px_ctx.nil? ? true : px_ctx.context[:verified]
100
+ # Request was verified
101
+ return px_ctx.nil? ? true : px_ctx.context[:verified]
102
+
103
+ rescue PxConfigurationException
104
+ raise
105
+ rescue Exception => e
106
+ error_logger = PxLogger.new(true)
107
+ error_logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
108
+ e.backtrace.drop(1).map {|s| error_logger.error("\t#{s}")}
109
+ return nil
110
+ end
98
111
  end
99
112
 
100
- def self.configure(params)
101
- @px_instance = PerimeterX.configure(params)
113
+ def self.configure(basic_config)
114
+ PerimeterX.set_basic_config(basic_config)
102
115
  end
103
116
 
104
117
 
105
118
  # PerimeterX Module
106
119
  class PerimeterX
107
- @@__instance = nil
108
- @@mutex = Mutex.new
109
120
 
110
121
  attr_reader :px_config
111
122
  attr_accessor :px_http_client
112
123
  attr_accessor :px_activity_client
113
124
 
114
125
  #Static methods
115
- def self.configure(params)
116
- return true if @@__instance
117
- @@mutex.synchronize {
118
- return @@__instance if @@__instance
119
- @@__instance = new(params)
120
- }
121
- return true
126
+ def self.set_basic_config(basic_config)
127
+ Configuration.set_basic_config(basic_config)
122
128
  end
123
129
 
124
- def self.instance
125
- return @@__instance if !@@__instance.nil?
126
- raise Exception.new('Please initialize perimeter x first')
127
- end
128
-
129
-
130
130
  #Instance Methods
131
131
  def verify(env)
132
132
  begin
@@ -155,6 +155,9 @@ module PxModule
155
155
  # Cookie phase
156
156
  cookie_verified, px_ctx = @px_cookie_validator.verify(px_ctx)
157
157
  if !cookie_verified
158
+ if !px_ctx.context[:mobile_error].nil?
159
+ px_ctx.context[:s2s_call_reason] = "mobile_error_#{px_ctx.context[:mobile_error]}"
160
+ end
158
161
  @px_s2s_validator.verify(px_ctx)
159
162
  end
160
163
 
@@ -166,8 +169,9 @@ module PxModule
166
169
  end
167
170
  end
168
171
 
169
- private def initialize(params)
170
- @px_config = Configuration.new(params).configuration
172
+ def initialize(request_config)
173
+
174
+ @px_config = Configuration.new(request_config).configuration
171
175
  @logger = @px_config[:logger]
172
176
  @px_http_client = PxHttpClient.new(@px_config)
173
177
 
@@ -219,6 +223,5 @@ module PxModule
219
223
  false
220
224
  end
221
225
 
222
- private_class_method :new
223
226
  end
224
227
  end
@@ -1,11 +1,13 @@
1
1
  require 'perimeterx/utils/px_logger'
2
2
  require 'perimeterx/utils/px_constants'
3
+ require 'perimeterx/internal/validators/hash_schema_validator'
3
4
 
4
5
  module PxModule
5
6
  class Configuration
7
+ @@basic_config = nil
8
+ @@mutex = Mutex.new
6
9
 
7
10
  attr_accessor :configuration
8
- attr_accessor :PX_DEFAULT
9
11
 
10
12
  PX_DEFAULT = {
11
13
  :app_id => nil,
@@ -16,15 +18,13 @@ module PxModule
16
18
  :encryption_enabled => true,
17
19
  :blocking_score => 100,
18
20
  :sensitive_headers => ["http-cookie", "http-cookies"],
19
- :api_connect_timeout => 1,
21
+ :api_timeout_connection => 1,
20
22
  :api_timeout => 1,
21
- :max_buffer_len => 10,
22
23
  :send_page_activities => true,
23
24
  :send_block_activities => true,
24
25
  :sdk_name => PxModule::SDK_NAME,
25
26
  :debug => false,
26
27
  :module_mode => PxModule::MONITOR_MODE,
27
- :local_proxy => false,
28
28
  :sensitive_routes => [],
29
29
  :whitelist_routes => [],
30
30
  :ip_headers => [],
@@ -33,9 +33,54 @@ module PxModule
33
33
  :risk_cookie_max_iterations => 5000
34
34
  }
35
35
 
36
+ CONFIG_SCHEMA = {
37
+ :app_id => {types: [String], required: true},
38
+ :cookie_key => {types: [String], required: true},
39
+ :auth_token => {types: [String], required: true},
40
+ :module_enabled => {types: [FalseClass, TrueClass], required: false},
41
+ :challenge_enabled => {types: [FalseClass, TrueClass], required: false},
42
+ :encryption_enabled => {types: [FalseClass, TrueClass], required: false},
43
+ :blocking_score => {types: [Integer], required: false},
44
+ :sensitive_headers => {types: [Array], allowed_element_types: [String], required: false},
45
+ :api_timeout_connection => {types: [Integer, Float], required: false},
46
+ :api_timeout => {types: [Integer, Float], required: false},
47
+ :send_page_activities => {types: [FalseClass, TrueClass], required: false},
48
+ :send_block_activities => {types: [FalseClass, TrueClass], required: false},
49
+ :sdk_name => {types: [String], required: false},
50
+ :debug => {types: [FalseClass, TrueClass], required: false},
51
+ :module_mode => {types: [Integer], required: false},
52
+ :sensitive_routes => {types: [Array], allowed_element_types: [String], required: false},
53
+ :whitelist_routes => {types: [Array], allowed_element_types: [String, Regexp], required: false},
54
+ :ip_headers => {types: [Array], allowed_element_types: [String], required: false},
55
+ :ip_header_function => {types: [Proc], required: false},
56
+ :bypass_monitor_header => {types: [FalseClass, TrueClass], required: false},
57
+ :risk_cookie_max_iterations => {types: [Integer], required: false},
58
+ :custom_verification_handler => {types: [Proc], required: false},
59
+ :additional_activity_handler => {types: [Proc], required: false},
60
+ :custom_logo => {types: [String], required: false},
61
+ :css_ref => {types: [String], required: false},
62
+ :js_ref => {types: [String], required: false},
63
+ :custom_uri => {types: [Proc], required: false}
64
+ }
65
+
66
+ def self.set_basic_config(basic_config)
67
+ if @@basic_config.nil?
68
+ @@mutex.synchronize {
69
+ @@basic_config = PX_DEFAULT.merge(basic_config)
70
+ }
71
+ end
72
+ end
73
+
36
74
  def initialize(params)
37
- PX_DEFAULT[:backend_url] = "https://sapi-#{params[:app_id].downcase}.perimeterx.net"
38
- @configuration = PX_DEFAULT.merge(params)
75
+ if ! @@basic_config.is_a?(Hash)
76
+ raise PxConfigurationException.new('PerimeterX: Please initialize PerimeterX first')
77
+ end
78
+
79
+ # merge request configuration into the basic configuration
80
+ @configuration = @@basic_config.merge(params)
81
+ validate_hash_schema(@configuration, CONFIG_SCHEMA)
82
+
83
+ @configuration[:backend_url] = "https://sapi-#{@configuration[:app_id].downcase}.perimeterx.net"
39
84
  @configuration[:logger] = PxLogger.new(@configuration[:debug])
40
85
  end
41
86
  end
@@ -17,8 +17,11 @@ module PxModule
17
17
  @px_config[:additional_activity_handler].call(activity_type, px_ctx, details)
18
18
  end
19
19
 
20
+ if !px_ctx.context[:px_cookie].empty?
21
+ details[:cookie_origin] = px_ctx.context[:cookie_origin]
22
+ end
23
+
20
24
  details[:module_version] = @px_config[:sdk_name]
21
- details[:cookie_origin] = px_ctx.context[:cookie_origin]
22
25
 
23
26
  px_data = {
24
27
  :type => activity_type,
@@ -0,0 +1,6 @@
1
+ class PxConfigurationException < StandardError
2
+ def initialize(msg)
3
+ super(msg)
4
+ end
5
+ end
6
+
@@ -37,12 +37,16 @@ module PxModule
37
37
  if req.headers[PxModule::TOKEN_HEADER]
38
38
  @context[:cookie_origin] = 'header'
39
39
  token = force_utf8(req.headers[PxModule::TOKEN_HEADER])
40
- if token.include? ':'
41
- exploded_token = token.split(':', 2)
42
- cookie_sym = "v#{exploded_token[0]}".to_sym
43
- @context[:px_cookie][cookie_sym] = exploded_token[1]
44
- else # TOKEN_HEADER exists yet there's no ':' delimiter - may indicate an error (storing original value)
45
- @context[:px_cookie] = force_utf8(req.headers[PxModule::TOKEN_HEADER])
40
+ if token.match(PxModule::MOBILE_TOKEN_V3_REGEX)
41
+ @context[:px_cookie][:v3] = token[2..-1]
42
+ elsif token.match(PxModule::MOBILE_ERROR_REGEX)
43
+ @context[:mobile_error] = token
44
+ if req.headers[PxModule::ORIGINAL_TOKEN_HEADER]
45
+ token = force_utf8(req.headers[PxModule::ORIGINAL_TOKEN_HEADER])
46
+ if token.match(PxModule::MOBILE_TOKEN_V3_REGEX)
47
+ @context[:px_cookie][:v3] = token[2..-1]
48
+ end
49
+ end
46
50
  end
47
51
  elsif !cookies.empty? # Get cookie from jar
48
52
  # Prepare hashed cookies
@@ -0,0 +1,26 @@
1
+ def validate_hash_schema(hash, schema)
2
+ hash.each do |key, value|
3
+ if schema.key?(key) && value != nil
4
+ # validate value types in hash are according to schema
5
+ if !schema[key][:types].include?(value.class)
6
+ raise PxConfigurationException.new("PerimeterX: Type of #{key} should be one of #{schema[key][:types]} but instead is #{value.class}")
7
+ end
8
+
9
+ # validate arrays elments types are according to schema
10
+ if value.class == Array
11
+ value.each do |element|
12
+ if !schema[key][:allowed_element_types].include?(element.class)
13
+ raise PxConfigurationException.new("PerimeterX: #{key} may only contain elements of the following types: #{schema[key][:allowed_element_types]} but includes element of type #{element.class}")
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ # validate required fields exist in hash
21
+ schema.each do |key, value|
22
+ if value[:required] && hash[key].nil?
23
+ raise PxConfigurationException.new("PerimeterX: #{key} configuration is missing")
24
+ end
25
+ end
26
+ end
@@ -18,31 +18,12 @@ module PxModule
18
18
 
19
19
  def verify(px_ctx)
20
20
  begin
21
- # Mobile Error cases
22
- if px_ctx.context[:cookie_origin] == 'header'
23
- if px_ctx.context[:px_cookie].to_s.empty?
24
- @logger.warn("PerimeterxCookieValidator:[verify]: Empty token value - decryption failed")
25
- px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
26
- return false, px_ctx
27
- elsif px_ctx.context[:px_cookie] == "1"
28
- @logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
29
- px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
30
- return false, px_ctx
31
- elsif px_ctx.context[:px_cookie] == "2" # Mobile SDK connection error
32
- @logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk connection error")
33
- px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_CONNECTION_ERROR
34
- return false, px_ctx
35
- elsif px_ctx.context[:px_cookie] == "3" # Mobile SDK pinning error
36
- @logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk pinning error")
37
- px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_PINNING_ERROR
38
- return false, px_ctx
39
- end
40
- elsif px_ctx.context[:px_cookie].empty?
21
+ if px_ctx.context[:px_cookie].empty?
41
22
  @logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
42
23
  px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
43
24
  return false, px_ctx
44
25
  end
45
-
26
+
46
27
  # Deserialize cookie start
47
28
  cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
48
29
  if !cookie.deserialize()
@@ -26,12 +26,22 @@ module PxModule
26
26
  :additional => {
27
27
  :s2s_call_reason => px_ctx.context[:s2s_call_reason],
28
28
  :module_version => @px_config[:sdk_name],
29
- :cookie_origin => px_ctx.context[:cookie_origin],
30
29
  :http_method => px_ctx.context[:http_method],
31
30
  :http_version => px_ctx.context[:http_version],
32
31
  :risk_mode => risk_mode
33
32
  }
34
33
  }
34
+
35
+ #Check for cookie_origin
36
+ if !px_ctx.context[:px_cookie].empty?
37
+ request_body[:additional][:cookie_origin] = px_ctx.context[:cookie_origin]
38
+ end
39
+
40
+ #Override s2s_call_reason in case of mobile error
41
+ if !px_ctx.context[:mobile_error].nil?
42
+ request_body[:additional][:s2s_call_reason] = "mobile_error_#{px_ctx.context[:mobile_error]}"
43
+ end
44
+
35
45
  #Check for hmac
36
46
  @logger.debug("px_ctx cookie_hmac key = #{px_ctx.context.key?(:cookie_hmac)}, value is: #{px_ctx.context[:cookie_hmac]}")
37
47
  if px_ctx.context.key?(:cookie_hmac)
@@ -54,9 +54,10 @@ module PxModule
54
54
 
55
55
  # Mobile SDK
56
56
  TOKEN_HEADER = 'X-PX-AUTHORIZATION'
57
- MOBILE_SDK_CONNECTION_ERROR = 'mobile_sdk_connection_error'
58
- MOBILE_SDK_PINNING_ERROR = 'mobile_sdk_pinning_error'
57
+ ORIGINAL_TOKEN_HEADER = 'X-PX-ORIGINAL-TOKEN'
59
58
 
60
59
  # Regular Expressions
61
60
  VID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
61
+ MOBILE_TOKEN_V3_REGEX = /\A3:(.*)\z/
62
+ MOBILE_ERROR_REGEX = /\A[0-9]\z/
62
63
  end
@@ -1,3 +1,3 @@
1
1
  module PxModule
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
data/readme.md CHANGED
@@ -1,11 +1,11 @@
1
1
  [![Build Status](https://travis-ci.org/PerimeterX/perimeterx-ruby-sdk.svg?branch=master)](https://travis-ci.org/PerimeterX/perimeterx-ruby-sdk)
2
2
 
3
- ![image](http://media.marketwire.com/attachments/201604/34215_PerimeterX_logo.jpg)
3
+ ![image](https://storage.googleapis.com/perimeterx-logos/primary_logo_red_cropped.png)
4
4
  #
5
5
  [PerimeterX](http://www.perimeterx.com) Ruby SDK
6
6
  =============================================================
7
7
 
8
- > Latest stable version: [v1.3.0](https://rubygems.org/gems/perimeter_x/versions/1.3.0)
8
+ > Latest stable version: [v2.1.0](https://rubygems.org/gems/perimeter_x)
9
9
 
10
10
  Table of Contents
11
11
  -----------------
@@ -30,6 +30,7 @@ Table of Contents
30
30
  * [Monitor Only](#logging)
31
31
  * [Debug Mode](#debug-mode)
32
32
  * [Whitelist Routes](#whitelist-routes)
33
+ * [Update Configuration on Runtime](#update-config)
33
34
 
34
35
  **[Contributing](#contributing)**
35
36
 
@@ -308,6 +309,7 @@ Enables debug logging mode to STDOUT
308
309
 
309
310
  <a name="whitelist-routes"></a>**Whitelist Routes**
310
311
  Default: []
312
+
311
313
  An array of route prefixes and/or regular expressions that are always whitelisted and not validated by PerimeterX.
312
314
  A string value of a path will be treated as a prefix.
313
315
  A regexp value of a path will be treated as is.
@@ -316,6 +318,35 @@ A regexp value of a path will be treated as is.
316
318
  params[:whitelist_routes] = ["/example", /\A\/example\z/]
317
319
  ```
318
320
 
321
+ <a name="update-config"></a>**Update Configuration on Runtime**
322
+
323
+ As mentioned before, PerimeterX Module should be configured in `<rails_app>/config/initializers/perimeterx.rb`.
324
+ However, it is possible to override configuration options on each request.
325
+ To do so, send the configuration options as an argument when calling to `px_verify_request` as described in the following example.
326
+ 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
+
328
+ ```ruby
329
+ class HomeController < ApplicationController
330
+ include PxModule
331
+
332
+
333
+ before_action do call_perimeterx_verify_request end
334
+
335
+ def call_perimeterx_verify_request
336
+ params = {
337
+ :blocking_score => 70,
338
+ :module_mode => 2
339
+ }
340
+ begin
341
+ px_verify_request(params)
342
+ rescue StandardError => e
343
+ # $stdout.write(e.message)
344
+ end
345
+ end
346
+
347
+ end
348
+ ```
349
+
319
350
  <a name="contributing"></a># Contributing #
320
351
  ------------------------------
321
352
  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.0.0
4
+ version: 2.1.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-07-26 00:00:00.000000000 Z
11
+ date: 2020-09-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -171,6 +171,7 @@ files:
171
171
  - lib/perimeterx/configuration.rb
172
172
  - lib/perimeterx/internal/clients/perimeter_x_activity_client.rb
173
173
  - lib/perimeterx/internal/clients/perimeter_x_risk_client.rb
174
+ - lib/perimeterx/internal/exceptions/px_config_exception.rb
174
175
  - lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb
175
176
  - lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb
176
177
  - lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb
@@ -178,6 +179,7 @@ files:
178
179
  - lib/perimeterx/internal/payload/perimeter_x_token_v1.rb
179
180
  - lib/perimeterx/internal/payload/perimeter_x_token_v3.rb
180
181
  - lib/perimeterx/internal/perimeter_x_context.rb
182
+ - lib/perimeterx/internal/validators/hash_schema_validator.rb
181
183
  - lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb
182
184
  - lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb
183
185
  - lib/perimeterx/utils/px_constants.rb