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 +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
|
[![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](
|
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: [
|
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
|