perimeter_x 2.2.1 → 2.3.2

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
2
  SHA256:
3
- metadata.gz: 1cb2ccba081bcd941104fd775fc6d047b1b82f9adc6c43619ab860ab1c9685b5
4
- data.tar.gz: 11b5d9a8addcbc817e7e8f30ba5216e3b16c8392088abbc6c72295b53a39720e
3
+ metadata.gz: ea98817074a862f9cf5dca2d01fd56ab31f3376e92fd695cd9a8a86c0713dea6
4
+ data.tar.gz: 302bf6d40f9e0e9a4776153a53fc5315bbd4047ccbc54dca5c082ecc6156900d
5
5
  SHA512:
6
- metadata.gz: 205b0471f18b749671b9f481af16d032c4559b6505e1f6a1198883ea8935f8acf11583433b14ba88a1e51e0eadd004462e49d2167e6e7bd09fe0ddcf4a2afd98
7
- data.tar.gz: 5767dbf74d101d8859b5c74453d9e5ea314721d7bb3489678e0837448bca5eff1210d7fe387d0bae40c5a3ae0de0d305eb55faa4c6d28619e32b29e704c2d0f7
6
+ metadata.gz: 4ab3a9adfcc046a69e9c0c43c7fb603ba62b7bc793919d0560eaeec2fcf52f5521dea5b9665883ecfc9fd667017898aebe73466bebf560af522a141184b13a13
7
+ data.tar.gz: 6a7ee7cd89f58d34edf035fc7762361738922ead805232bc607450df2479bef4d5545a0e55243b65d355ff36442ff9fd6089fbea3abcf626fdd3b54f9ce7c8ac
data/.gitignore CHANGED
@@ -17,6 +17,8 @@ examples/app/views/home/index.html.erb
17
17
  *.gem
18
18
  rerun.txt
19
19
  pickle-email-*.html
20
+ .idea/
21
+ .vscode/
20
22
 
21
23
  # TODO Comment out these rules if you are OK with secrets being uploaded to the repo
22
24
  config/initializers/secret_token.rb
data/Gemfile.lock CHANGED
@@ -1,8 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perimeter_x (2.0.0)
5
- activesupport (>= 5.2.4.3)
4
+ perimeter_x (2.3.1)
6
5
  concurrent-ruby (~> 1.0, >= 1.0.5)
7
6
  mustache (~> 1.0, >= 1.0.3)
8
7
  typhoeus (~> 1.1, >= 1.1.2)
@@ -10,20 +9,11 @@ PATH
10
9
  GEM
11
10
  remote: https://rubygems.org/
12
11
  specs:
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)
12
+ concurrent-ruby (1.1.10)
20
13
  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)
14
+ ethon (0.15.0)
15
+ ffi (>= 1.15.0)
16
+ ffi (1.15.5)
27
17
  mocha (1.11.2)
28
18
  mustache (1.1.1)
29
19
  rake (13.0.1)
@@ -40,12 +30,8 @@ GEM
40
30
  diff-lcs (>= 1.2.0, < 2.0)
41
31
  rspec-support (~> 3.9.0)
42
32
  rspec-support (3.9.3)
43
- thread_safe (0.3.6)
44
33
  typhoeus (1.4.0)
45
34
  ethon (>= 0.9.0)
46
- tzinfo (1.2.7)
47
- thread_safe (~> 0.1)
48
- zeitwerk (2.3.1)
49
35
 
50
36
  PLATFORMS
51
37
  ruby
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2016 PerimeterX, Inc.
1
+ Copyright © 2022 PerimeterX, Inc.
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/changelog.md CHANGED
@@ -5,6 +5,30 @@ 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.3.2] - 2022-xx-xx
9
+
10
+ ### Changed
11
+
12
+ - Removed dependency on Active Support
13
+ - Replaced `eval()` calls with `JSON.parse()` for improved security
14
+ - Small spec alignment changes (risk_api and block activities)
15
+
16
+ ## [2.3.1] - 2022-04-11
17
+
18
+ ### Fixed
19
+
20
+ - URLs with query params did not render properly on new block page
21
+
22
+ ## [2.3.0] - 2022-04-10
23
+
24
+ ### Added
25
+
26
+ - Custom logo in block JSON response
27
+
28
+ ### Changed
29
+
30
+ - Updated block page to use new template
31
+
8
32
  ## [2.2.1] - 2020-09-27
9
33
  ### Fixed
10
34
  - bypass_monitor_header type validation
data/lib/perimeter_x.rb CHANGED
@@ -56,19 +56,22 @@ module PxModule
56
56
  end
57
57
 
58
58
  is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
59
- action = px_ctx.context[:block_action][0,1]
59
+ action = px_ctx.context[:block_action][0,1]
60
+ block_script_uri = "/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}"
60
61
 
61
62
  if px_config[:first_party_enabled]
62
63
  px_template_object = {
63
64
  js_client_src: "/#{px_config[:app_id][2..-1]}/init.js",
64
- block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}",
65
- host_url: "/#{px_config[:app_id][2..-1]}/xhr"
65
+ block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}#{block_script_uri}",
66
+ host_url: "/#{px_config[:app_id][2..-1]}/xhr",
67
+ alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
66
68
  }
67
69
  else
68
70
  px_template_object = {
69
71
  js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
70
- 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}",
71
- host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net"
72
+ block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}",
73
+ host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net",
74
+ alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
72
75
  }
73
76
  end
74
77
 
@@ -92,6 +95,8 @@ module PxModule
92
95
  :vid => px_ctx.context[:vid],
93
96
  :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
94
97
  :blockScript => px_template_object[:block_script],
98
+ :altBlockScript => px_template_object[:alt_block_script],
99
+ :customLogo => px_config[:custom_logo]
95
100
  }
96
101
 
97
102
  render :json => hash_json
@@ -82,8 +82,10 @@ module PxModule
82
82
  # merge request configuration into the basic configuration
83
83
  @configuration = @@basic_config.merge(params)
84
84
  validate_hash_schema(@configuration, CONFIG_SCHEMA)
85
-
86
- @configuration[:backend_url] = "https://sapi-#{@configuration[:app_id].downcase}.perimeterx.net"
85
+
86
+ if (! @configuration.key?(:backend_url))
87
+ @configuration[:backend_url] = "https://sapi-#{@configuration[:app_id].downcase}.perimeterx.net"
88
+ end
87
89
  @configuration[:logger] = PxLogger.new(@configuration[:debug])
88
90
  end
89
91
  end
@@ -63,6 +63,7 @@ module PxModule
63
63
  :client_uuid => px_ctx.context[:uuid],
64
64
  :block_score => px_ctx.context[:score],
65
65
  :block_reason => px_ctx.context[:blocking_reason],
66
+ :block_action => px_ctx.context[:block_action],
66
67
  :simulated_block => @px_config[:module_mode] == PxModule::MONITOR_MODE
67
68
  }
68
69
 
@@ -22,7 +22,7 @@ module PxModule
22
22
  end
23
23
 
24
24
  def valid_format?(cookie)
25
- return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:u) && cookie.key?(:a)
25
+ return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:a)
26
26
  end
27
27
 
28
28
  def cookie_block_action
@@ -1,6 +1,6 @@
1
- require 'active_support/security_utils'
2
1
  require 'base64'
3
2
  require 'openssl'
3
+ require 'json'
4
4
  require 'perimeterx/internal/exceptions/px_cookie_decryption_exception'
5
5
 
6
6
  module PxModule
@@ -123,7 +123,7 @@ module PxModule
123
123
  cipher.iv = iv
124
124
  plaintext = cipher.update(cipher_text) + cipher.final
125
125
 
126
- return eval(plaintext)
126
+ return JSON.parse(plaintext, symbolize_names: true)
127
127
  rescue Exception => e
128
128
  @logger.debug("PerimeterxCookie[decrypt]: Cookie decrypt fail #{e.message}")
129
129
  raise PxCookieDecryptionException.new("Cookie decrypt fail => #{e.message}");
@@ -131,18 +131,26 @@ module PxModule
131
131
  end
132
132
 
133
133
  def decode(px_cookie)
134
- return eval(Base64.decode64(px_cookie))
134
+ return JSON.parse(Base64.decode64(px_cookie), symbolize_names: true)
135
135
  end
136
136
 
137
137
 
138
138
  def hmac_valid?(hmac_str, cookie_hmac)
139
139
  hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @cookie_secret, hmac_str)
140
- # ref: https://thisdata.com/blog/timing-attacks-against-string-comparison/
141
- password_correct = ActiveSupport::SecurityUtils.secure_compare(
142
- ::Digest::SHA256.hexdigest(cookie_hmac),
143
- ::Digest::SHA256.hexdigest(hmac)
144
- )
140
+ password_correct = secure_compare(hmac, cookie_hmac)
141
+ end
142
+
143
+ def secure_compare(a, b)
144
+ # https://github.com/rails/rails/blob/main/activesupport/lib/active_support/security_utils.rb
145
+ if (a.bytesize != b.bytesize)
146
+ return false
147
+ end
148
+
149
+ l = a.unpack "C#{a.bytesize}"
145
150
 
151
+ res = 0
152
+ b.each_byte { |byte| res |= byte ^ l.shift }
153
+ res == 0
146
154
  end
147
155
  end
148
156
  end
@@ -101,17 +101,17 @@ module PxModule
101
101
  @context[:sensitive_route] = check_sensitive_route(px_config[:sensitive_routes], @context[:uri])
102
102
  end #end init
103
103
 
104
- def check_sensitive_route(sensitive_routes, uri)
105
- sensitive_routes.each do |sensitive_route|
106
- return true if uri.start_with? sensitive_route
107
- end
108
- false
109
- end
104
+ def check_sensitive_route(sensitive_routes, uri)
105
+ sensitive_routes.each do |sensitive_route|
106
+ return true if uri.start_with? sensitive_route
107
+ end
108
+ false
109
+ end
110
110
 
111
111
  def set_block_action_type(action)
112
112
  @context[:block_action] = case action
113
113
  when 'c'
114
- 'captcha'
114
+ return 'captcha'
115
115
  when 'b'
116
116
  return 'block'
117
117
  when 'j'
@@ -119,7 +119,7 @@ module PxModule
119
119
  when 'r'
120
120
  return 'rate_limit'
121
121
  else
122
- return captcha
122
+ return 'captcha'
123
123
  end
124
124
  end
125
125
 
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
  require 'perimeterx/internal/clients/perimeter_x_risk_client'
2
3
 
3
4
  module PxModule
@@ -20,7 +21,6 @@ module PxModule
20
21
  :request => {
21
22
  :ip => px_ctx.context[:ip],
22
23
  :headers => format_headers(px_ctx),
23
- :uri => px_ctx.context[:uri],
24
24
  :url => px_ctx.context[:full_url]
25
25
  },
26
26
  :additional => {
@@ -103,7 +103,7 @@ module PxModule
103
103
  px_ctx.context[:made_s2s_risk_api_call] = true
104
104
 
105
105
  # From here response should be valid, if success or error
106
- response_body = eval(response.body);
106
+ response_body = JSON.parse(response.body, symbolize_names: true);
107
107
  # When success
108
108
  if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
109
109
  @logger.debug("PerimeterxS2SValidator[verify]: response ok")
@@ -36,11 +36,11 @@ module PxModule
36
36
  PROP_APP_ID = :appId
37
37
  PROP_VID = :vid
38
38
  PROP_UUID = :uuid
39
- PROP_LOGO_VISIBILITY = :logoVisibility
40
39
  PROP_CUSTOM_LOGO = :customLogo
41
40
  PROP_CSS_REF = :cssRef
42
41
  PROP_JS_REF = :jsRef
43
42
  PROP_BLOCK_SCRIPT = :blockScript
43
+ PROP_ALT_BLOCK_SCRIPT = :altBlockScript
44
44
  PROP_JS_CLIENT_SRC = :jsClientSrc
45
45
  PROP_HOST_URL = :hostUrl
46
46
  PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
@@ -48,6 +48,7 @@ module PxModule
48
48
  # Hosts
49
49
  CLIENT_HOST = 'client.perimeterx.net'
50
50
  CAPTCHA_HOST = 'captcha.px-cdn.net'
51
+ ALT_CAPTCHA_HOST = 'captcha.px-cloud.net'
51
52
 
52
53
  VISIBLE = 'visible'
53
54
  HIDDEN = 'hidden'
@@ -6,7 +6,7 @@ module PxModule
6
6
  def self.get_template(px_ctx, px_config, px_template_object)
7
7
  logger = px_config[:logger]
8
8
  if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
9
- logger.debug('PxTemplateFactory[get_template]: px challange triggered')
9
+ logger.debug('PxTemplateFactory[get_template]: px challenge triggered')
10
10
  return px_ctx.context[:block_action_data].html_safe
11
11
  end
12
12
 
@@ -23,15 +23,14 @@ module PxModule
23
23
  Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{PxModule::TEMPLATE_EXT}"
24
24
 
25
25
  view[PxModule::PROP_APP_ID] = px_config[:app_id]
26
- view[PxModule::PROP_REF_ID] = px_ctx.context[:uuid]
27
26
  view[PxModule::PROP_VID] = px_ctx.context[:vid]
28
27
  view[PxModule::PROP_UUID] = px_ctx.context[:uuid]
29
28
  view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
30
29
  view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
31
30
  view[PxModule::PROP_JS_REF] = px_config[:js_ref]
32
31
  view[PxModule::PROP_HOST_URL] = px_template_object[:host_url]
33
- view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
34
32
  view[PxModule::PROP_BLOCK_SCRIPT] = px_template_object[:block_script]
33
+ view[PxModule::PROP_ALT_BLOCK_SCRIPT] = px_template_object[:alt_block_script]
35
34
  view[PxModule::PROP_JS_CLIENT_SRC] = px_template_object[:js_client_src]
36
35
  view[PxModule::PROP_FIRST_PARTY_ENABLED] = px_ctx.context[:first_party_enabled]
37
36
 
@@ -3,173 +3,42 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
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 -->
6
+ <meta name="description" content="px-captcha">
7
+ <title>Access to this page has been denied</title>
89
8
  {{#cssRef}}
90
9
  <link rel="stylesheet" type="text/css" href="{{{cssRef}}}"/>
91
10
  {{/cssRef}}
92
11
  </head>
93
-
94
12
  <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);
13
+ <script>
14
+ window._pxVid = '{{vid}}';
15
+ window._pxUuid = '{{uuid}}';
16
+ window._pxAppId = '{{appId}}';
17
+ window._pxCustomLogo = '{{{customLogo}}}';
18
+ window._pxHostUrl = '{{{hostUrl}}}';
19
+ window._pxJsClientSrc = '{{{jsClientSrc}}}';
20
+ window._pxFirstPartyEnabled = {{firstPartyEnabled}};
21
+ var script = document.createElement('script');
22
+ script.src = '{{{blockScript}}}';
23
+ document.head.appendChild(script);
24
+ script.onerror = function () {
25
+ script = document.createElement('script');
26
+ script.src = '{{{altBlockScript}}}';
27
+ script.onerror = window._pxDisplayErrorMessage;
28
+ document.head.appendChild(script);
166
29
  };
167
- }
168
- </script>
169
-
170
- <!-- Custom Script -->
171
- {{#jsRef}}
172
- <script src="{{{jsRef}}}"></script>
173
- {{/jsRef}}
30
+ window._pxDisplayErrorMessage = function () {
31
+ var style = document.createElement('style');
32
+ style.innerText = '@import url(https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap);body{background-color:#fafbfc}@media (max-width:480px){body{background-color:#fff}}.px-captcha-error-container{position:fixed;height:328px;background-color:#fff;font-family:Roboto,sans-serif}.px-captcha-error-header{color:#f0f1f2;font-size:29px;margin:67px 0 33px;font-weight:500;line-height:.83;text-align:center}.px-captcha-error-message{color:#f0f1f2;font-size:18px;margin:0 0 29px;line-height:1.33;text-align:center}div.px-captcha-error-button{text-align:center;line-height:50px;width:253px;margin:auto;border-radius:25px;border:solid 1px #f0f1f2;font-size:20px;color:#f0f1f2}div.px-captcha-error-wrapper{margin:23px 0 0}div.px-captcha-error{margin:auto;text-align:center;width:400px;height:30px;font-size:12px;background-color:#fcf0f2;color:#ce0e2d}img.px-captcha-error{margin:6px 10px -2px 0}@media (min-width:620px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (min-width:481px) and (max-width:620px){.px-captcha-error-container{width:85%;top:50%;left:50%;margin-top:-164px;margin-left:-42.5%;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (max-width:480px){.px-captcha-error-container{width:528px;top:50%;left:50%;margin-top:-164px;margin-left:-264px}}';
33
+ document.head.appendChild(style);
34
+ var div = document.createElement('div');
35
+ div.className = 'px-captcha-error-container';
36
+ div.innerHTML = '<div class="px-captcha-error-header">Before we continue...</div><div class="px-captcha-error-message">Press & Hold to confirm you are<br>a human (and not a bot).</div><div class="px-captcha-error-button">Press & Hold</div><div class="px-captcha-error-wrapper"><div class="px-captcha-error"><img class="px-captcha-error" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAQCAMAAADDGrRQAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAABFUExURUdwTNYELOEGONQILd0AONwALtwEL+AAL9MFLfkJSNQGLdMJLdQJLdQGLdQKLtYFLNcELdUGLdcBL9gFL88OLdUFLNEOLglBhT4AAAAXdFJOUwC8CqgNIRgRoAS1dWWuR4RTjzgryZpYblfkcAAAAI9JREFUGNNdj+sWhCAIhAdvqGVa1r7/oy6RZ7eaH3D4ZACBIed9wlOOMtUnSrEmZ6cHa9YAIfsbCkWrdpi/c50Bk2CO9mNLdMAu03wJA3HpEnfpxbyOg6ruyx8JJi6KNstnslp1dbPd9GnqmuYq7mmcv1zjnbQw8cV0xzkqo+fX1zkjUOO7wnrInUTxJiruC3vtBNRoQQn2AAAAAElFTkSuQmCC">Please check your network connection or disable your ad-blocker.</div></div>';
37
+ document.body.appendChild(div);
38
+ };
39
+ </script>
40
+ {{#jsRef}}
41
+ <script src="{{{jsRef}}}"></script>
42
+ {{/jsRef}}
174
43
  </body>
175
- </html>
44
+ </html>
@@ -1,3 +1,3 @@
1
1
  module PxModule
2
- VERSION = '2.2.1'
2
+ VERSION = '2.3.2'
3
3
  end
data/perimeter_x.gemspec CHANGED
@@ -33,7 +33,6 @@ Gem::Specification.new do |gem|
33
33
  gem.add_dependency('concurrent-ruby', '~> 1.0', '>= 1.0.5')
34
34
  gem.add_dependency('typhoeus', '~> 1.1', '>= 1.1.2')
35
35
  gem.add_dependency('mustache', '~> 1.0', '>= 1.0.3')
36
- gem.add_dependency('activesupport', '>= 5.2.4.3')
37
36
 
38
37
  gem.add_development_dependency 'rspec', '~> 3.0'
39
38
  gem.add_development_dependency 'mocha', '~> 1.2', '>= 1.2.1'
data/px_metadata.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "version": "2.3.2",
3
+ "supported_features": [
4
+ "additional_activity_handler",
5
+ "advanced_blocking_response",
6
+ "batched_activities",
7
+ "block_activity",
8
+ "block_page_captcha",
9
+ "block_page_rate_limit",
10
+ "bypass_monitor_header",
11
+ "client_ip_extraction",
12
+ "cookie_v3",
13
+ "css_ref",
14
+ "custom_logo",
15
+ "logger",
16
+ "filter_by_route",
17
+ "first_party",
18
+ "js_ref",
19
+ "mobile_support",
20
+ "module_enable",
21
+ "module_mode",
22
+ "page_requested_activity",
23
+ "vid_extraction",
24
+ "risk_api",
25
+ "sensitive_headers",
26
+ "sensitive_routes"
27
+ ]
28
+ }
data/readme.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [PerimeterX](http://www.perimeterx.com) Ruby SDK
6
6
  =============================================================
7
7
 
8
- > Latest stable version: [v2.2.1](https://rubygems.org/gems/perimeter_x)
8
+ > Latest stable version: [v2.3.2](https://rubygems.org/gems/perimeter_x)
9
9
 
10
10
  Table of Contents
11
11
  -----------------
@@ -31,6 +31,7 @@ Table of Contents
31
31
  * [Update Configuration on Runtime](#update-config)
32
32
  * [First Party](#first-party)
33
33
 
34
+ **[Additional Information](#additional-information)**
34
35
  **[Contributing](#contributing)**
35
36
 
36
37
  <a name="Usage"></a>
@@ -160,14 +161,15 @@ params = {
160
161
 
161
162
  > 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.
162
163
 
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
164
+ 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.
164
165
 
165
166
  ***Custom header***
167
+ > Note: If there are multiple headers configured, the module will evaluate the headers in order and use the first value it finds.
166
168
  ```ruby
167
169
  configuration = {
168
170
  "app_id" => <APP_ID>,
169
171
  "auth_token" => <AUTH_TOKEN>,
170
- "custom_user_ip" => <HTTP_HEADER_NAME>,
172
+ "ip_headers" => [<HTTP_HEADER_NAME>, <BACKUP_HTTP_HEADER_NAME>],
171
173
  ```
172
174
 
173
175
  ***Custom Function***
@@ -177,7 +179,7 @@ configuration = {
177
179
  configuration = {
178
180
  "app_id" => <APP_ID>,
179
181
  "auth_token" => <AUTH_TOKEN>,
180
- "custom_user_ip_method" => -> (req) {
182
+ "ip_header_function" => -> (req) {
181
183
  # Method body
182
184
  return "1.2.3.4"
183
185
  }
@@ -213,14 +215,14 @@ params = [
213
215
  > Note: Custom logo/js/css can be added together
214
216
 
215
217
  <a name="logging"></a>**No Blocking, Monitor Only**
216
- Default mode: PxModule::ACTIVE_MODE
218
+ Default mode: PxModule::MONITOR_MODE
217
219
 
218
220
  - PxModule::ACTIVE_MODE - Module blocks users crossing the predefined block threshold. Server-to-server requests are sent synchronously.
219
221
 
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.
222
+ - 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
223
 
222
224
  ```ruby
223
- params[:module_mode] = PxModule::MONITOR_MODE
225
+ params[:module_mode] = PxModule::ACTIVE_MODE
224
226
  ```
225
227
 
226
228
  <a name="custom-uri"></a>**Custom URI**
@@ -350,8 +352,12 @@ Default: true
350
352
  params[:first_party_enabled] = false
351
353
  ```
352
354
 
355
+ <a name="additional_information"></a> Additional Information
356
+ ------------------------------
357
+ ### URI Delimiters
358
+ PerimeterX processes URI paths with general- and sub-delimiters according to RFC 3986. General delimiters (e.g., `?`, `#`) are used to separate parts of the URI. Sub-delimiters (e.g., `$`, `&`) are not used to split the URI as they are considered valid characters in the URI path.
353
359
 
354
- <a name="contributing"></a># Contributing #
360
+ <a name="contributing"></a> Contributing
355
361
  ------------------------------
356
362
  The following steps are welcome when contributing to our project.
357
363
  ###Fork/Clone
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: perimeter_x
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nitzan Goldfeder
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-27 00:00:00.000000000 Z
11
+ date: 2022-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -98,20 +98,6 @@ dependencies:
98
98
  - - ">="
99
99
  - !ruby/object:Gem::Version
100
100
  version: 1.0.3
101
- - !ruby/object:Gem::Dependency
102
- name: activesupport
103
- requirement: !ruby/object:Gem::Requirement
104
- requirements:
105
- - - ">="
106
- - !ruby/object:Gem::Version
107
- version: 5.2.4.3
108
- type: :runtime
109
- prerelease: false
110
- version_requirements: !ruby/object:Gem::Requirement
111
- requirements:
112
- - - ">="
113
- - !ruby/object:Gem::Version
114
- version: 5.2.4.3
115
101
  - !ruby/object:Gem::Dependency
116
102
  name: rspec
117
103
  requirement: !ruby/object:Gem::Requirement
@@ -191,6 +177,7 @@ files:
191
177
  - lib/perimeterx/utils/templates/ratelimit.mustache
192
178
  - lib/perimeterx/version.rb
193
179
  - perimeter_x.gemspec
180
+ - px_metadata.json
194
181
  - readme.md
195
182
  homepage: https://www.perimeterx.com
196
183
  licenses:
@@ -215,7 +202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
215
202
  - !ruby/object:Gem::Version
216
203
  version: '0'
217
204
  requirements: []
218
- rubygems_version: 3.0.3
205
+ rubygems_version: 3.0.3.1
219
206
  signing_key:
220
207
  specification_version: 4
221
208
  summary: PerimeterX ruby implmentation