perimeter_x 1.4.0 → 2.0.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
- SHA1:
3
- metadata.gz: 3dca9964af5aa39c20643f7e5d6f00271862f0de
4
- data.tar.gz: e0f2a81bce3aa7c8b8eb0b0b73d70cc29f272dbc
2
+ SHA256:
3
+ metadata.gz: a06a83f955ae265238a23df8471c062428cf82ba37612160a439c52af3568e79
4
+ data.tar.gz: 0eaeb8c8b424a219d155f933494c0baec382b96e707cb7af22e676ea83a1fd4f
5
5
  SHA512:
6
- metadata.gz: 257d51330a1dc51e1ca2559559d7eb4e0ca6ebc891fd8bfcdd0171b4e31a5a3b4455f4c3b4227b6db7d403eeecb3bb1bb4b62e4e7cf72024d6efa89f5636bd3f
7
- data.tar.gz: 4cd74c725090a46718716b782551e1e5bae7efb939818c1b34970bde070897d6d53334b5c54bb6df7f4a24291dd5ed608e2e39723e008647a07c4e2da8de6b07
6
+ metadata.gz: c615587bc9e1203636e0a74284aa9faadd3027dc50056929b4bb8fc2cdd6a86bdc0df10c94f3e262f0f313e518fc0397ac2b0b76669fe3e1b5afc85ad589567d
7
+ data.tar.gz: 22faae3132f9fce873829a0feec4b8f9bfcda420960a380d40a5101b4dae793969b8225a5376f2937954519a8c8cb1c20eb252fca096af77bff7980d5c223f71
@@ -1,3 +1,3 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.3
3
+ - 2.7.1
data/Dockerfile CHANGED
@@ -1,21 +1,26 @@
1
1
  # Based on manual compile instructions at http://wiki.nginx.org/HttpLuaModule#Installation
2
- FROM ruby:2.3.0
2
+ FROM ruby:2.7.1
3
3
 
4
- RUN apt-get update && apt-get --force-yes -qq -y install \
5
- nodejs
6
- ENV RAILS_VERSION 4.2.0
4
+ RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg -o /root/yarn-pubkey.gpg && apt-key add /root/yarn-pubkey.gpg
5
+ RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" > /etc/apt/sources.list.d/yarn.list
6
+ RUN apt-get update && apt-get install -y --no-install-recommends nodejs yarn vim
7
+
8
+ ENV RAILS_VERSION 6.0.3.2
7
9
  RUN gem install rails --version "$RAILS_VERSION"
8
10
  RUN gem install bundler
9
11
  RUN mkdir -p /tmp/ruby_sandbox
10
12
  WORKDIR /tmp/ruby_sandbox
11
- RUN git clone https://github.com/PerimeterX/perimeterx-ruby-sdk.git
13
+ COPY lib /tmp/ruby_sandbox/lib
14
+ COPY Gemfile /tmp/ruby_sandbox/
15
+ COPY perimeter_x.gemspec /tmp/ruby_sandbox/
16
+ COPY Rakefile /tmp/ruby_sandbox/
12
17
  RUN rails new webapp
13
18
  WORKDIR /tmp/ruby_sandbox/webapp
14
19
 
15
20
  RUN rails generate controller home index
16
21
  WORKDIR /tmp/ruby_sandbox/webapp
17
22
  EXPOSE 3000
18
- RUN sed -i '2i gem "perimeter_x", :path => "/tmp/ruby_sandbox/perimeterx-ruby-sdk"' /tmp/ruby_sandbox/webapp/Gemfile
23
+ RUN sed -i '2i gem "perimeter_x", :path => "/tmp/ruby_sandbox"' /tmp/ruby_sandbox/webapp/Gemfile
19
24
  RUN bundler update
20
- COPY ./examples/ /tmp/ruby_sandbox/webapp
25
+ COPY ./dev/site/ /tmp/ruby_sandbox/webapp
21
26
  CMD ["rails","server","-b","0.0.0.0"]
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perimeter_x (1.3.0)
5
- activesupport (>= 4.2.0)
4
+ perimeter_x (2.0.0)
5
+ activesupport (>= 5.2.4.3)
6
6
  concurrent-ruby (~> 1.0, >= 1.0.5)
7
7
  mustache (~> 1.0, >= 1.0.3)
8
8
  typhoeus (~> 1.1, >= 1.1.2)
@@ -10,51 +10,52 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
- activesupport (5.0.2)
13
+ activesupport (6.0.3.2)
14
14
  concurrent-ruby (~> 1.0, >= 1.0.2)
15
- i18n (~> 0.7)
15
+ i18n (>= 0.7, < 2)
16
16
  minitest (~> 5.1)
17
17
  tzinfo (~> 1.1)
18
- concurrent-ruby (1.0.5)
19
- diff-lcs (1.3)
20
- ethon (0.10.1)
18
+ zeitwerk (~> 2.2, >= 2.2.2)
19
+ concurrent-ruby (1.1.6)
20
+ diff-lcs (1.4.4)
21
+ ethon (0.12.0)
21
22
  ffi (>= 1.3.0)
22
- ffi (1.9.18)
23
- i18n (0.8.6)
24
- metaclass (0.0.4)
25
- minitest (5.10.1)
26
- mocha (1.2.1)
27
- metaclass (~> 0.0.1)
28
- mustache (1.0.5)
29
- rake (10.4.2)
30
- rspec (3.5.0)
31
- rspec-core (~> 3.5.0)
32
- rspec-expectations (~> 3.5.0)
33
- rspec-mocks (~> 3.5.0)
34
- rspec-core (3.5.4)
35
- rspec-support (~> 3.5.0)
36
- rspec-expectations (3.5.0)
23
+ ffi (1.13.1)
24
+ i18n (1.8.3)
25
+ concurrent-ruby (~> 1.0)
26
+ minitest (5.14.1)
27
+ mocha (1.11.2)
28
+ mustache (1.1.1)
29
+ rake (13.0.1)
30
+ rspec (3.9.0)
31
+ rspec-core (~> 3.9.0)
32
+ rspec-expectations (~> 3.9.0)
33
+ rspec-mocks (~> 3.9.0)
34
+ rspec-core (3.9.2)
35
+ rspec-support (~> 3.9.3)
36
+ rspec-expectations (3.9.2)
37
37
  diff-lcs (>= 1.2.0, < 2.0)
38
- rspec-support (~> 3.5.0)
39
- rspec-mocks (3.5.0)
38
+ rspec-support (~> 3.9.0)
39
+ rspec-mocks (3.9.1)
40
40
  diff-lcs (>= 1.2.0, < 2.0)
41
- rspec-support (~> 3.5.0)
42
- rspec-support (3.5.0)
41
+ rspec-support (~> 3.9.0)
42
+ rspec-support (3.9.3)
43
43
  thread_safe (0.3.6)
44
- typhoeus (1.1.2)
44
+ typhoeus (1.4.0)
45
45
  ethon (>= 0.9.0)
46
- tzinfo (1.2.3)
46
+ tzinfo (1.2.7)
47
47
  thread_safe (~> 0.1)
48
+ zeitwerk (2.3.1)
48
49
 
49
50
  PLATFORMS
50
51
  ruby
51
52
 
52
53
  DEPENDENCIES
53
- bundler (~> 1.14)
54
+ bundler (>= 2.1)
54
55
  mocha (~> 1.2, >= 1.2.1)
55
56
  perimeter_x!
56
- rake (~> 10.0)
57
+ rake (>= 12.3)
57
58
  rspec (~> 3.0)
58
59
 
59
60
  BUNDLED WITH
60
- 1.14.6
61
+ 2.1.4
@@ -5,6 +5,31 @@ 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.0.0] - 2020-07-24
9
+ ### Added
10
+ - Added fields to Block Activity: simulated_block, http_version, http_method, risk_rtt, px_orig_cookie
11
+ - Added fields to page_requested activity: pass_reason, risk_rtt, px_orig_cookie
12
+ - Added px_orig_cookie field to risk_api in case of cookie_decryption_failed
13
+ - Added support for captcha v2
14
+ - Added support for Advanced Blocking Response
15
+ - Added support for whitelise routes
16
+ - Added support for bypass monitor header
17
+ - Added support for extracting vid from _pxvid cookie
18
+ - Added support for rate limit
19
+ - Added risk_cookie_max_iterations configuration
20
+
21
+ ### Fixed
22
+ - Updated dependencies
23
+ - Updated sample site dockerfile
24
+ - Fixed monitor mode
25
+ - Fixed send_page_activities and send_block_activities configurations
26
+ - Updated risk to v3
27
+ - Refactored ip header extraction
28
+ - Renamed block_uuid field to client_uuid
29
+ - Renamed perimeterx_server_host configuration to backend_url
30
+ - Updated risk_response handling: pass the request if risk_response.status is -1
31
+ - Forcing http header values to be utf8
32
+
8
33
  ## [1.4.0] - 2018-03-18
9
34
  ### Fixed
10
35
  - Incorrect assigment for s2s_call_reason
@@ -1,6 +1,7 @@
1
1
  require 'concurrent'
2
2
  require 'json'
3
3
  require 'base64'
4
+ require 'uri'
4
5
  require 'perimeterx/configuration'
5
6
  require 'perimeterx/utils/px_logger'
6
7
  require 'perimeterx/utils/px_constants'
@@ -10,7 +11,6 @@ require 'perimeterx/internal/perimeter_x_context'
10
11
  require 'perimeterx/internal/clients/perimeter_x_activity_client'
11
12
  require 'perimeterx/internal/validators/perimeter_x_s2s_validator'
12
13
  require 'perimeterx/internal/validators/perimeter_x_cookie_validator'
13
- require 'perimeterx/internal/validators/perimeter_x_captcha_validator'
14
14
 
15
15
  module PxModule
16
16
  # Module expose API
@@ -25,10 +25,7 @@ module PxModule
25
25
  return instance_exec(px_ctx, &px_config[:custom_verification_handler])
26
26
  end
27
27
 
28
- # Invalidate _pxCaptcha, can be done only on the controller level
29
- cookies[:_pxCaptcha] = {value: "", expires: -1.minutes.from_now}
30
-
31
- unless px_ctx.nil? || px_ctx.context[:verified]
28
+ unless px_ctx.nil? || px_ctx.context[:verified] || (px_config[:module_mode] == PxModule::MONITOR_MODE && !px_ctx.context[:should_bypass_monitor])
32
29
  # In case custom block handler exists (soon to be deprecated)
33
30
  if px_config.key?(:custom_block_handler)
34
31
  px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
@@ -36,16 +33,50 @@ module PxModule
36
33
  "#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
37
34
  return instance_exec(px_ctx, &px_config[:custom_block_handler])
38
35
  else
39
- # Generate template
40
- px_config[:logger].debug("#{msg_title}: sending default block page")
41
- html = PxTemplateFactory.get_template(px_ctx, px_config)
42
- response.headers['Content-Type'] = 'text/html'
43
- response.status = 403
36
+ if px_ctx.context[:block_action]== 'rate_limit'
37
+ px_config[:logger].debug("#{msg_title}: sending rate limit page")
38
+ response.status = 429
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)
53
+
44
54
  # Web handler
45
55
  if px_ctx.context[:cookie_origin] == 'cookie'
46
- px_config[:logger].debug('#{msg_title}: web block')
47
- response.headers['Content-Type'] = 'text/html'
48
- render :html => html
56
+
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;
59
+
60
+ if (is_json_response)
61
+ px_config[:logger].debug("#{msg_title}: advanced blocking response response")
62
+ response.headers['Content-Type'] = 'application/json'
63
+
64
+ hash_json = {
65
+ :appId => px_config[:app_id],
66
+ :jsClientSrc => px_template_object[:js_client_src],
67
+ :firstPartyEnabled => false,
68
+ :uuid => px_ctx.context[:uuid],
69
+ :vid => px_ctx.context[:vid],
70
+ :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
71
+ :blockScript => px_template_object[:block_script],
72
+ }
73
+
74
+ 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
+ end
49
80
  else # Mobile SDK
50
81
  px_config[:logger].debug("#{msg_title}: mobile sdk block")
51
82
  response.headers['Content-Type'] = 'application/json'
@@ -99,19 +130,27 @@ module PxModule
99
130
  #Instance Methods
100
131
  def verify(env)
101
132
  begin
133
+
134
+ # check module_enabled
102
135
  @logger.debug('PerimeterX[pxVerify]')
103
136
  if !@px_config[:module_enabled]
104
137
  @logger.warn('Module is disabled')
105
138
  return nil
106
139
  end
107
- req = ActionDispatch::Request.new(env)
108
- px_ctx = PerimeterXContext.new(@px_config, req)
109
140
 
110
- # Captcha phase
111
- captcha_verified, px_ctx = @px_captcha_validator.verify(px_ctx)
112
- if captcha_verified
113
- return handle_verification(px_ctx)
141
+ req = ActionDispatch::Request.new(env)
142
+
143
+ # filter whitelist routes
144
+ url_path = URI.parse(req.original_url).path
145
+ if url_path && !url_path.empty?
146
+ if check_whitelist_routes(px_config[:whitelist_routes], url_path)
147
+ @logger.debug("PerimeterX[pxVerify]: whitelist route: #{url_path}")
148
+ return nil
149
+ end
114
150
  end
151
+
152
+ # create context
153
+ px_ctx = PerimeterXContext.new(@px_config, req)
115
154
 
116
155
  # Cookie phase
117
156
  cookie_verified, px_ctx = @px_cookie_validator.verify(px_ctx)
@@ -136,8 +175,7 @@ module PxModule
136
175
 
137
176
  @px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
138
177
  @px_s2s_validator = PerimeterxS2SValidator.new(@px_config, @px_http_client)
139
- @px_captcha_validator = PerimeterxCaptchaValidator.new(@px_config, @px_http_client)
140
- @logger.debug('PerimeterX[initialize]Z')
178
+ @logger.debug('PerimeterX[initialize]')
141
179
  end
142
180
 
143
181
  private def handle_verification(px_ctx)
@@ -145,6 +183,8 @@ module PxModule
145
183
  @logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
146
184
 
147
185
  score = px_ctx.context[:score]
186
+ px_ctx.context[:should_bypass_monitor] = @px_config[:bypass_monitor_header] && px_ctx.context[:headers][@px_config[:bypass_monitor_header].to_sym] == '1';
187
+
148
188
  px_ctx.context[:verified] = score < @px_config[:blocking_score]
149
189
  # Case PASS request
150
190
  if px_ctx.context[:verified]
@@ -157,8 +197,8 @@ module PxModule
157
197
  @px_activity_client.send_block_activity(px_ctx)
158
198
 
159
199
  # In case were in monitor mode, end here
160
- if @px_config[:module_mode] == PxModule::MONITOR_MODE
161
- @logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
200
+ if @px_config[:module_mode] == PxModule::MONITOR_MODE && !px_ctx.context[:should_bypass_monitor]
201
+ @logger.debug("PerimeterX[handle_verification]: monitor mode is on, passing request")
162
202
  return px_ctx
163
203
  end
164
204
 
@@ -167,6 +207,18 @@ module PxModule
167
207
  return px_ctx
168
208
  end
169
209
 
210
+ private def check_whitelist_routes(whitelist_routes, path)
211
+ whitelist_routes.each do |whitelist_route|
212
+ if whitelist_route.is_a?(Regexp) && path.match(whitelist_route)
213
+ return true
214
+ end
215
+ if whitelist_route.is_a?(String) && path.start_with?(whitelist_route)
216
+ return true
217
+ end
218
+ end
219
+ false
220
+ end
221
+
170
222
  private_class_method :new
171
223
  end
172
224
  end
@@ -8,29 +8,33 @@ module PxModule
8
8
  attr_accessor :PX_DEFAULT
9
9
 
10
10
  PX_DEFAULT = {
11
- :app_id => nil,
12
- :cookie_key => nil,
13
- :auth_token => nil,
14
- :module_enabled => true,
15
- :captcha_provider => "reCaptcha",
16
- :challenge_enabled => true,
17
- :encryption_enabled => true,
18
- :blocking_score => 70,
19
- :sensitive_headers => ["http-cookie", "http-cookies"],
20
- :api_connect_timeout => 1,
21
- :api_timeout => 1,
22
- :max_buffer_len => 10,
23
- :send_page_activities => true,
24
- :send_block_activities => true,
25
- :sdk_name => PxModule::SDK_NAME,
26
- :debug => false,
27
- :module_mode => PxModule::ACTIVE_MODE,
28
- :local_proxy => false,
29
- :sensitive_routes => []
11
+ :app_id => nil,
12
+ :cookie_key => nil,
13
+ :auth_token => nil,
14
+ :module_enabled => true,
15
+ :challenge_enabled => true,
16
+ :encryption_enabled => true,
17
+ :blocking_score => 100,
18
+ :sensitive_headers => ["http-cookie", "http-cookies"],
19
+ :api_connect_timeout => 1,
20
+ :api_timeout => 1,
21
+ :max_buffer_len => 10,
22
+ :send_page_activities => true,
23
+ :send_block_activities => true,
24
+ :sdk_name => PxModule::SDK_NAME,
25
+ :debug => false,
26
+ :module_mode => PxModule::MONITOR_MODE,
27
+ :local_proxy => false,
28
+ :sensitive_routes => [],
29
+ :whitelist_routes => [],
30
+ :ip_headers => [],
31
+ :ip_header_function => nil,
32
+ :bypass_monitor_header => nil,
33
+ :risk_cookie_max_iterations => 5000
30
34
  }
31
35
 
32
36
  def initialize(params)
33
- PX_DEFAULT[:perimeterx_server_host] = "https://sapi-#{params[:app_id].downcase}.perimeterx.net"
37
+ PX_DEFAULT[:backend_url] = "https://sapi-#{params[:app_id].downcase}.perimeterx.net"
34
38
  @configuration = PX_DEFAULT.merge(params)
35
39
  @configuration[:logger] = PxLogger.new(@configuration[:debug])
36
40
  end
@@ -49,41 +49,61 @@ module PxModule
49
49
 
50
50
  def send_block_activity(px_ctx)
51
51
  @logger.debug("PerimeterxActivitiesClients[send_block_activity]")
52
- if (!@px_config[:send_page_acitivites])
52
+ if (!@px_config[:send_block_activities])
53
53
  @logger.debug("PerimeterxActivitiesClients[send_block_activity]: sending activites is disabled")
54
54
  return
55
55
  end
56
56
 
57
57
  details = {
58
- :block_uuid => px_ctx.context[:uuid],
58
+ :http_version => px_ctx.context[:http_version],
59
+ :http_method => px_ctx.context[:http_method],
60
+ :client_uuid => px_ctx.context[:uuid],
59
61
  :block_score => px_ctx.context[:score],
60
- :block_reason => px_ctx.context[:blocking_reason]
62
+ :block_reason => px_ctx.context[:blocking_reason],
63
+ :simulated_block => @px_config[:module_mode] == PxModule::MONITOR_MODE
61
64
  }
62
65
 
66
+ if (px_ctx.context.key?(:risk_rtt))
67
+ details[:risk_rtt] = px_ctx.context[:risk_rtt]
68
+ end
69
+
70
+ if (px_ctx.context.key?(:px_orig_cookie))
71
+ details[:px_orig_cookie] = px_ctx.context[:px_orig_cookie]
72
+ end
73
+
63
74
  send_to_perimeterx(PxModule::BLOCK_ACTIVITY, px_ctx, details)
64
75
 
65
76
  end
66
77
 
67
78
  def send_page_requested_activity(px_ctx)
68
79
  @logger.debug("PerimeterxActivitiesClients[send_page_requested_activity]")
69
- if (!@px_config[:send_page_acitivites])
80
+ if (!@px_config[:send_page_activities])
70
81
  return
71
82
  end
72
83
 
73
84
  details = {
74
85
  :http_version => px_ctx.context[:http_version],
75
86
  :http_method => px_ctx.context[:http_method],
76
- :client_uuid => px_ctx.context[:uuid]
87
+ :client_uuid => px_ctx.context[:uuid],
88
+ :pass_reason => px_ctx.context[:pass_reason]
77
89
  }
78
90
 
79
91
  if (px_ctx.context.key?(:decoded_cookie))
80
92
  details[:px_cookie] = px_ctx.context[:decoded_cookie]
81
93
  end
82
94
 
95
+ if (px_ctx.context.key?(:px_orig_cookie))
96
+ details[:px_orig_cookie] = px_ctx.context[:px_orig_cookie]
97
+ end
98
+
83
99
  if (px_ctx.context.key?(:cookie_hmac))
84
100
  details[:px_cookie_hmac] = px_ctx.context[:cookie_hmac]
85
101
  end
86
102
 
103
+ if (px_ctx.context.key?(:risk_rtt))
104
+ details[:risk_rtt] = px_ctx.context[:risk_rtt]
105
+ end
106
+
87
107
  send_to_perimeterx(PxModule::PAGE_REQUESTED_ACTIVITY, px_ctx, details)
88
108
  end
89
109
  end