perimeter_x 2.0.0 → 2.1.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: 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