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 +4 -4
- data/changelog.md +11 -0
- data/lib/perimeter_x.rb +89 -86
- data/lib/perimeterx/configuration.rb +51 -6
- data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +4 -1
- data/lib/perimeterx/internal/exceptions/px_config_exception.rb +6 -0
- data/lib/perimeterx/internal/perimeter_x_context.rb +10 -6
- data/lib/perimeterx/internal/validators/hash_schema_validator.rb +26 -0
- data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +2 -21
- data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +11 -1
- data/lib/perimeterx/utils/px_constants.rb +3 -2
- data/lib/perimeterx/version.rb +1 -1
- data/readme.md +33 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d65b91dbf3bdf1829dbfe7b1d44677d86ecde223bc0bd614618d1a9b1a188e84
|
4
|
+
data.tar.gz: 473dc3f4a54232d59c2f89dcc6a1bddbb6a95de0268fddf53cfa28b47e8653d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a89194401d9a60cd41fdb524724329bee547285742c218ffd157337203090ab66f4992fed517389868c639fea175a9c3561663b81b2463a9b1f3fb0011391e7b
|
7
|
+
data.tar.gz: 428757d1a7dafc96543a125db63a8a2c7639ba27673f96bd5222b1628bafcd117f26d1fefb8041179f30e89f5ea286454ebbfb897b0d81d54e5be2be24d6fb8e
|
data/changelog.md
CHANGED
@@ -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
|
data/lib/perimeter_x.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
55
|
-
|
48
|
+
is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
|
49
|
+
action = px_ctx.context[:block_action][0,1]
|
56
50
|
|
57
|
-
|
58
|
-
|
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
|
-
|
61
|
-
|
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
|
-
:
|
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
|
-
:
|
71
|
-
:
|
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
|
-
|
97
|
-
|
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(
|
101
|
-
|
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.
|
116
|
-
|
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
|
-
|
170
|
-
|
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
|
-
:
|
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
|
-
|
38
|
-
|
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,
|
@@ -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.
|
41
|
-
|
42
|
-
|
43
|
-
@context[:
|
44
|
-
|
45
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/perimeterx/version.rb
CHANGED
data/readme.md
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
[](https://travis-ci.org/PerimeterX/perimeterx-ruby-sdk)
|
2
2
|
|
3
|
-

|
4
4
|
#
|
5
5
|
[PerimeterX](http://www.perimeterx.com) Ruby SDK
|
6
6
|
=============================================================
|
7
7
|
|
8
|
-
> Latest stable version: [
|
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.
|
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-
|
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
|