perimeter_x 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 52e3418c30763e225706ca5ee7a3a88958c4a210
4
- data.tar.gz: 4671a1eb52edc4cfdb9ba4e85932c2dcb7e33dbb
3
+ metadata.gz: 1f3a1b696f592b2f1478cc376a96475a5d5f0778
4
+ data.tar.gz: e859865af132477c953d6aed1a7c70bb927b432e
5
5
  SHA512:
6
- metadata.gz: 13ca73c6ac3c22ff0d9d93d42cab42049a06e458f9596538cd2225a07dd1ea9e20db8bf4c6921519ed29c349e717eb929643051ef1510337b1414bf6ea8f6c01
7
- data.tar.gz: 2bc1a8fe0845f3899537bb34151c545ea7c6a5343b271c7bceb4d221a9551d74dec174042e30b02e7e29d533d7e0453235163ee0635d2275576c75ed4758efd5
6
+ metadata.gz: 1f67c50466d0a347d7d5e3dc6c30f66332a3dabb27a34152e49c36a6614fa06ce81bc02d8f50ba2e061f9fb4cef8b21e9fb684d2d2b8b138ffab87298d0e1f81
7
+ data.tar.gz: 35d39968545b296e04f02bed0bb2c67b40ca1ff6773e352533fbe06e2795ad630a838d159fe517a12702e79d5054d81ef828fa966e9278dec7fd6eaa37dd2e06
data/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
1
  *.rbc
2
+ *.iml
2
3
  capybara-*.html
3
4
  .rspec
4
5
  /log
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3
@@ -1,10 +1,11 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- perimeter_x (1.0.5)
4
+ perimeter_x (1.3.0)
5
5
  activesupport (>= 4.2.0)
6
- httpclient (= 2.8.2.4)
6
+ concurrent-ruby (~> 1.0, >= 1.0.5)
7
7
  mustache (~> 1.0, >= 1.0.3)
8
+ typhoeus (~> 1.1, >= 1.1.2)
8
9
 
9
10
  GEM
10
11
  remote: https://rubygems.org/
@@ -16,13 +17,15 @@ GEM
16
17
  tzinfo (~> 1.1)
17
18
  concurrent-ruby (1.0.5)
18
19
  diff-lcs (1.3)
19
- httpclient (2.8.2.4)
20
- i18n (0.8.1)
20
+ ethon (0.10.1)
21
+ ffi (>= 1.3.0)
22
+ ffi (1.9.18)
23
+ i18n (0.8.6)
21
24
  metaclass (0.0.4)
22
25
  minitest (5.10.1)
23
26
  mocha (1.2.1)
24
27
  metaclass (~> 0.0.1)
25
- mustache (1.0.4)
28
+ mustache (1.0.5)
26
29
  rake (10.4.2)
27
30
  rspec (3.5.0)
28
31
  rspec-core (~> 3.5.0)
@@ -38,6 +41,8 @@ GEM
38
41
  rspec-support (~> 3.5.0)
39
42
  rspec-support (3.5.0)
40
43
  thread_safe (0.3.6)
44
+ typhoeus (1.1.2)
45
+ ethon (>= 0.9.0)
41
46
  tzinfo (1.2.3)
42
47
  thread_safe (~> 0.1)
43
48
 
data/Rakefile CHANGED
@@ -3,6 +3,7 @@ begin
3
3
 
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
+ task :default => :spec
6
7
  task :test => :spec
7
8
  rescue LoadError
8
9
  # no rspec available
@@ -5,6 +5,13 @@ 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
+ ## [1.3.0] - 2017-06-04
9
+ ### Added
10
+ - Sending client_uuid on page_requested activities
11
+ - Supporting mobile sdk
12
+ ### Fixed
13
+ - Using `request.env` instead of `env`
14
+
8
15
  ## [1.2.0] - 2017-06-04
9
16
  ### Fixed
10
17
  - Default timeouts for post api requests
@@ -27,3 +34,4 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
27
34
  - Constants on px_constants
28
35
  - Cookie Validation flow when cookie score was over the configured threshold
29
36
  - Using symbols instead of strings for requests body
37
+
@@ -1,7 +1,7 @@
1
1
  class HomeController < ApplicationController
2
2
  include PxModule
3
3
 
4
- before_filter :px_verify_request
4
+ before_action :px_verify_request
5
5
 
6
6
  def index
7
7
  end
@@ -1,3 +1,6 @@
1
+ require 'concurrent'
2
+ require 'json'
3
+ require 'base64'
1
4
  require 'perimeterx/configuration'
2
5
  require 'perimeterx/utils/px_logger'
3
6
  require 'perimeterx/utils/px_constants'
@@ -10,29 +13,46 @@ require 'perimeterx/internal/validators/perimeter_x_cookie_validator'
10
13
  require 'perimeterx/internal/validators/perimeter_x_captcha_validator'
11
14
 
12
15
  module PxModule
13
-
14
16
  # Module expose API
15
17
  def px_verify_request
16
- verified, px_ctx = PerimeterX.instance.verify(env)
18
+ verified, px_ctx = PerimeterX.instance.verify(request.env)
17
19
 
18
20
  # Invalidate _pxCaptcha, can be done only on the controller level
19
- cookies[:_pxCaptcha] = { value: "", expires: -1.minutes.from_now }
21
+ cookies[:_pxCaptcha] = {value: "", expires: -1.minutes.from_now}
20
22
 
21
- if (!verified)
23
+ unless verified
22
24
  # In case custon block handler exists
23
25
  if (PerimeterX.instance.px_config.key?(:custom_block_handler))
24
- PerimeterX.instance.px_config[:logger].debug("PxModule[px_verify_request]: custom_block_handler triggered")
26
+ PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: custom_block_handler triggered')
25
27
  return instance_exec(px_ctx, &PerimeterX.instance.px_config[:custom_block_handler])
26
28
  else
27
29
  # Generate template
28
- PerimeterX.instance.px_config[:logger].debug("PxModule[px_verify_request]: sending default block page")
30
+ PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: sending default block page')
29
31
  html = PxTemplateFactory.get_template(px_ctx, PerimeterX.instance.px_config)
30
- response.headers["Content-Type"] = "text/html"
32
+ response.headers['Content-Type'] = 'text/html'
31
33
  response.status = 403
32
- render :html => html
34
+ # Web handler
35
+ if px_ctx.context[:cookie_origin] == 'cookie'
36
+ PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: web block')
37
+ response.headers['Content-Type'] = 'text/html'
38
+ render :html => html
39
+ else # Mobile SDK
40
+ PerimeterX.instance.px_config[:logger].debug('PxModule[px_verify_request]: mobile sdk block')
41
+ response.headers['Content-Type'] = 'application/json'
42
+ hash_json = {
43
+ :action => px_ctx.context[:block_action],
44
+ :uuid => px_ctx.context[:uuid],
45
+ :vid => px_ctx.context[:vid],
46
+ :appId => PerimeterX.instance.px_config[:app_id],
47
+ :page => Base64.strict_encode64(html),
48
+ :collectorUrl => "https://collector-#{PerimeterX.instance.px_config[:app_id]}.perimeterx.net"
49
+ }
50
+ render :json => hash_json
51
+ end
33
52
  end
34
53
  end
35
54
 
55
+ # Request was verified
36
56
  return verified
37
57
  end
38
58
 
@@ -41,7 +61,7 @@ module PxModule
41
61
  end
42
62
 
43
63
 
44
- # PerimtereX Module
64
+ # PerimeterX Module
45
65
  class PerimeterX
46
66
  @@__instance = nil
47
67
  @@mutex = Mutex.new
@@ -62,19 +82,19 @@ module PxModule
62
82
 
63
83
  def self.instance
64
84
  return @@__instance if !@@__instance.nil?
65
- raise Exception.new("Please initialize perimeter x first")
85
+ raise Exception.new('Please initialize perimeter x first')
66
86
  end
67
87
 
68
88
 
69
89
  #Instance Methods
70
90
  def verify(env)
71
91
  begin
72
- @logger.debug("PerimeterX[pxVerify]")
73
- req = ActionDispatch::Request.new(env)
92
+ @logger.debug('PerimeterX[pxVerify]')
74
93
  if (!@px_config[:module_enabled])
75
- @logger.warn("Module is disabled")
94
+ @logger.warn('Module is disabled')
76
95
  return true
77
96
  end
97
+ req = ActionDispatch::Request.new(env)
78
98
  px_ctx = PerimeterXContext.new(@px_config, req)
79
99
 
80
100
  # Captcha phase
@@ -96,7 +116,7 @@ module PxModule
96
116
  end
97
117
  rescue Exception => e
98
118
  @logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
99
- e.backtrace.drop(1).map { |s| @logger.error("\t#{s}") }
119
+ e.backtrace.drop(1).map {|s| @logger.error("\t#{s}")}
100
120
  return true
101
121
  end
102
122
  end
@@ -111,11 +131,11 @@ module PxModule
111
131
  @px_cookie_validator = PerimeterxCookieValidator.new(@px_config)
112
132
  @px_s2s_validator = PerimeterxS2SValidator.new(@px_config, @px_http_client)
113
133
  @px_captcha_validator = PerimeterxCaptchaValidator.new(@px_config, @px_http_client)
114
- @logger.debug("PerimeterX[initialize]")
134
+ @logger.debug('PerimeterX[initialize]Z')
115
135
  end
116
136
 
117
137
  private def handle_verification(px_ctx)
118
- @logger.debug("PerimeterX[handle_verification]")
138
+ @logger.debug('PerimeterX[handle_verification]')
119
139
  @logger.debug("PerimeterX[handle_verification]: processing ended - score:#{px_ctx.context[:score]}, uuid:#{px_ctx.context[:uuid]}")
120
140
 
121
141
  score = px_ctx.context[:score]
@@ -130,12 +150,12 @@ module PxModule
130
150
  @px_activity_client.send_block_activity(px_ctx)
131
151
 
132
152
  # In case were in monitor mode, end here
133
- if(@px_config[:module_mode] == PxModule::MONITOR_MODE)
134
- @logger.debug("PerimeterX[handle_verification]: monitor mode is on, passing request")
153
+ if (@px_config[:module_mode] == PxModule::MONITOR_MODE)
154
+ @logger.debug('PerimeterX[handle_verification]: monitor mode is on, passing request')
135
155
  return true
136
156
  end
137
157
 
138
- @logger.debug("PerimeterX[handle_verification]: verification ended, the request should be blocked")
158
+ @logger.debug('PerimeterX[handle_verification]: verification ended, the request should be blocked')
139
159
 
140
160
  return false, px_ctx
141
161
  end
@@ -20,7 +20,7 @@ module PxModule
20
20
  :api_connect_timeout => 1,
21
21
  :api_timeout => 1,
22
22
  :max_buffer_len => 10,
23
- :send_page_activities => false,
23
+ :send_page_activities => true,
24
24
  :send_block_activities => true,
25
25
  :sdk_name => PxModule::SDK_NAME,
26
26
  :debug => false,
@@ -31,7 +31,7 @@ module PxModule
31
31
 
32
32
  def initialize(params)
33
33
  PX_DEFAULT[:perimeterx_server_host] = "https://sapi-#{params[:app_id].downcase}.perimeterx.net"
34
- @configuration = PX_DEFAULT.merge(params);
34
+ @configuration = PX_DEFAULT.merge(params)
35
35
  @configuration[:logger] = PxLogger.new(@configuration[:debug])
36
36
  end
37
37
  end
@@ -18,6 +18,8 @@ module PxModule
18
18
  end
19
19
 
20
20
  details[:module_version] = @px_config[:sdk_name]
21
+ details[:cookie_origin] = px_ctx.context[:cookie_origin]
22
+
21
23
  px_data = {
22
24
  :type => activity_type,
23
25
  :headers => format_headers(px_ctx),
@@ -53,9 +55,9 @@ module PxModule
53
55
  end
54
56
 
55
57
  details = {
56
- :block_uuid => px_ctx.context[:uuid],
57
- :block_score => px_ctx.context[:score],
58
- :block_reason => px_ctx.context[:block_reason]
58
+ :block_uuid => px_ctx.context[:uuid],
59
+ :block_score => px_ctx.context[:score],
60
+ :block_reason => px_ctx.context[:blocking_reason]
59
61
  }
60
62
 
61
63
  send_to_perimeterx(PxModule::BLOCK_ACTIVITY, px_ctx, details)
@@ -70,7 +72,8 @@ module PxModule
70
72
 
71
73
  details = {
72
74
  :http_version => px_ctx.context[:http_version],
73
- :http_method => px_ctx.context[:http_method]
75
+ :http_method => px_ctx.context[:http_method],
76
+ :client_uuid => px_ctx.context[:uuid]
74
77
  }
75
78
 
76
79
  if (px_ctx.context.key?(:decoded_cookie))
@@ -1,5 +1,5 @@
1
1
  module PxModule
2
- class PerimeterxCookieV1 < PerimeterxCookie
2
+ class PerimeterxCookieV1 < PerimeterxPayload
3
3
 
4
4
  attr_accessor :px_config, :px_ctx
5
5
 
@@ -1,5 +1,5 @@
1
1
  module PxModule
2
- class PerimeterxCookieV3 < PerimeterxCookie
2
+ class PerimeterxCookieV3 < PerimeterxPayload
3
3
 
4
4
  attr_accessor :px_config, :px_ctx, :cookie_hash
5
5
 
@@ -4,7 +4,7 @@ require 'openssl'
4
4
  require 'perimeterx/internal/exceptions/px_cookie_decryption_exception'
5
5
 
6
6
  module PxModule
7
- class PerimeterxCookie
7
+ class PerimeterxPayload
8
8
  attr_accessor :px_cookie, :px_config, :px_ctx, :cookie_secret, :decoded_cookie
9
9
 
10
10
  def initialize(px_config)
@@ -13,7 +13,12 @@ module PxModule
13
13
  end
14
14
 
15
15
  def self.px_cookie_factory(px_ctx, px_config)
16
- if (px_ctx.context[:px_cookie].key?(:v3))
16
+ if px_ctx.context[:cookie_origin] == 'header'
17
+ if (px_ctx.context[:px_cookie].key?(:v3))
18
+ return PerimeterxTokenV3.new(px_config,px_ctx)
19
+ end
20
+ return PerimeterxTokenV1.new(px_config,px_ctx)
21
+ elsif (px_ctx.context[:px_cookie].key?(:v3))
17
22
  return PerimeterxCookieV3.new(px_config, px_ctx)
18
23
  end
19
24
  return PerimeterxCookieV1.new(px_config, px_ctx)
@@ -131,8 +136,8 @@ module PxModule
131
136
  hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA256.new, @cookie_secret, hmac_str)
132
137
  # ref: https://thisdata.com/blog/timing-attacks-against-string-comparison/
133
138
  password_correct = ActiveSupport::SecurityUtils.secure_compare(
134
- ::Digest::SHA256.hexdigest(cookie_hmac),
135
- ::Digest::SHA256.hexdigest(hmac)
139
+ ::Digest::SHA256.hexdigest(cookie_hmac),
140
+ ::Digest::SHA256.hexdigest(hmac)
136
141
  )
137
142
 
138
143
  end
@@ -0,0 +1,38 @@
1
+ module PxModule
2
+ class PerimeterxTokenV1 < PerimeterxPayload
3
+
4
+ attr_accessor :px_config, :px_ctx
5
+
6
+ def initialize(px_config, px_ctx)
7
+ super(px_config)
8
+ @px_ctx = px_ctx
9
+ @px_cookie = px_ctx.get_px_cookie
10
+ @cookie_secret = px_config[:cookie_key]
11
+ @logger.debug('PerimeterxTokenV1[initialize]')
12
+ end
13
+
14
+ def cookie_score
15
+ return @decoded_cookie[:s][:b]
16
+ end
17
+
18
+ def cookie_hmac
19
+ return @decoded_cookie[:h]
20
+ end
21
+
22
+ def valid_format?(cookie)
23
+ return cookie.key?(:t) && cookie.key?(:s) && cookie[:s].key?(:b) && cookie.key?(:s) && cookie.key?(:v) && cookie.key?(:h)
24
+ end
25
+
26
+ def cookie_block_action
27
+ return 'c'
28
+ end
29
+
30
+ def secured?
31
+ hmac_str = "#{cookie_time}#{@decoded_cookie[:s][:a]}#{cookie_score}#{cookie_uuid}#{cookie_vid}"
32
+
33
+ return hmac_valid?(hmac_str, cookie_hmac)
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,36 @@
1
+ module PxModule
2
+ class PerimeterxTokenV3 < PerimeterxPayload
3
+
4
+ attr_accessor :px_config, :px_ctx, :cookie_hash
5
+
6
+ def initialize(px_config, px_ctx)
7
+ super(px_config)
8
+ hash, cookie = px_ctx.get_px_cookie().split(':', 2)
9
+ @px_cookie = cookie
10
+ @cookie_hash = hash
11
+ @px_ctx = px_ctx
12
+ @cookie_secret = px_config[:cookie_key]
13
+ @logger.debug('PerimeterxTokenV3[initialize]')
14
+ end
15
+
16
+ def cookie_score
17
+ return @decoded_cookie[:s]
18
+ end
19
+
20
+ def cookie_hmac
21
+ return @cookie_hash
22
+ end
23
+
24
+ def valid_format?(cookie)
25
+ return cookie.key?(:t) && cookie.key?(:s) && cookie.key?(:u) && cookie.key?(:u) && cookie.key?(:a)
26
+ end
27
+
28
+ def cookie_block_action
29
+ @decoded_cookie[:a]
30
+ end
31
+
32
+ def secured?
33
+ return hmac_valid?(@px_cookie, cookie_hmac)
34
+ end
35
+ end
36
+ end
@@ -7,31 +7,42 @@ module PxModule
7
7
  attr_accessor :px_config
8
8
 
9
9
  def initialize(px_config, req)
10
- @logger = px_config[:logger];
11
- @logger.debug("PerimeterXContext[initialize] ")
10
+ @logger = px_config[:logger]
11
+ @logger.debug('PerimeterXContext[initialize]')
12
12
  @context = Hash.new
13
13
 
14
14
  @context[:px_cookie] = Hash.new
15
15
  @context[:headers] = Hash.new
16
+ @context[:cookie_origin] = 'cookie'
16
17
  cookies = req.cookies
17
- if (!cookies.empty?)
18
+
19
+ # Get token from header
20
+ if req.headers[PxModule::TOKEN_HEADER]
21
+ @context[:cookie_origin] = 'header'
22
+ token = req.headers[PxModule::TOKEN_HEADER]
23
+ if token.include? ':'
24
+ exploded_token = token.split(':', 2)
25
+ cookie_sym = "v#{exploded_token[0]}".to_sym
26
+ @context[:px_cookie][cookie_sym] = exploded_token[1]
27
+ end
28
+ elsif !cookies.empty? # Get cookie from jar
18
29
  # Prepare hashed cookies
19
30
  cookies.each do |k, v|
20
31
  case k.to_s
21
- when "_px3"
32
+ when '_px3'
22
33
  @context[:px_cookie][:v3] = v
23
- when "_px"
34
+ when '_px'
24
35
  @context[:px_cookie][:v1] = v
25
- when "_pxCaptcha"
36
+ when '_pxCaptcha'
26
37
  @context[:px_captcha] = v
27
38
  end
28
39
  end #end case
29
40
  end #end empty cookies
30
41
 
31
42
  req.headers.each do |k, v|
32
- if (k.start_with? "HTTP_")
33
- header = k.to_s.gsub("HTTP_", "")
34
- header = header.gsub("_", "-").downcase
43
+ if (k.start_with? 'HTTP_')
44
+ header = k.to_s.gsub('HTTP_', '')
45
+ header = header.gsub('_', '-').downcase
35
46
  @context[:headers][header.to_sym] = v
36
47
  end
37
48
  end #end headers foreach
@@ -52,9 +63,9 @@ module PxModule
52
63
  end
53
64
 
54
65
  if req.server_protocol
55
- httpVer = req.server_protocol.split("/")
66
+ httpVer = req.server_protocol.split('/')
56
67
  if httpVer.size > 0
57
- @context[:http_version] = httpVer[1];
68
+ @context[:http_version] = httpVer[1]
58
69
  end
59
70
  end
60
71
  @context[:http_method] = req.method
@@ -65,19 +76,19 @@ module PxModule
65
76
  sensitive_routes.each do |sensitive_route|
66
77
  return true if uri.start_with? sensitive_route
67
78
  end
68
- return false
79
+ false
69
80
  end
70
81
 
71
82
  def set_block_action_type(action)
72
83
  @context[:block_action] = case action
73
- when "c"
74
- "captcha"
75
- when "b"
76
- return "block"
77
- when "j"
78
- return "challenge"
84
+ when 'c'
85
+ 'captcha'
86
+ when 'b'
87
+ return 'block'
88
+ when 'j'
89
+ return 'challenge'
79
90
  else
80
- return "captcha"
91
+ return captcha
81
92
  end
82
93
  end
83
94
 
@@ -48,7 +48,7 @@ module PxModule
48
48
 
49
49
  if (response.status_code == 200)
50
50
  response_body = eval(response.body)
51
- if ( response_body[:status] == 0 )
51
+ if ( response_body[:code] == 0 )
52
52
  captcha_validated = true
53
53
  end
54
54
  end
@@ -1,7 +1,9 @@
1
1
  require 'perimeterx/utils/px_constants'
2
- require 'perimeterx/internal/perimeter_x_cookie'
3
- require 'perimeterx/internal/perimeter_x_cookie_v1'
4
- require 'perimeterx/internal/perimeter_x_cookie_v3'
2
+ require 'perimeterx/internal/payload/perimeter_x_payload'
3
+ require 'perimeterx/internal/payload/perimeter_x_token_v1'
4
+ require 'perimeterx/internal/payload/perimeter_x_token_v3'
5
+ require 'perimeterx/internal/payload/perimeter_x_cookie_v1'
6
+ require 'perimeterx/internal/payload/perimeter_x_cookie_v3'
5
7
 
6
8
  module PxModule
7
9
  class PerimeterxCookieValidator
@@ -24,7 +26,7 @@ module PxModule
24
26
  end
25
27
 
26
28
  # Deserialize cookie start
27
- cookie = PerimeterxCookie.px_cookie_factory(px_ctx, @px_config)
29
+ cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
28
30
  if (!cookie.deserialize())
29
31
  @logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
30
32
  px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
@@ -46,6 +48,7 @@ module PxModule
46
48
  if (cookie.high_score?)
47
49
  @logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
48
50
  px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_HIGH_SCORE
51
+ px_ctx.context[:blocking_reason] = 'cookie_high_score'
49
52
  return true, px_ctx
50
53
  end
51
54
 
@@ -54,12 +57,12 @@ module PxModule
54
57
  px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_VALIDATION_FAILED
55
58
  return false, px_ctx
56
59
  end
57
-
58
- if (px_ctx.context[:sensitive_route])
59
- @logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
60
- px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
61
- return false, px_ctx
62
- end
60
+
61
+ if (px_ctx.context[:sensitive_route])
62
+ @logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
63
+ px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
64
+ return false, px_ctx
65
+ end
63
66
 
64
67
  @logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
65
68
 
@@ -5,11 +5,11 @@ module PxModule
5
5
 
6
6
  def initialize(px_config, http_client)
7
7
  super(px_config, http_client)
8
- @logger.debug("PerimeterxS2SValidator[initialize]")
8
+ @logger.debug('PerimeterxS2SValidator[initialize]')
9
9
  end
10
10
 
11
11
  def send_risk_request(px_ctx)
12
- @logger.debug("PerimeterxS2SValidator[send_risk_request]: send_risk_request")
12
+ @logger.debug('PerimeterxS2SValidator[send_risk_request]: send_risk_request')
13
13
 
14
14
  risk_mode = PxModule::RISK_MODE_ACTIVE
15
15
  if @px_config[:module_mode] == PxModule::MONITOR_MODE
@@ -26,6 +26,7 @@ module PxModule
26
26
  :additional => {
27
27
  :s2s_call_reason => px_ctx.context[:s2s_call_reason],
28
28
  :module_version => @px_config[:sdk_name],
29
+ :cookie_origin => px_ctx.context[:cookie_origin],
29
30
  :http_method => px_ctx.context[:http_method],
30
31
  :http_version => px_ctx.context[:http_version],
31
32
  :risk_mode => risk_mode
@@ -103,7 +104,7 @@ module PxModule
103
104
  if(response.code != 200)
104
105
  @logger.warn("PerimeterxS2SValidator[verify]: bad response, return code #{response.code}")
105
106
  px_ctx.context[:uuid] = ""
106
- px_ctx.context[:s2s_error_msg] = response_body[:message]
107
+ px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
107
108
  end
108
109
 
109
110
  @logger.debug("PerimeterxS2SValidator[verify]: done")
@@ -4,33 +4,35 @@ module PxModule
4
4
  # Misc
5
5
  MONITOR_MODE = 1
6
6
  ACTIVE_MODE = 2
7
- RISK_MODE_ACTIVE = "active_blocking"
8
- RISK_MODE_MONITOR = "monitor"
7
+ RISK_MODE_ACTIVE = 'active_blocking'
8
+ RISK_MODE_MONITOR = 'monitor'
9
9
  SDK_NAME = "RUBY SDK v#{PxModule::VERSION}"
10
10
 
11
11
  # Routes
12
- API_V1_S2S = "/api/v1/collector/s2s"
13
- API_V1_CAPTCHA = "/api/v1/risk/captcha"
14
- API_V2_RISK = "/api/v2/risk"
12
+ API_V1_S2S = '/api/v1/collector/s2s'
13
+ API_V1_CAPTCHA = '/api/v1/risk/captcha'
14
+ API_V2_RISK = '/api/v2/risk'
15
15
 
16
16
  # Activity Types
17
- BLOCK_ACTIVITY = "block"
18
- PAGE_REQUESTED_ACTIVITY = "page_requested"
17
+ BLOCK_ACTIVITY = 'block'
18
+ PAGE_REQUESTED_ACTIVITY = 'page_requested'
19
19
 
20
20
  # PxContext
21
- NO_COOKIE = "no_cookie"
22
- INVALID_COOKIE = "invalid_cookie"
23
- EXPIRED_COOKIE = "cookie_expired"
24
- COOKIE_HIGH_SCORE = "cookie_high_score"
25
- COOKIE_VALIDATION_FAILED = "cookie_validation_failed"
26
- COOKIE_DECRYPTION_FAILED = "cookie_decryption_failed"
27
- SENSITIVE_ROUTE = "sensitive_route"
21
+ NO_COOKIE = 'no_cookie'
22
+ INVALID_COOKIE = 'invalid_cookie'
23
+ EXPIRED_COOKIE = 'cookie_expired'
24
+ COOKIE_HIGH_SCORE = 'cookie_high_score'
25
+ COOKIE_VALIDATION_FAILED = 'cookie_validation_failed'
26
+ COOKIE_DECRYPTION_FAILED = 'cookie_decryption_failed'
27
+ SENSITIVE_ROUTE = 'sensitive_route'
28
28
 
29
29
  # Templates
30
- BLOCK_TEMPLATE = "block.mustache"
31
- CAPTCHA_TEMPLATE = "captcha.mustache"
30
+ BLOCK_TEMPLATE = 'block'
31
+ CAPTCHA_TEMPLATE = 'captcha'
32
+ TEMPLATE_EXT = '.mustache'
32
33
 
33
- # Tempalte Props
34
+
35
+ # Template Props
34
36
  PROP_REF_ID = :refId
35
37
  PROP_APP_ID = :appId
36
38
  PROP_VID = :vid
@@ -39,7 +41,11 @@ module PxModule
39
41
  PROP_CUSTOM_LOGO = :customLogo
40
42
  PROP_CSS_REF = :cssRef
41
43
  PROP_JS_REF = :jsRef
44
+ HOST_URL = :hostUrl
42
45
 
43
46
  VISIBLE = 'visible'
44
47
  HIDDEN = 'hidden'
48
+
49
+ # Mobile SDK
50
+ TOKEN_HEADER = 'X-PX-AUTHORIZATION'
45
51
  end
@@ -15,7 +15,7 @@ module PxModule
15
15
  @logger.debug("PxHttpClient[initialize]: HTTP client is being initilized with base_uri: #{px_config[:perimeterx_server_host]}")
16
16
  end
17
17
 
18
- # Runs a POST commant to Perimeter X servers
18
+ # Runs a POST command to Perimeter X servers
19
19
  # Params:
20
20
  # +path+:: string containing uri
21
21
  # +body+:: hash object, containing the request body, must be converted to json format
@@ -5,15 +5,20 @@ module PxModule
5
5
 
6
6
  def self.get_template(px_ctx, px_config)
7
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")
8
+ if (px_config[:challenge_enabled] && px_ctx.context[:block_action] == 'challenge')
9
+ logger.debug('PxTemplateFactory[get_template]: px challange triggered')
10
10
  return px_ctx.context[:block_action_data].html_safe
11
11
  end
12
12
 
13
- logger.debug("PxTemplateFactory[get_template]: rendering template")
14
- template_type = px_config[:captcha_enabled] ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
13
+ logger.debug('PxTemplateFactory[get_template]: rendering template')
14
+ template_type = px_ctx.context[:block_action] == 'captcha' ? PxModule::CAPTCHA_TEMPLATE : BLOCK_TEMPLATE
15
15
 
16
- Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}"
16
+ template_postfix = ''
17
+ if px_ctx.context[:cookie_origin] == 'header'
18
+ template_postfix = '.mobile'
19
+ end
20
+
21
+ Mustache.template_file = "#{File.dirname(__FILE__) }/templates/#{template_type}#{template_postfix}#{PxModule::TEMPLATE_EXT}"
17
22
  view = Mustache.new
18
23
 
19
24
  view[PxModule::PROP_APP_ID] = px_config[:app_id]
@@ -23,9 +28,10 @@ module PxModule
23
28
  view[PxModule::PROP_CUSTOM_LOGO] = px_config[:custom_logo]
24
29
  view[PxModule::PROP_CSS_REF] = px_config[:css_ref]
25
30
  view[PxModule::PROP_JS_REF] = px_config[:js_ref]
31
+ view[PxModule::HOST_URL] = "https://collector-#{px_config[:app_id]}.perimeterx.net"
26
32
  view[PxModule::PROP_LOGO_VISIBILITY] = px_config[:custom_logo] ? PxModule::VISIBLE : PxModule::HIDDEN
27
33
 
28
34
  return view.render.html_safe
29
35
  end
30
36
  end #end class
31
- end #end module
37
+ end #end module
@@ -0,0 +1,133 @@
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
+ <!-- Custom Script -->
129
+ {{# jsRef }}
130
+ <script src="{{jsRef}}"></script>
131
+ {{/ jsRef }}
132
+ </body>
133
+ </html>
@@ -0,0 +1,196 @@
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
+ <!-- Captcha -->
145
+ <script>
146
+ function captchaSolved(res) {
147
+ window.location.href = '/px/captcha_callback?status=' + res.status;
148
+ }
149
+
150
+ function handleCaptcha(response) {
151
+ var appId = '{{appId}}';
152
+ var vid = '{{vid}}';
153
+ var uuid = '{{uuid}}';
154
+ var collectorUrl = '{{{hostUrl}}}';
155
+ var req = new XMLHttpRequest();
156
+ req.open('POST', collectorUrl + '/api/v1/collector/captcha');
157
+ req.setRequestHeader('Content-Type', 'application/json');
158
+ req.addEventListener('error', function() {
159
+ captchaSolved({
160
+ status: 1
161
+ });
162
+ });
163
+ req.addEventListener('cancel', function() {
164
+ captchaSolved({
165
+ status: 2
166
+ });
167
+ });
168
+ req.addEventListener('load', function() {
169
+ if (req.status == 200) {
170
+ try {
171
+ var responseJSON = JSON.parse(req.responseText);
172
+ return captchaSolved(responseJSON);
173
+ } catch (ex) {}
174
+ }
175
+ captchaSolved({
176
+ status: 3
177
+ });
178
+ });
179
+ req.send(JSON.stringify({
180
+ appId: appId,
181
+ uuid: uuid,
182
+ vid: vid,
183
+ pxCaptcha: response,
184
+ hostname: window.location.hostname,
185
+ request: {
186
+ url: window.location.href
187
+ }
188
+ }));
189
+ }
190
+ </script>
191
+ <!-- Custom Script -->
192
+ {{#jsRef}}
193
+ <script src="{{jsRef}}"></script>
194
+ {{/jsRef}}
195
+ </body>
196
+ </html>
@@ -1,3 +1,3 @@
1
1
  module PxModule
2
- VERSION = '1.2.0'
2
+ VERSION = '1.3.0'
3
3
  end
data/readme.md CHANGED
@@ -61,7 +61,7 @@ On the Rails controller include the PerimeterX SDK via the before_action and cal
61
61
  class HomeController < ApplicationController
62
62
  include PxModule
63
63
 
64
- before_filter :px_verify_request
64
+ before_action :px_verify_request
65
65
  ...
66
66
  ...
67
67
  end
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: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nitzan Goldfeder
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-06-07 00:00:00.000000000 Z
11
+ date: 2017-07-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -156,6 +156,7 @@ extra_rdoc_files:
156
156
  - changelog.md
157
157
  files:
158
158
  - ".gitignore"
159
+ - ".travis.yml"
159
160
  - Dockerfile
160
161
  - Gemfile
161
162
  - Gemfile.lock
@@ -171,10 +172,12 @@ files:
171
172
  - lib/perimeterx/internal/clients/perimeter_x_activity_client.rb
172
173
  - lib/perimeterx/internal/clients/perimeter_x_risk_client.rb
173
174
  - lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb
175
+ - lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb
176
+ - lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb
177
+ - lib/perimeterx/internal/payload/perimeter_x_payload.rb
178
+ - lib/perimeterx/internal/payload/perimeter_x_token_v1.rb
179
+ - lib/perimeterx/internal/payload/perimeter_x_token_v3.rb
174
180
  - lib/perimeterx/internal/perimeter_x_context.rb
175
- - lib/perimeterx/internal/perimeter_x_cookie.rb
176
- - lib/perimeterx/internal/perimeter_x_cookie_v1.rb
177
- - lib/perimeterx/internal/perimeter_x_cookie_v3.rb
178
181
  - lib/perimeterx/internal/validators/perimeter_x_captcha_validator.rb
179
182
  - lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb
180
183
  - lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb
@@ -182,7 +185,9 @@ files:
182
185
  - lib/perimeterx/utils/px_http_client.rb
183
186
  - lib/perimeterx/utils/px_logger.rb
184
187
  - lib/perimeterx/utils/px_template_factory.rb
188
+ - lib/perimeterx/utils/templates/block.mobile.mustache
185
189
  - lib/perimeterx/utils/templates/block.mustache
190
+ - lib/perimeterx/utils/templates/captcha.mobile.mustache
186
191
  - lib/perimeterx/utils/templates/captcha.mustache
187
192
  - lib/perimeterx/version.rb
188
193
  - perimeter_x.gemspec