perimeter_x 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +47 -0
  3. data/Dockerfile +50 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +55 -0
  6. data/LICENSE.txt +18 -0
  7. data/Rakefile +9 -0
  8. data/changelog.md +0 -0
  9. data/examples/app/controllers/home_controller.rb +9 -0
  10. data/examples/app/views/home/index.html.erb.dist +20 -0
  11. data/examples/config/initializers/perimeterx.rb.dist +8 -0
  12. data/examples/config/routes.rb +62 -0
  13. data/lib/perimeter_x.rb +149 -0
  14. data/lib/perimeterx/configuration.rb +37 -0
  15. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +92 -0
  16. data/lib/perimeterx/internal/clients/perimeter_x_risk_client.rb +28 -0
  17. data/lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb +5 -0
  18. data/lib/perimeterx/internal/perimeter_x_context.rb +82 -0
  19. data/lib/perimeterx/internal/perimeter_x_cookie.rb +140 -0
  20. data/lib/perimeterx/internal/perimeter_x_cookie_v1.rb +42 -0
  21. data/lib/perimeterx/internal/perimeter_x_cookie_v3.rb +37 -0
  22. data/lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb +65 -0
  23. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +69 -0
  24. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +110 -0
  25. data/lib/perimeterx/utils/px_constants.rb +42 -0
  26. data/lib/perimeterx/utils/px_http_client.rb +55 -0
  27. data/lib/perimeterx/utils/px_logger.rb +17 -0
  28. data/lib/perimeterx/utils/px_template_factory.rb +31 -0
  29. data/lib/perimeterx/utils/templates/block.mustache +146 -0
  30. data/lib/perimeterx/utils/templates/captcha.mustache +185 -0
  31. data/lib/perimeterx/version.rb +3 -0
  32. data/perimeter_x.gemspec +39 -0
  33. data/readme.md +294 -0
  34. metadata +192 -0
@@ -0,0 +1,146 @@
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: auto;
56
+ }
57
+
58
+ .page-title-wrapper{
59
+ flex-grow: 2;
60
+ }
61
+ .page-title {
62
+ flex-direction: column-reverse;
63
+ }
64
+
65
+ .content-wrapper{
66
+ flex-grow: 5;
67
+ }
68
+ .content{
69
+ flex-direction: column;
70
+ }
71
+
72
+ .page-footer-wrapper{
73
+ align-items: center;
74
+ flex-grow: 0.2;
75
+ background-color: #000;
76
+ color: #c5c5c5;
77
+ font-size: 70%;
78
+ }
79
+ @media (min-width:768px){
80
+ html,body{
81
+ height: 100%;
82
+ }
83
+ }
84
+ </style>
85
+ <!-- Custom CSS -->
86
+ {{# cssRef }}
87
+ <link rel="stylesheet" type="text/css" href="{{cssRef}}" />
88
+ {{/ cssRef }}
89
+ </head>
90
+ <body>
91
+ <section class="container">
92
+ <div class="customer-logo-wrapper">
93
+ <div class="customer-logo">
94
+ <img src="{{customLogo}}" alt="Logo"/>
95
+ </div>
96
+ </div>
97
+ <div class="page-title-wrapper">
98
+ <div class="page-title">
99
+ <h1>Access to this page has been denied.</h1>
100
+ </div>
101
+ </div>
102
+ <div class="content-wrapper">
103
+ <div class="content">
104
+ <p>
105
+ You have been blocked because we believe you are using automation tools to browse the website.
106
+ </p>
107
+ <p>
108
+ Please note that Javascript and Cookies must be enabled on your browser to access the website.
109
+ </p>
110
+ <p>
111
+ If you think you have been blocked by mistake, please contact the website administrator with the reference ID below.
112
+ </p>
113
+ <p>
114
+ Reference ID: #{{refId}}
115
+ </p>
116
+ </div>
117
+ </div>
118
+ <div class="page-footer-wrapper">
119
+ <div class="page-footer">
120
+ <p>
121
+ Powered by
122
+ <a href="https://www.perimeterx.com">PerimeterX</a>
123
+ , Inc.
124
+ </p>
125
+ </div>
126
+ </div>
127
+ </section>
128
+ <!-- Px -->
129
+ <script>
130
+ (
131
+ function (){
132
+ window._pxAppId = '{{appId}}';
133
+ var p = document.getElementsByTagName("script")[0], s = document.createElement("script");
134
+
135
+ s.async = 1;
136
+ s.src = '//client.perimeterx.net/{{appId}}/main.min.js';
137
+ p.parentNode.insertBefore(s, p);
138
+ } ()
139
+ );
140
+ </script>
141
+ <!-- Custom Script -->
142
+ {{# jsRef }}
143
+ <script src="{{jsRef}}"></script>
144
+ {{/ jsRef }}
145
+ </body>
146
+ </html>
@@ -0,0 +1,185 @@
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: auto;
56
+ }
57
+
58
+ .page-title-wrapper{
59
+ flex-grow: 2;
60
+ }
61
+ .page-title {
62
+ flex-direction: column-reverse;
63
+ }
64
+
65
+ .content-wrapper{
66
+ flex-grow: 5;
67
+ }
68
+ .content{
69
+ flex-direction: column;
70
+ }
71
+
72
+ .page-footer-wrapper{
73
+ align-items: center;
74
+ flex-grow: 0.2;
75
+ background-color: #000;
76
+ color: #c5c5c5;
77
+ font-size: 70%;
78
+ }
79
+
80
+ @media (min-width:768px){
81
+ html,body{
82
+ height: 100%;
83
+ }
84
+ }
85
+ </style>
86
+ <!-- Custom CSS -->
87
+ {{#cssRef}}
88
+ <link rel="stylesheet" type="text/css" href="{{cssRef}}" />
89
+ {{/cssRef}}
90
+ <script src="https://www.google.com/recaptcha/api.js" async defer></script>
91
+ </head>
92
+
93
+ <body>
94
+ <section class="container">
95
+ <div class="customer-logo-wrapper">
96
+ <div class="customer-logo">
97
+ <img src="{{customLogo}}" alt="Logo"/>
98
+ </div>
99
+ </div>
100
+ <div class="page-title-wrapper">
101
+ <div class="page-title">
102
+ <h1>Please verify you are a human</h1>
103
+ </div>
104
+ </div>
105
+ <div class="content-wrapper">
106
+ <div class="content">
107
+ <p>
108
+ Please click "I am not a robot" to continue
109
+ </p>
110
+ <div class="g-recaptcha" data-sitekey="6Lcj-R8TAAAAABs3FrRPuQhLMbp5QrHsHufzLf7b" data-callback="handleCaptcha" data-theme="dark">
111
+ </div>
112
+ <p>
113
+ Access to this page has been denied because we believe you are using automation tools to browse the 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 them from loading.
128
+ </p>
129
+ <p>
130
+ Reference ID: #{{refId}}
131
+ </p>
132
+ </div>
133
+ </div>
134
+ <div class="page-footer-wrapper">
135
+ <div class="page-footer">
136
+ <p>
137
+ Powered by
138
+ <a href="https://www.perimeterx.com">PerimeterX</a>
139
+ , Inc.
140
+ </p>
141
+ </div>
142
+ </div>
143
+ </section>
144
+ <!-- Px -->
145
+ <script>
146
+ (
147
+ function (){
148
+ window._pxAppId = '{{appId}}';
149
+ var p = document.getElementsByTagName("script")[0], s = document.createElement("script");
150
+
151
+ s.async = 1;
152
+ s.src = '//client.perimeterx.net/{{appId}}/main.min.js';
153
+ p.parentNode.insertBefore(s, p);
154
+ } ()
155
+ );
156
+ </script>
157
+ <!-- Captcha -->
158
+ <script>
159
+ window.px_vid = '{{vid}}';
160
+ function handleCaptcha(response){
161
+ var vid = '{{vid}}';
162
+ var uuid = '{{uuid}}';
163
+ var name = "_pxCaptcha";
164
+
165
+ var expiryUtc = new Date(Date.now()+1000*10).toUTCString();
166
+
167
+ var cookieParts = [
168
+ name,
169
+ "=",
170
+ response+":"+vid+":"+uuid,
171
+ "; expires=",
172
+ expiryUtc,
173
+ "; path=/"
174
+ ];
175
+
176
+ document.cookie = cookieParts.join("");
177
+ location.reload();
178
+ }
179
+ </script>
180
+ <!-- Custom Script -->
181
+ {{#jsRef}}
182
+ <script src="{{jsRef}}"></script>
183
+ {{/jsRef}}
184
+ </body>
185
+ </html>
@@ -0,0 +1,3 @@
1
+ module PxModule
2
+ VERSION = '1.0.1'
3
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'perimeterx/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "perimeter_x"
8
+ gem.summary = "PerimeterX ruby implmentation"
9
+ gem.description = "PerimeterX ruby module to monitor and block traffic according to PerimeterX risk score"
10
+ gem.licenses = ['MIT']
11
+ gem.homepage = "https://www.perimeterx.com"
12
+ gem.version = PxModule::VERSION
13
+
14
+ gem.authors = ["Nitzan Goldfeder"]
15
+ gem.email = "nitzan@perimeterx.com"
16
+
17
+ gem.require_paths = ["lib"]
18
+ gem.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|gem|features)/})
20
+ end
21
+
22
+ gem.bindir = "exe"
23
+ gem.executables = gem.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ gem.require_paths = ["lib"]
25
+ gem.add_development_dependency "bundler", "~> 1.14"
26
+ gem.add_development_dependency "rake", "~> 10.0"
27
+
28
+ gem.extra_rdoc_files = ["readme.md", "changelog.md"]
29
+ gem.rdoc_options = ["--line-numbers", "--inline-source", "--title", "PerimeterX"]
30
+
31
+ gem.required_ruby_version = '>= 2.3'
32
+
33
+ gem.add_dependency('httpclient', '2.8.2.4')
34
+ gem.add_dependency('mustache', '~> 1.0', '>= 1.0.3')
35
+ gem.add_dependency('activesupport', '>= 4.2.0')
36
+
37
+ gem.add_development_dependency 'rspec', '~> 3.0'
38
+ gem.add_development_dependency 'mocha', '~> 1.2', '>= 1.2.1'
39
+ end
data/readme.md ADDED
@@ -0,0 +1,294 @@
1
+ ![image](http://media.marketwire.com/attachments/201604/34215_PerimeterX_logo.jpg)
2
+ #
3
+ [PerimeterX](http://www.perimeterx.com) Ruby SDK
4
+ =============================================================
5
+
6
+ Table of Contents
7
+ -----------------
8
+ - [Usage](#usage)
9
+ * [Dependencies](#dependencies)
10
+ * [Installation](#installation)
11
+ * [Basic Usage Example](#basic-usage)
12
+ - [Configuration](#configuration)
13
+ * [Configuring Required Parameters](#requireied-params)
14
+ * [Blocking Score](#blocking-score)
15
+ * [Custom Block Page](#custom-block-page)
16
+ * [Custom Block Action](#custom-block-action)
17
+ * [Enable/Disable Captcha](#captcha-support)
18
+ * [Extracting Real IP Address](#real-ip)
19
+ * [Custom URI](#custom-uri)
20
+ * [Filter Sensitive Headers](#sensitive-headers)
21
+ * [API Timeouts](#api-timeout)
22
+ * [Send Page Activities](#send-page-activities)
23
+ * [Additional Page Activity Handler](#additional-page-activity-handler)
24
+ * [Monitor Only](#logging)
25
+ * [Debug Mode](#debug-mode)
26
+ - [Contributing](#contributing)
27
+
28
+ <a name="Usage"></a>
29
+ <a name="dependencies"></a> Dependencies
30
+ ----------------------------------------
31
+
32
+ - Ruby version 2.3+
33
+ - Rails version 4.2
34
+ - [httparty](https://github.com/jnunemaker/httparty)
35
+
36
+ <a name="installation"></a> Installation
37
+ ----------------------------------------
38
+ Install it through command line ```gem install perimeter_x```
39
+
40
+
41
+ <a name=basic-usage></a> Basic Usage Example
42
+ ----------------------------------------
43
+
44
+ ### Configuration & Initialization
45
+ Create a configuration file at `<rails_app>/config/initializers/perimeterx.rb` and initialize PerimeterX instance on the rails application startup
46
+ ```ruby
47
+ params = {
48
+ :app_id => "APP_ID",
49
+ :cookie_key => "COOKIE_KEY",
50
+ :auth_token => "AUTH_TOKEN"
51
+ }
52
+
53
+ PxModule.configure(params)
54
+ ```
55
+
56
+ On the Rails controller include the PerimeterX SDK via the before_action and call PerimterX middleware function.
57
+
58
+ ```ruby
59
+ class HomeController < ApplicationController
60
+ include PxModule
61
+
62
+ before_filter :px_verify_request
63
+ ...
64
+ ...
65
+ end
66
+ ```
67
+
68
+ <a name="configuration"></a> Configuration options
69
+ ----------------------------------------
70
+ <a name="requireied-params"></a>**Configuring Required Parameters**
71
+ Configuration options are set on the ``params`` variable on the initializer file.
72
+
73
+ - ``app_id``
74
+ - ``cookie_key``
75
+ - ``auth_token``
76
+
77
+ All parameters are obtainable via the PerimeterX Portal. (Applications and Policies pages)
78
+
79
+ <a name="blocking-score"></a>**Changing the Minimum Score for Blocking**
80
+
81
+ >Note: Default blocking value: 70
82
+
83
+ ```ruby
84
+ params = {
85
+ ...
86
+ blocking_score = 100
87
+ ...
88
+ }
89
+ ```
90
+
91
+
92
+
93
+ <a name="custom-block-action"></a>**Custom Verification Handler**
94
+
95
+ 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.
96
+
97
+ When implemented, this method receives a hash variable as input which represents data from the PerimeterX context of the request (px_ctx).
98
+
99
+ - `px_ctx[:score] ` contains the risk score
100
+ - `px_ctx[:uuid] ` contains the request UUID
101
+
102
+ To replace the default verification behavior, add the configuration a lambda member as shown in the example below.
103
+
104
+ The method must return boolen value.
105
+
106
+ ```ruby
107
+ params = {
108
+ :app_id => <APP_ID>,
109
+ :auth_token => <AUTH_TOKEN>,
110
+ :custom_block_handler => -> (px_ctx) {
111
+ if px_ctx.context[:score] >= 60
112
+ # 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.
113
+ end
114
+ return true
115
+ }
116
+ }
117
+ ```
118
+
119
+ **Example**
120
+ ### Serving a Custom HTML Page ###
121
+ ```ruby
122
+
123
+ params[:custom_block_handler] = -> (px_ctx)
124
+ {
125
+ block_score = px_ctx.context[:score];
126
+ block_uuid = px_ctx.context[:uuid];
127
+ full_url = px_ctx.context[:full_url];
128
+
129
+ html = "<html>
130
+ <body>
131
+ <div>Access to #{full_url} has been blocked.</div>
132
+ <div>Block reference - #{block_uuid} </div>
133
+ <div>Block score - #{block_score} </div>
134
+ </body>
135
+ </html>".html_safe
136
+ response.headers["Content-Type"] = "text/html"
137
+ response.status = 403
138
+ render :html => html
139
+ return false
140
+ };
141
+
142
+ PxModule.configure(params)
143
+ ```
144
+
145
+ <a name="real-ip"></a>** Custom User IP **
146
+
147
+ > 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.
148
+
149
+ 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
150
+
151
+ *** Custom header ***
152
+ ```ruby
153
+ configuration = {
154
+ "app_id" => <APP_ID>,
155
+ "auth_token" => <AUTH_TOKEN>,
156
+ "custom_user_ip" => <HTTP_HEADER_NAME>,
157
+ ```
158
+
159
+ *** Custom Function ***
160
+ > Note: the function receive as a first parameter the controller request and must return the ip at the end as string
161
+
162
+ ```ruby
163
+ configuration = {
164
+ "app_id" => <APP_ID>,
165
+ "auth_token" => <AUTH_TOKEN>,
166
+ "custom_user_ip_method" => -> (req) {
167
+ # Method body
168
+ return "1.2.3.4"
169
+ }
170
+ }
171
+ ```
172
+ <a name="custom-block-page"></a>**Customizing Default Block Pages**
173
+
174
+ 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`
175
+
176
+ The key custom_logo expects a valid URL address such as https://s.perimeterx.net/logo.png
177
+
178
+ ```ruby
179
+ params = [
180
+ :app_id => 'APP_ID',
181
+ :cookie_key => 'COOKIE_SECRET',
182
+ :auth_token => 'AUTH_TOKEN',
183
+ :custom_logo => 'LOGO_URL'
184
+ ];
185
+ ```
186
+
187
+ **Custom JS/CSS**
188
+ 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
189
+
190
+ ```ruby
191
+ params = [
192
+ :app_id => 'APP_ID',
193
+ :cookie_key => 'COOKIE_SECRET',
194
+ :auth_token => 'AUTH_TOKEN',
195
+ :css_ref => 'CSS',
196
+ :js_ref => 'JS'
197
+ ];
198
+ ```
199
+ > Note: Custom logo/js/css can be added together
200
+
201
+ <a name="logging"></a>**No Blocking, Monitor Only**
202
+ Default mode: PxModule::ACTIVE_MODE
203
+
204
+ - PxModule::ACTIVE_MODE - Module blocks users crossing the predefined block threshold. Server-to-server requests are sent synchronously.
205
+
206
+ - 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.
207
+
208
+ ```ruby
209
+ params[:module_mode] = PxModule::MONITOR_MODE
210
+ ```
211
+
212
+ <a name="captcha-support"></a>**Enable/Disable CAPTCHA on the block page**
213
+ Default mode: enabled
214
+
215
+ 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.
216
+
217
+ ```ruby
218
+ params[:captcha_enabled] = false
219
+ ```
220
+
221
+ <a name="custom-uri"></a>**Custom URI**
222
+
223
+ Default: 'REQUEST_URI'
224
+
225
+ The URI can be returned to the PerimeterX module, using a custom user function, defined on the ``params`` variable
226
+
227
+ ```ruby
228
+ params[:custom_uri] = -> (request) {
229
+ return request.headers['HTTP_X_CUSTOM_URI']
230
+ }
231
+ ```
232
+
233
+ <a name="sensitive-headers"></a>**Filter sensitive headers**
234
+ 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.
235
+
236
+ Default: cookie, cookies
237
+
238
+ ```ruby
239
+ params[:sensitive_headers] = ['cookie', 'cookies', 'secret-header']
240
+
241
+ ```
242
+
243
+ <a name="api-timeout"></a>**API Timeouts**
244
+ >Note: Controls the timeouts for PerimeterX requests. The API is called when a Risk Cookie does not exist, or is expired or invalid
245
+
246
+ The API Timeout, in seconds (int), to wait for the PerimeterX server API response.
247
+
248
+ Default: 1
249
+
250
+ ```ruby
251
+ params[:api_timeout] = 4
252
+ ```
253
+
254
+ <a name="send-page-activities"></a>**Send Page Activities**
255
+ Default: true
256
+ 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.
257
+
258
+ ```ruby
259
+ params[:send_page_activities] = false
260
+ ```
261
+
262
+ <a name="additional-page-activity-handler"></a>**Additional Page Activity Handler**
263
+
264
+ 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.
265
+
266
+ Default: Only send activity to PerimeterX as controlled by `params`.
267
+
268
+
269
+
270
+ ```ruby
271
+ params[:additional_activity_handler] = -> (activity_type, px_ctx, details){
272
+ // user defined logic comes here
273
+ };
274
+ ```
275
+
276
+ <a name="debug-mode"></a>**Debug Mode**
277
+ Default: false
278
+
279
+ Enables debug logging mode to STDOUT
280
+ ```ruby
281
+ params[:debug] = true
282
+ ```
283
+
284
+ <a name="contributing"></a># Contributing #
285
+ ------------------------------
286
+ The following steps are welcome when contributing to our project.
287
+ ###Fork/Clone
288
+ First and foremost, [Create a fork](https://guides.github.com/activities/forking/) of the repository, and clone it locally.
289
+ Create a branch on your fork, preferably using a self descriptive branch name.
290
+
291
+ ###Code/Run
292
+ Help improve our project by implementing missing features, adding capabilities or fixing bugs.
293
+
294
+ 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.