perimeter_x 1.0.6.pre.alpha → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -1,34 +1,49 @@
1
- require "perimeterx/utils/px_logger"
2
- require "httpclient"
1
+ require 'perimeterx/utils/px_logger'
2
+ require 'typhoeus'
3
+ require 'concurrent'
3
4
 
4
- class PxHttpClient
5
- L = PxLogger.instance
6
- attr_accessor :px_config
7
- attr_accessor :BASE_URL
8
- attr_accessor :http_client
5
+ module PxModule
6
+ class PxHttpClient
7
+ include Concurrent::Async
9
8
 
10
- def initialize(px_config)
11
- L.info("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config['perimeterx_server_host']}")
12
- @px_config = px_config
13
- @http_client = HTTPClient.new(:base_url => px_config['perimeterx_server_host'])
14
- end
9
+ attr_accessor :px_config
10
+ attr_accessor :px_client
15
11
 
16
- def post(path, body, headers, connection_timeout = 0, timeoute = 0)
17
- s = Time.now
18
- begin
19
- L.info("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
20
- response = @http_client.post(path,
21
- :header => headers,
22
- :body => body.to_json(),
23
- :timeout => @px_config['api_timeout']
24
- )
25
- rescue Net::OpenTimeout, Net::ReadTimeout => error
26
- L.warn("PerimeterxS2SValidator[verify]: request timedout")
27
- return false
12
+ def initialize(px_config)
13
+ @px_config = px_config
14
+ @logger = px_config[:logger]
15
+ @logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:backend_url]}")
28
16
  end
29
- e = Time.now
30
- L.info("PxHttpClient[post]: runtime: #{e-s}")
31
- return response
32
- end
33
17
 
18
+ # Runs a POST command to Perimeter X servers
19
+ # Params:
20
+ # +path+:: string containing uri
21
+ # +body+:: hash object, containing the request body, must be converted to json format
22
+ # +headers+:: hash object, hold headers
23
+ # +api_timeout+:: int, sets the timeout for a request
24
+ # +connection_timeout+:: int, sets the timeout for opening a connection
25
+
26
+ def post(path, body, headers, api_timeout = 1, connection_timeout = 1)
27
+ s = Time.now
28
+ begin
29
+ @logger.debug("PxHttpClient[post]: posting to #{path} headers {#{headers.to_json()}} body: {#{body.to_json()}} ")
30
+ response = Typhoeus.post(
31
+ "#{px_config[:backend_url]}#{path}",
32
+ headers: headers,
33
+ body: body.to_json,
34
+ timeout: api_timeout,
35
+ connecttimeout: connection_timeout
36
+ )
37
+ if response.timed_out?
38
+ @logger.warn('PerimeterxS2SValidator[verify]: request timed out')
39
+ return false
40
+ end
41
+ ensure
42
+ e = Time.now
43
+ @logger.debug("PxHttpClient[post]: runtime: #{(e-s) * 1000.0}")
44
+ end
45
+ return response
46
+ end
47
+
48
+ end
34
49
  end
@@ -1,11 +1,17 @@
1
1
  require 'logger'
2
+ module PxModule
2
3
 
3
- class PxLogger
4
- @@instance = Logger.new(STDOUT)
4
+ class PxLogger < Logger
5
5
 
6
- def self.instance
7
- return @@instance
8
- end
6
+ def initialize(debug)
7
+ if debug
8
+ super(STDOUT)
9
+ else
10
+ super(nil)
11
+ end
12
+
13
+ end
9
14
 
10
- private_class_method :new
15
+ end
16
+
11
17
  end
@@ -0,0 +1,41 @@
1
+ require 'mustache'
2
+ require 'perimeterx/utils/px_constants'
3
+ module PxModule
4
+ module PxTemplateFactory
5
+
6
+ def self.get_template(px_ctx, px_config, px_template_object)
7
+ logger = px_config[:logger]
8
+ if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
9
+ logger.debug('PxTemplateFactory[get_template]: px challange triggered')
10
+ return px_ctx.context[:block_action_data].html_safe
11
+ end
12
+
13
+ view = Mustache.new
14
+
15
+ if (px_ctx.context[:block_action] == 'rate_limit')
16
+ logger.debug('PxTemplateFactory[get_template]: rendering ratelimit template')
17
+ template_type = RATELIMIT_TEMPLATE
18
+ else
19
+ logger.debug('PxTemplateFactory[get_template]: rendering template')
20
+ template_type = CHALLENGE_TEMPLATE
21
+ end
22
+
23
+ Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
24
+
25
+ view[PxModule::PROP_APP_ID] = px_config[:app_id]
26
+ view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
27
+ view[PxModule::PROP_VID] = px_ctx.context[:vid]
28
+ view[PxModule::PROP_UUID] = px_ctx.context[:uuid]
29
+ view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
30
+ view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
31
+ view[PxModule::PROP_JS_REF] = px_config[:js_ref]
32
+ view[PxModule::PROP_HOST_URL] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
33
+ view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
34
+ view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
35
+ view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
36
+ view[PxModule::PROP_FIRST_PARTY_ENABLED] = false
37
+
38
+ return view.render.html_safe
39
+ end
40
+ end #end class
41
+ end #end module
@@ -0,0 +1,175 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Access to this page has been denied.</title>
7
+ <link href="https://fonts.googleapis.com/css?family=Open+Sans:300" rel="stylesheet">
8
+ <style>
9
+ html, body {
10
+ margin: 0;
11
+ padding: 0;
12
+ font-family: 'Open Sans', sans-serif;
13
+ color: #000;
14
+ }
15
+
16
+ a {
17
+ color: #c5c5c5;
18
+ text-decoration: none;
19
+ }
20
+
21
+ .container {
22
+ align-items: center;
23
+ display: flex;
24
+ flex: 1;
25
+ justify-content: space-between;
26
+ flex-direction: column;
27
+ height: 100%;
28
+ }
29
+
30
+ .container > div {
31
+ width: 100%;
32
+ display: flex;
33
+ justify-content: center;
34
+ }
35
+
36
+ .container > div > div {
37
+ display: flex;
38
+ width: 80%;
39
+ }
40
+
41
+ .customer-logo-wrapper {
42
+ padding-top: 2rem;
43
+ flex-grow: 0;
44
+ background-color: #fff;
45
+ visibility: {{logoVisibility}};
46
+ }
47
+
48
+ .customer-logo {
49
+ border-bottom: 1px solid #000;
50
+ }
51
+
52
+ .customer-logo > img {
53
+ padding-bottom: 1rem;
54
+ max-height: 50px;
55
+ max-width: 100%;
56
+ }
57
+
58
+ .page-title-wrapper {
59
+ flex-grow: 2;
60
+ }
61
+
62
+ .page-title {
63
+ flex-direction: column-reverse;
64
+ }
65
+
66
+ .content-wrapper {
67
+ flex-grow: 5;
68
+ }
69
+
70
+ .content {
71
+ flex-direction: column;
72
+ }
73
+
74
+ .page-footer-wrapper {
75
+ align-items: center;
76
+ flex-grow: 0.2;
77
+ background-color: #000;
78
+ color: #c5c5c5;
79
+ font-size: 70%;
80
+ }
81
+
82
+ @media (min-width: 768px) {
83
+ html, body {
84
+ height: 100%;
85
+ }
86
+ }
87
+ </style>
88
+ <!-- Custom CSS -->
89
+ {{#cssRef}}
90
+ <link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
91
+ {{/cssRef}}
92
+ </head>
93
+
94
+ <body>
95
+ <section class="container">
96
+ <div class="customer-logo-wrapper">
97
+ <div class="customer-logo">
98
+ <img src="{{customLogo}}" alt="Logo"/>
99
+ </div>
100
+ </div>
101
+ <div class="page-title-wrapper">
102
+ <div class="page-title">
103
+ <h1>Please verify you are a human</h1>
104
+ </div>
105
+ </div>
106
+ <div class="content-wrapper">
107
+ <div class="content">
108
+
109
+ <div id="px-captcha">
110
+ </div>
111
+ <p>
112
+ Access to this page has been denied because we believe you are using automation tools to browse the
113
+ website.
114
+ </p>
115
+ <p>
116
+ This may happen as a result of the following:
117
+ </p>
118
+ <ul>
119
+ <li>
120
+ Javascript is disabled or blocked by an extension (ad blockers for example)
121
+ </li>
122
+ <li>
123
+ Your browser does not support cookies
124
+ </li>
125
+ </ul>
126
+ <p>
127
+ Please make sure that Javascript and cookies are enabled on your browser and that you are not blocking
128
+ them from loading.
129
+ </p>
130
+ <p>
131
+ Reference ID: #{{refId}}
132
+ </p>
133
+ </div>
134
+ </div>
135
+ <div class="page-footer-wrapper">
136
+ <div class="page-footer">
137
+ <p>
138
+ Powered by
139
+ <a href="https://www.perimeterx.com/whywasiblocked">PerimeterX</a>
140
+ , Inc.
141
+ </p>
142
+ </div>
143
+ </div>
144
+ </section>
145
+ <!-- Px -->
146
+ <script>
147
+ window._pxAppId = '{{appId}}';
148
+ window._pxJsClientSrc = '{{{jsClientSrc}}}';
149
+ window._pxFirstPartyEnabled = {{firstPartyEnabled}};
150
+ window._pxVid = '{{vid}}';
151
+ window._pxUuid = '{{uuid}}';
152
+ window._pxHostUrl = '{{{hostUrl}}}';
153
+ </script>
154
+ <script>
155
+ var s = document.createElement('script');
156
+ s.src = '{{{blockScript}}}';
157
+ var p = document.getElementsByTagName('head')[0];
158
+ p.insertBefore(s, null);
159
+ if ({{firstPartyEnabled}}) {
160
+ s.onerror = function () {
161
+ s = document.createElement('script');
162
+ var suffixIndex = '{{{blockScript}}}'.indexOf('captcha.js');
163
+ var temperedBlockScript = '{{{blockScript}}}'.substring(suffixIndex);
164
+ s.src = '//captcha.px-cdn.net/{{appId}}/' + temperedBlockScript;
165
+ p.parentNode.insertBefore(s, p);
166
+ };
167
+ }
168
+ </script>
169
+
170
+ <!-- Custom Script -->
171
+ {{#jsRef}}
172
+ <script src="{{{jsRef}}}"></script>
173
+ {{/jsRef}}
174
+ </body>
175
+ </html>
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>Too Many Requests</title>
4
+ </head>
5
+ <body>
6
+ <h1>Too Many Requests</h1>
7
+ <p>Reached maximum requests limitation, try again soon.</p>
8
+ </body>
9
+ </html>
@@ -1,3 +1,3 @@
1
- module PerimeterX
2
- VERSION = '1.0.6-alpha'
1
+ module PxModule
2
+ VERSION = '2.0.0'
3
3
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |gem|
9
9
  gem.description = "PerimeterX ruby module to monitor and block traffic according to PerimeterX risk score"
10
10
  gem.licenses = ['MIT']
11
11
  gem.homepage = "https://www.perimeterx.com"
12
- gem.version = PerimeterX::VERSION
12
+ gem.version = PxModule::VERSION
13
13
 
14
14
  gem.authors = ["Nitzan Goldfeder"]
15
15
  gem.email = "nitzan@perimeterx.com"
@@ -22,13 +22,19 @@ Gem::Specification.new do |gem|
22
22
  gem.bindir = "exe"
23
23
  gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
24
  gem.require_paths = ["lib"]
25
- gem.add_development_dependency "bundler", "~> 1.14"
26
- gem.add_development_dependency "rake", "~> 10.0"
25
+ gem.add_development_dependency "bundler", ">= 2.1"
26
+ gem.add_development_dependency "rake", ">= 12.3"
27
27
 
28
28
  gem.extra_rdoc_files = ["readme.md", "changelog.md"]
29
29
  gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "PerimeterX"]
30
30
 
31
31
  gem.required_ruby_version = '>= 2.3'
32
32
 
33
- gem.add_dependency('httpclient', '2.8.2.4')
33
+ gem.add_dependency('concurrent-ruby', '~> 1.0', '>= 1.0.5')
34
+ gem.add_dependency('typhoeus', '~> 1.1', '>= 1.1.2')
35
+ gem.add_dependency('mustache', '~> 1.0', '>= 1.0.3')
36
+ gem.add_dependency('activesupport', '>= 5.2.4.3')
37
+
38
+ gem.add_development_dependency 'rspec', '~> 3.0'
39
+ gem.add_development_dependency 'mocha', '~> 1.2', '>= 1.2.1'
34
40
  end
data/readme.md CHANGED
@@ -1,15 +1,37 @@
1
+ [![Build Status](https://travis-ci.org/PerimeterX/perimeterx-ruby-sdk.svg?branch=master)](https://travis-ci.org/PerimeterX/perimeterx-ruby-sdk)
2
+
1
3
  ![image](http://media.marketwire.com/attachments/201604/34215_PerimeterX_logo.jpg)
2
4
  #
3
5
  [PerimeterX](http://www.perimeterx.com) Ruby SDK
4
6
  =============================================================
5
7
 
8
+ > Latest stable version: [v1.3.0](https://rubygems.org/gems/perimeter_x/versions/1.3.0)
9
+
6
10
  Table of Contents
7
11
  -----------------
8
- - [Usage](#usage)
12
+ **[Usage](#usage)**
9
13
  * [Dependencies](#dependencies)
10
14
  * [Installation](#installation)
11
15
  * [Basic Usage Example](#basic-usage)
12
- - [Configuration](#configuration)
16
+
17
+ **[Configuration](#configuration)**
18
+ * [Configuring Required Parameters](#requireied-params)
19
+ * [Blocking Score](#blocking-score)
20
+ * [Custom Verification Action](#custom-verification-action)
21
+ * [Custom Block Page](#custom-block-page)
22
+ * [Enable/Disable Captcha](#captcha-support)
23
+ * [Select Captcha Provider](#captcha-provider)
24
+ * [Extracting Real IP Address](#real-ip)
25
+ * [Custom URI](#custom-uri)
26
+ * [Filter Sensitive Headers](#sensitive-headers)
27
+ * [API Timeouts](#api-timeout)
28
+ * [Send Page Activities](#send-page-activities)
29
+ * [Additional Page Activity Handler](#additional-page-activity-handler)
30
+ * [Monitor Only](#logging)
31
+ * [Debug Mode](#debug-mode)
32
+ * [Whitelist Routes](#whitelist-routes)
33
+
34
+ **[Contributing](#contributing)**
13
35
 
14
36
  <a name="Usage"></a>
15
37
  <a name="dependencies"></a> Dependencies
@@ -17,80 +39,130 @@ Table of Contents
17
39
 
18
40
  - Ruby version 2.3+
19
41
  - Rails version 4.2
20
- - [httparty](https://github.com/jnunemaker/httparty)
42
+ - [concurrent-ruby](https://github.com/ruby-concurrency/concurrent-ruby)
43
+ - [typhoeus](https://github.com/typhoeus/typhoeus)
44
+ - [mustache](https://rubygems.org/gems/mustache)
21
45
 
22
46
  <a name="installation"></a> Installation
23
47
  ----------------------------------------
24
- Install it through command line ```gem install perimeter_x --pre```
25
- Or add it in Gemfile ```gem 'perimeter_x', '~> 1.0.4.pre.alpha'```
48
+ Install it through command line ```gem install perimeter_x```
26
49
 
27
50
 
28
51
  <a name=basic-usage></a> Basic Usage Example
29
52
  ----------------------------------------
30
- On the Rails controller include the PerimeterX SDK via the before_action which will call your defined middleware function. This function is a wrapper for the px_verify method which takes a request and processes it. The verify method can return true if verified, or false if not verified.
31
53
 
32
- The default condition is to always return true for monitoring mode.
54
+ ### Configuration & Initialization
55
+ Create a configuration file at `<rails_app>/config/initializers/perimeterx.rb` and initialize PerimeterX instance on the rails application startup
56
+ ```ruby
57
+ params = {
58
+ :app_id => "APP_ID",
59
+ :cookie_key => "COOKIE_KEY",
60
+ :auth_token => "AUTH_TOKEN"
61
+ }
33
62
 
63
+ PxModule.configure(params)
34
64
  ```
65
+
66
+ On the Rails controller include the PerimeterX SDK via the before_action and call PerimterX middleware function.
67
+
68
+ ```ruby
35
69
  class HomeController < ApplicationController
36
- include PerimeterX
37
- attr_accessor :px
38
- ...
39
- ...
40
- before_action :px_middleware
41
- ...
42
- ...
43
- initialize()
44
- configuration = {
45
- "app_id" => <APP_ID>
46
- "auth_token" => <AUTH_TOKEN>
47
- }
48
- @px = PxModule.instance(params)
49
- end
70
+ include PxModule
71
+
72
+ before_action :px_verify_request
50
73
  ...
51
74
  ...
52
- def px_middleware
53
- px.px_verify(request.env)
54
- end
75
+ end
55
76
  ```
56
77
 
57
- <a name="configuration"></a> Configuration
78
+ <a name="configuration"></a> Configuration options
58
79
  ----------------------------------------
80
+ <a name="requireied-params"></a>**Configuring Required Parameters**
81
+ Configuration options are set on the ``params`` variable on the initializer file.
59
82
 
60
- ** Custom Verification Handler **
61
- A custom verification handler replaces the default handle_verification method and allows you to take a custom action based on the risk score returned by PerimeterX.
83
+ - ``app_id``
84
+ - ``cookie_key``
85
+ - ``auth_token``
62
86
 
63
- When implemented, this method receives a hash variable as input which represents data from the PerimeterX context of the request (px_ctx).
87
+ All parameters are obtainable via the PerimeterX Portal. (Applications and Policies pages)
64
88
 
65
- - `px_ctx[:score] ` contains the risk score
66
- - `px_ctx[:uuid] ` contains the request UUID
89
+ <a name="blocking-score"></a>**Changing the Minimum Score for Blocking**
67
90
 
68
- To replace the default verification behavior, add the configuration a lambda member as shown in the example below.
91
+ >Note: Default blocking value: 100
69
92
 
70
- The method must return boolen value.
93
+ ```ruby
94
+ params = {
95
+ ...
96
+ :blocking_score => 100
97
+ ...
98
+ }
99
+ ```
100
+
101
+ <a name="custom-verification-action"></a>**Custom Verification Handler**
102
+
103
+ > Note: This handler replaces the now deprecated `custom_block_handler`.
71
104
 
105
+ A custom verification handler is being executed inside `px_verify_request` instead of the the default behavior and allows a user to use a custom action based on the risk score returned by PerimeterX.
72
106
 
107
+ When implemented, this method receives a hash variable as input which represents data from the PerimeterX context of the request (px_ctx).
108
+
109
+ - `px_ctx.context[:score]` - contains the risk score
110
+ - `px_ctx.context[:uuid]` - contains the request UUID
111
+ - `px_ctx.context[:verified]` - contains indication whether the request passed verification or was blocked (inspect `px_ctx.context[:block_reason]` for block reason)
112
+
113
+ > Note: to determine whether to return a captcha/block page (HTML) or block JSON payload a reference key on the context will be available: ```px_ctx.context[:format]```
114
+
115
+ To replace the default verification behavior, add the configuration a lambda member as shown in the example below.
73
116
 
74
117
  ```ruby
75
- configuration = {
76
- "app_id" => <APP_ID>,
77
- "auth_token" => <AUTH_TOKEN>,
78
- "custom_verification_handler" => -> (px_ctx) {
79
- if px_ctx[:score] >= 60
80
- # take your action and retun a message or JSON with a status code of 403 and option UUID of the request. Can return false and include action in the px_middleware method.
118
+ params = {
119
+ :app_id => <APP_ID>,
120
+ :auth_token => <AUTH_TOKEN>,
121
+ :custom_verification_handler => -> (px_ctx) {
122
+ if px_ctx.context[:score] >= 60
123
+ # take your action and render an html page or JSON with applicable status code.
124
+ render json: { :score => px_ctx.context[:score] }
81
125
  end
82
- return true
83
126
  }
84
127
  }
85
128
  ```
86
129
 
87
- ** Custom User IP **
130
+ > Note: Unlike previous versions, the method no longer needs to return a boolean value.
131
+
132
+ **Example**
133
+ #### Serving a Custom HTML Page ####
134
+ ```ruby
135
+
136
+ params = {
137
+ :app_id => <APP_ID>,
138
+ :auth_token => <AUTH_TOKEN>,
139
+ ...
140
+ :custom_verification_handler => -> (px_ctx) {
141
+ block_score = px_ctx.context[:score];
142
+ client_uuid = px_ctx.context[:uuid];
143
+ full_url = px_ctx.context[:full_url];
144
+
145
+ html = "<html>
146
+ <body>
147
+ <div>Access to #{full_url} has been blocked.</div>
148
+ <div>Block reference - #{client_uuid} </div>
149
+ <div>Block score - #{block_score} </div>
150
+ </body>
151
+ </html>".html_safe
152
+ response.headers["Content-Type"] = "text/html"
153
+ response.status = 403
154
+ render :html => html
155
+ }
156
+ }
157
+ ```
158
+
159
+ <a name="real-ip"></a>**Custom User IP**
88
160
 
89
161
  > Note: IP extraction, according to your network setup, is very important. It is common to have a load balancer/proxy on top of your applications, in which case the PerimeterX module will send the system's internal IP as the user's. In order to properly perform processing and detection on server-to-server calls, PerimeterX module needs the real user's IP.
90
162
 
91
163
  By default the clients IP is taken from the ``REMOTE_ADDR`` header, in case the user decides to use different header or custom function that extract the header the following key should be added to the configuration
92
164
 
93
- *** Custom header ***
165
+ ***Custom header***
94
166
  ```ruby
95
167
  configuration = {
96
168
  "app_id" => <APP_ID>,
@@ -98,7 +170,7 @@ configuration = {
98
170
  "custom_user_ip" => <HTTP_HEADER_NAME>,
99
171
  ```
100
172
 
101
- *** Custom Function ***
173
+ ***Custom Function***
102
174
  > Note: the function receive as a first parameter the controller request and must return the ip at the end as string
103
175
 
104
176
  ```ruby
@@ -111,3 +183,147 @@ configuration = {
111
183
  }
112
184
  }
113
185
  ```
186
+ <a name="custom-block-page"></a>**Customizing Default Block Pages**
187
+
188
+ Adding a custom logo to the blocking page is by providing the `params` a key `custom_logo` , the logo will be displayed at the top div of the the block page The logo's `max-heigh` property would be `150px` and width would be set to `auto`
189
+
190
+ The key custom_logo expects a valid URL address such as https://s.perimeterx.net/logo.png
191
+
192
+ ```ruby
193
+ params = [
194
+ :app_id => 'APP_ID',
195
+ :cookie_key => 'COOKIE_SECRET',
196
+ :auth_token => 'AUTH_TOKEN',
197
+ :custom_logo => 'LOGO_URL'
198
+ ];
199
+ ```
200
+
201
+ **Custom JS/CSS**
202
+ The block page can be modified with a custom CSS by adding to the `params` the key `css_ref` and providing a valid URL to the css In addition there is also the option to add a custom JS file by adding `js_ref` key to the pxConfig and providing the JS file that will be loaded with the block page, this key also expects a valid URL
203
+
204
+ ```ruby
205
+ params = [
206
+ :app_id => 'APP_ID',
207
+ :cookie_key => 'COOKIE_SECRET',
208
+ :auth_token => 'AUTH_TOKEN',
209
+ :css_ref => 'CSS',
210
+ :js_ref => 'JS'
211
+ ];
212
+ ```
213
+ > Note: Custom logo/js/css can be added together
214
+
215
+ <a name="logging"></a>**No Blocking, Monitor Only**
216
+ Default mode: PxModule::ACTIVE_MODE
217
+
218
+ - PxModule::ACTIVE_MODE - Module blocks users crossing the predefined block threshold. Server-to-server requests are sent synchronously.
219
+
220
+ - PxModule::$MONITOR_MODE - Module does not block users crossing the predefined block threshold. The `custom_block_handler` function will be eval'd in case one is supplied, upon crossing the defined block threshold.
221
+
222
+ ```ruby
223
+ params[:module_mode] = PxModule::MONITOR_MODE
224
+ ```
225
+
226
+ <a name="captcha-support"></a>**Enable/Disable CAPTCHA on the block page**
227
+ Default mode: enabled
228
+
229
+ By enabling CAPTCHA support, a CAPTCHA will be served as part of the block page, giving real users the ability to identify as a human. By solving the CAPTCHA, the user's score is then cleaned up and the user is allowed to continue normal use.
230
+
231
+ ```ruby
232
+ params[:captcha_enabled] = false
233
+ ```
234
+
235
+ <a name="captcha-provider"></a>**Select CAPTCHA Provider**
236
+
237
+ The CAPTCHA part of the block page can use one of the following:
238
+ * [reCAPTCHA](https://www.google.com/recaptcha)
239
+
240
+ Default: 'reCaptcha'
241
+
242
+ ```ruby
243
+ captchaProvider = "reCaptcha"
244
+ ```
245
+
246
+ <a name="custom-uri"></a>**Custom URI**
247
+
248
+ Default: 'REQUEST_URI'
249
+
250
+ The URI can be returned to the PerimeterX module, using a custom user function, defined on the ``params`` variable
251
+
252
+ ```ruby
253
+ params[:custom_uri] = -> (request) {
254
+ return request.headers['HTTP_X_CUSTOM_URI']
255
+ }
256
+ ```
257
+
258
+ <a name="sensitive-headers"></a>**Filter sensitive headers**
259
+ A list of sensitive headers can be configured to prevent specific headers from being sent to PerimeterX servers (lower case header names). Filtering cookie headers for privacy is set by default, and can be overridden on the `params` variable.
260
+
261
+ Default: cookie, cookies
262
+
263
+ ```ruby
264
+ params[:sensitive_headers] = ['cookie', 'cookies', 'secret-header']
265
+
266
+ ```
267
+
268
+ <a name="api-timeout"></a>**API Timeouts**
269
+ >Note: Controls the timeouts for PerimeterX requests. The API is called when a Risk Cookie does not exist, or is expired or invalid
270
+
271
+ The API Timeout, in seconds (int), to wait for the PerimeterX server API response.
272
+
273
+ Default: 1
274
+
275
+ ```ruby
276
+ params[:api_timeout] = 4
277
+ ```
278
+
279
+ <a name="send-page-activities"></a>**Send Page Activities**
280
+ Default: true
281
+ A boolean flag to enable or disable sending of activities and metrics to PerimeterX on each page request. Enabling this feature will provide data that populates the PerimeterX portal with valuable information, such as the amount of requests blocked and additional API usage statistics.
282
+
283
+ ```ruby
284
+ params[:send_page_activities] = false
285
+ ```
286
+
287
+ <a name="additional-page-activity-handler"></a>**Additional Page Activity Handler**
288
+
289
+ Adding an additional activity handler is done by setting `additional_activity_handler` with a user defined function on the `params` variable. The `additional_activity_handler` function will be executed before sending the data to the PerimeterX portal.
290
+
291
+ Default: Only send activity to PerimeterX as controlled by `params`.
292
+
293
+
294
+
295
+ ```ruby
296
+ params[:additional_activity_handler] = -> (activity_type, px_ctx, details){
297
+ // user defined logic comes here
298
+ };
299
+ ```
300
+
301
+ <a name="debug-mode"></a>**Debug Mode**
302
+ Default: false
303
+
304
+ Enables debug logging mode to STDOUT
305
+ ```ruby
306
+ params[:debug] = true
307
+ ```
308
+
309
+ <a name="whitelist-routes"></a>**Whitelist Routes**
310
+ Default: []
311
+ An array of route prefixes and/or regular expressions that are always whitelisted and not validated by PerimeterX.
312
+ A string value of a path will be treated as a prefix.
313
+ A regexp value of a path will be treated as is.
314
+
315
+ ```ruby
316
+ params[:whitelist_routes] = ["/example", /\A\/example\z/]
317
+ ```
318
+
319
+ <a name="contributing"></a># Contributing #
320
+ ------------------------------
321
+ The following steps are welcome when contributing to our project.
322
+ ###Fork/Clone
323
+ First and foremost, [Create a fork](https://guides.github.com/activities/forking/) of the repository, and clone it locally.
324
+ Create a branch on your fork, preferably using a self descriptive branch name.
325
+
326
+ ###Code/Run
327
+ Help improve our project by implementing missing features, adding capabilities or fixing bugs.
328
+
329
+ To run the code, simply follow the steps in the [installation guide](#installation). Grab the keys from the PerimeterX Portal, and try refreshing your page several times continously. If no default behaviours have been overriden, you should see the PerimeterX block page. Solve the CAPTCHA to clean yourself and start fresh again.