perimeter_x 1.4.0 → 2.0.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
- 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