perimeter_x 1.0.6.pre.alpha → 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.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +6 -3
  3. data/.travis.yml +3 -0
  4. data/Dockerfile +19 -41
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +51 -3
  7. data/LICENSE.txt +9 -12
  8. data/Rakefile +10 -2
  9. data/changelog.md +72 -0
  10. data/examples/app/controllers/home_controller.rb +9 -0
  11. data/examples/app/views/home/index.html.erb.dist +20 -0
  12. data/examples/config/initializers/perimeterx.rb.dist +8 -0
  13. data/examples/{routes.rb → config/routes.rb} +0 -0
  14. data/lib/perimeter_x.rb +192 -37
  15. data/lib/perimeterx/configuration.rb +30 -18
  16. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +110 -0
  17. data/lib/perimeterx/internal/clients/perimeter_x_risk_client.rb +28 -0
  18. data/lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb +5 -0
  19. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb +42 -0
  20. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb +37 -0
  21. data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +148 -0
  22. data/lib/perimeterx/internal/payload/perimeter_x_token_v1.rb +38 -0
  23. data/lib/perimeterx/internal/payload/perimeter_x_token_v3.rb +36 -0
  24. data/lib/perimeterx/internal/perimeter_x_context.rb +112 -53
  25. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +103 -0
  26. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +128 -0
  27. data/lib/perimeterx/utils/px_constants.rb +62 -0
  28. data/lib/perimeterx/utils/px_http_client.rb +43 -28
  29. data/lib/perimeterx/utils/px_logger.rb +12 -6
  30. data/lib/perimeterx/utils/px_template_factory.rb +41 -0
  31. data/lib/perimeterx/utils/templates/block_template.mustache +175 -0
  32. data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
  33. data/lib/perimeterx/version.rb +2 -2
  34. data/perimeter_x.gemspec +10 -4
  35. data/readme.md +258 -42
  36. metadata +130 -24
  37. data/bin/console +0 -14
  38. data/bin/setup +0 -8
  39. data/examples/home_controller.rb.dist +0 -23
  40. data/lib/perimeterx/internal/perimeter_x_risk_client.rb +0 -29
  41. data/lib/perimeterx/internal/perimeter_x_s2s_validator.rb +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 2d1e967ee5f87625d37e4c7ca523b00ae089f512
4
- data.tar.gz: b08932769740ec74416a0072dc0313c12ce4ea78
2
+ SHA256:
3
+ metadata.gz: a06a83f955ae265238a23df8471c062428cf82ba37612160a439c52af3568e79
4
+ data.tar.gz: 0eaeb8c8b424a219d155f933494c0baec382b96e707cb7af22e676ea83a1fd4f
5
5
  SHA512:
6
- metadata.gz: 52a63dd5557e73e43f7db668f804997d57c27dd3a609f252346353e264a85c8649254e5053e3cd3db86b6a531dd45f40a97bf9de3195daee89e4b21cce2dfb6b
7
- data.tar.gz: 5d213eb7b31094d369959acbd2ea240d2f747298466a922a760836b1d706017a96a6f4a3b9bac7f6865c268ec70f7f718b53fb5b1bea04de61ec83a62f21b8aa
6
+ metadata.gz: c615587bc9e1203636e0a74284aa9faadd3027dc50056929b4bb8fc2cdd6a86bdc0df10c94f3e262f0f313e518fc0397ac2b0b76669fe3e1b5afc85ad589567d
7
+ data.tar.gz: 22faae3132f9fce873829a0feec4b8f9bfcda420960a380d40a5101b4dae793969b8225a5376f2937954519a8c8cb1c20eb252fca096af77bff7980d5c223f71
data/.gitignore CHANGED
@@ -1,20 +1,23 @@
1
1
  *.rbc
2
+ *.iml
2
3
  capybara-*.html
3
4
  .rspec
4
5
  /log
5
6
  /tmp
7
+ /bin
6
8
  /dev
7
9
  /db/*.sqlite3
8
10
  /db/*.sqlite3-journal
9
11
  /public/system
10
12
  /coverage/
11
13
  /spec/tmp
12
- examples/home_controller.rb
14
+ examples/config/initializers/perimeterx.rb
15
+ examples/app/views/home/index.html.erb
13
16
  **.orig
14
17
  *.gem
15
18
  rerun.txt
16
19
  pickle-email-*.html
17
- dev
20
+
18
21
  # TODO Comment out these rules if you are OK with secrets being uploaded to the repo
19
22
  config/initializers/secret_token.rb
20
23
  config/secrets.yml
@@ -22,7 +25,7 @@ config/secrets.yml
22
25
  # dotenv
23
26
  # TODO Comment out this rule if environment variables can be committed
24
27
  .env
25
- .idea
28
+
26
29
  ## Environment normalization:
27
30
  /.bundle
28
31
  /vendor/bundle
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.7.1
data/Dockerfile CHANGED
@@ -1,48 +1,26 @@
1
1
  # Based on manual compile instructions at http://wiki.nginx.org/HttpLuaModule#Installation
2
- FROM ubuntu:14.04
3
- RUN apt-get update && apt-get --force-yes -qq -y install \
4
- build-essential \
5
- ca-certificates \
6
- curl \
7
- git \
8
- libpcre3 \
9
- libpcre3-dev \
10
- libssl-dev \
11
- libreadline-dev \
12
- libyaml-dev \
13
- libgdbm-dev \
14
- libtool \
15
- automake \
16
- bison \
17
- lua-cjson \
18
- libncurses5-dev \
19
- m4 \
20
- libsqlite3-dev \
21
- rsyslog \
22
- sqlite3 \
23
- libxml2-dev \
24
- libxslt1-dev \
25
- libcurl4-openssl-dev \
26
- python-software-properties \
27
- libffi-dev \
28
- nodejs \
29
- wget \
30
- zlib1g-dev
2
+ FROM ruby:2.7.1
31
3
 
32
- RUN gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
33
- RUN /bin/bash -l -c "curl -L get.rvm.io | bash -s stable --rails"
34
- RUN /bin/bash -l -c "rvm install 2.3.0"
35
- RUN /bin/bash -l -c "rvm use 2.3.0"
36
- RUN /bin/bash -l -c "gem install bundler"
37
- RUN /bin/bash -l -c "gem install rails -v 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
9
+ RUN gem install rails --version "$RAILS_VERSION"
10
+ RUN gem install bundler
38
11
  RUN mkdir -p /tmp/ruby_sandbox
39
12
  WORKDIR /tmp/ruby_sandbox
40
- RUN /bin/bash -l -c "rails new webapp"
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/
17
+ RUN rails new webapp
41
18
  WORKDIR /tmp/ruby_sandbox/webapp
42
- RUN /bin/bash -l -c "rails generate controller home index"
19
+
20
+ RUN rails generate controller home index
43
21
  WORKDIR /tmp/ruby_sandbox/webapp
44
22
  EXPOSE 3000
45
- RUN sed -i "2i gem 'perimeter_x', '~> 1.0.5.pre.alpha'" /tmp/ruby_sandbox/webapp/Gemfile
46
- RUN /bin/bash -l -c "bundler update"
47
- RUN /bin/bash -l -c "gem list|grep peri"
48
- CMD ["/bin/bash", "-l", "-c", "rails server -b 0.0.0.0;"]
23
+ RUN sed -i '2i gem "perimeter_x", :path => "/tmp/ruby_sandbox"' /tmp/ruby_sandbox/webapp/Gemfile
24
+ RUN bundler update
25
+ COPY ./dev/site/ /tmp/ruby_sandbox/webapp
26
+ CMD ["rails","server","-b","0.0.0.0"]
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- gem 'httpclient', '2.8.2.4'
3
+ gemspec
@@ -1,13 +1,61 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ perimeter_x (2.0.0)
5
+ activesupport (>= 5.2.4.3)
6
+ concurrent-ruby (~> 1.0, >= 1.0.5)
7
+ mustache (~> 1.0, >= 1.0.3)
8
+ typhoeus (~> 1.1, >= 1.1.2)
9
+
1
10
  GEM
2
11
  remote: https://rubygems.org/
3
12
  specs:
4
- httpclient (2.8.3)
13
+ activesupport (6.0.3.2)
14
+ concurrent-ruby (~> 1.0, >= 1.0.2)
15
+ i18n (>= 0.7, < 2)
16
+ minitest (~> 5.1)
17
+ tzinfo (~> 1.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)
22
+ ffi (>= 1.3.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
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.9.0)
39
+ rspec-mocks (3.9.1)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.9.0)
42
+ rspec-support (3.9.3)
43
+ thread_safe (0.3.6)
44
+ typhoeus (1.4.0)
45
+ ethon (>= 0.9.0)
46
+ tzinfo (1.2.7)
47
+ thread_safe (~> 0.1)
48
+ zeitwerk (2.3.1)
5
49
 
6
50
  PLATFORMS
7
51
  ruby
8
52
 
9
53
  DEPENDENCIES
10
- httpclient (= 2.8.3)
54
+ bundler (>= 2.1)
55
+ mocha (~> 1.2, >= 1.2.1)
56
+ perimeter_x!
57
+ rake (>= 12.3)
58
+ rspec (~> 3.0)
11
59
 
12
60
  BUNDLED WITH
13
- 1.14.6
61
+ 2.1.4
@@ -1,6 +1,4 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2017 nitzanpx
1
+ Copyright © 2016 PerimeterX, Inc.
4
2
 
5
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
4
  of this software and associated documentation files (the "Software"), to deal
@@ -9,13 +7,12 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
7
  copies of the Software, and to permit persons to whom the Software is
10
8
  furnished to do so, subject to the following conditions:
11
9
 
12
- The above copyright notice and this permission notice shall be included in
13
- all copies or substantial portions of the Software.
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14
11
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- THE SOFTWARE.
12
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
14
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
16
+ DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
17
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
18
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
- require "bundler/gem_tasks"
2
- task :default => :spec
1
+ begin
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+ task :test => :spec
8
+ rescue LoadError
9
+ # no rspec available
10
+ end
@@ -0,0 +1,72 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/)
6
+ and this project adheres to [Semantic Versioning](http://semver.org/).
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
+
33
+ ## [1.4.0] - 2018-03-18
34
+ ### Fixed
35
+ - Incorrect assigment for s2s_call_reason
36
+ - Fixed empty token result correct s2s reason
37
+
38
+ ### Added
39
+ - Added support to captcha api v2
40
+ - Mobile sdk support for special tokens 1/2/3
41
+
42
+
43
+ ## [1.3.0] - 2017-07-27
44
+ ### Added
45
+ - Sending client_uuid on page_requested activities
46
+ - Supporting mobile sdk
47
+ ### Fixed
48
+ - Using `request.env` instead of `env`
49
+
50
+ ## [1.2.0] - 2017-06-04
51
+ ### Fixed
52
+ - Default timeouts for post api requests
53
+ - Fixed Dockerfile
54
+ ### Changed
55
+ - Removed httpclient and instead using typheous
56
+ ### Added
57
+ - Using concurrent-ruby for async post requests
58
+
59
+ ## [1.1.0] - 2017-06-04
60
+ ### Added
61
+ - Added support for sensitive routes
62
+
63
+ ## [1.0.5] - 2017-05-07
64
+ ### Fixed
65
+ - Added request format into context for custom callbacks
66
+
67
+ ## [1.0.4] - 2017-04-27
68
+ ### Fixed
69
+ - Constants on px_constants
70
+ - Cookie Validation flow when cookie score was over the configured threshold
71
+ - Using symbols instead of strings for requests body
72
+
@@ -0,0 +1,9 @@
1
+ class HomeController < ApplicationController
2
+ include PxModule
3
+
4
+ before_action :px_verify_request
5
+
6
+ def index
7
+ end
8
+
9
+ end
@@ -0,0 +1,20 @@
1
+ <h1>Home#index</h1>
2
+ <p>Find me in app/views/home/index.html.erb</p>
3
+
4
+ <script type="text/javascript">
5
+ (function(){
6
+ window._pxAppId ='APP_ID';
7
+ // Custom parameters
8
+ // window._pxParam1 = "<param1>";
9
+ var p = document.getElementsByTagName('script')[0],
10
+ s = document.createElement('script');
11
+ s.async = 1;
12
+ s.src = '//client.perimeterx.net/APP_ID/main.min.js';
13
+ p.parentNode.insertBefore(s,p);
14
+ }());
15
+ </script>
16
+ <noscript>
17
+ <div style="position:fixed; top:0; left:0; display:none" width="1" height="1">
18
+ <img src="//collector-APP_ID.perimeterx.net/api/v1/collector/noScript.gif?appId=APP_ID">
19
+ </div>
20
+ </noscript>
@@ -0,0 +1,8 @@
1
+ params = {
2
+ :app_id => "APP_ID",
3
+ :cookie_key => "COOKIE_KEY",
4
+ :auth_token => "AUTH_TOKEN"
5
+ }
6
+
7
+
8
+ PxModule.configure(params)
File without changes
@@ -1,69 +1,224 @@
1
+ require 'concurrent'
2
+ require 'json'
3
+ require 'base64'
4
+ require 'uri'
1
5
  require 'perimeterx/configuration'
2
6
  require 'perimeterx/utils/px_logger'
7
+ require 'perimeterx/utils/px_constants'
3
8
  require 'perimeterx/utils/px_http_client'
9
+ require 'perimeterx/utils/px_template_factory'
4
10
  require 'perimeterx/internal/perimeter_x_context'
5
- require 'perimeterx/internal/perimeter_x_s2s_validator'
11
+ require 'perimeterx/internal/clients/perimeter_x_activity_client'
12
+ require 'perimeterx/internal/validators/perimeter_x_s2s_validator'
13
+ require 'perimeterx/internal/validators/perimeter_x_cookie_validator'
6
14
 
7
- module PerimeterX
8
- class PxModule
9
- L = PxLogger.instance
15
+ module PxModule
16
+ # 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]'
10
21
 
11
- @@singleton__instance__ = nil
12
- @@singleton__mutex__ = Mutex.new
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
27
+
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
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
+
54
+ # Web handler
55
+ if px_ctx.context[:cookie_origin] == 'cookie'
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
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
+ end
93
+ end
94
+ end
95
+
96
+ # Request was verified
97
+ return px_ctx.nil? ? true : px_ctx.context[:verified]
98
+ end
99
+
100
+ def self.configure(params)
101
+ @px_instance = PerimeterX.configure(params)
102
+ end
103
+
104
+
105
+ # PerimeterX Module
106
+ class PerimeterX
107
+ @@__instance = nil
108
+ @@mutex = Mutex.new
13
109
 
14
110
  attr_reader :px_config
15
111
  attr_accessor :px_http_client
112
+ attr_accessor :px_activity_client
16
113
 
17
- def self.instance(params)
18
- return @@singleton__instance__ if @@singleton__instance__
19
- @@singleton__mutex__.synchronize {
20
- return @@singleton__instance__ if @@singleton__instance__
21
- @@singleton__instance__ = new(params)
114
+ #Static methods
115
+ def self.configure(params)
116
+ return true if @@__instance
117
+ @@mutex.synchronize {
118
+ return @@__instance if @@__instance
119
+ @@__instance = new(params)
22
120
  }
23
- @@singleton__instance__
121
+ return true
24
122
  end
25
123
 
26
-
27
- private def initialize(params)
28
- L.info("PerimeterX[initialize]")
29
- @px_config = Configuration.new(params).configuration
30
- @px_http_client = PxHttpClient.new(@px_config)
124
+ def self.instance
125
+ return @@__instance if !@@__instance.nil?
126
+ raise Exception.new('Please initialize perimeter x first')
31
127
  end
32
128
 
33
- def px_verify(env)
129
+
130
+ #Instance Methods
131
+ def verify(env)
34
132
  begin
35
- L.info("PerimeterX[pxVerify]")
36
- req = ActionDispatch::Request.new(env)
37
133
 
38
- if (!@px_config['module_enabled'])
39
- L.warn("Module is disabled")
40
- return true
134
+ # check module_enabled
135
+ @logger.debug('PerimeterX[pxVerify]')
136
+ if !@px_config[:module_enabled]
137
+ @logger.warn('Module is disabled')
138
+ return nil
41
139
  end
42
140
 
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
150
+ end
151
+
152
+ # create context
43
153
  px_ctx = PerimeterXContext.new(@px_config, req)
44
- px_ctx.context[:s2s_call_reason] = "no_cookie"
45
154
 
46
- s2sValidator = PerimeterxS2SValidator.new(px_ctx, @px_config, @px_http_client)
47
- px_ctx = s2sValidator.verify()
48
-
49
- if (px_config.key?('custom_verification_handler'))
50
- return px_config['custom_verification_handler'].call(px_ctx.context)
51
- else
52
- return handle_verification(px_ctx)
155
+ # Cookie phase
156
+ cookie_verified, px_ctx = @px_cookie_validator.verify(px_ctx)
157
+ if !cookie_verified
158
+ @px_s2s_validator.verify(px_ctx)
53
159
  end
160
+
161
+ return handle_verification(px_ctx)
54
162
  rescue Exception => e
55
- puts("#{e.backtrace.first}: #{e.message} (#{e.class})", e.backtrace.drop(1).map { |s| "\t#{s}" })
56
- return true
163
+ @logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
164
+ e.backtrace.drop(1).map {|s| @logger.error("\t#{s}")}
165
+ return nil
57
166
  end
58
167
  end
59
168
 
60
- # private methods
169
+ private def initialize(params)
170
+ @px_config = Configuration.new(params).configuration
171
+ @logger = @px_config[:logger]
172
+ @px_http_client = PxHttpClient.new(@px_config)
173
+
174
+ @px_activity_client = PerimeterxActivitiesClient.new(@px_config, @px_http_client)
175
+
176
+ @px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
177
+ @px_s2s_validator = PerimeterxS2SValidator.new(@px_config, @px_http_client)
178
+ @logger.debug('PerimeterX[initialize]')
179
+ end
180
+
61
181
  private def handle_verification(px_ctx)
62
- L.info("perimeterx processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
63
- return true
182
+ @logger.debug('PerimeterX[handle_verification]')
183
+ @logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
184
+
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
+
188
+ px_ctx.context[:verified] = score < @px_config[:blocking_score]
189
+ # Case PASS request
190
+ if px_ctx.context[:verified]
191
+ @logger.debug("PerimeterX[handle_verification]: score:#{score} < blocking score, passing request")
192
+ @px_activity_client.send_page_requested_activity(px_ctx)
193
+ return px_ctx
194
+ end
195
+
196
+ # Case blocking activity
197
+ @px_activity_client.send_block_activity(px_ctx)
198
+
199
+ # In case were in monitor mode, end here
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")
202
+ return px_ctx
203
+ end
204
+
205
+ @logger.debug('PerimeterX[handle_verification]: verification ended, the request should be blocked')
206
+
207
+ return px_ctx
208
+ end
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
64
220
  end
65
221
 
66
222
  private_class_method :new
67
223
  end
68
-
69
224
  end