perimeter_x 1.0.6.pre.alpha → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +6 -3
  3. data/.travis.yml +3 -0
  4. data/Dockerfile +19 -41
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +51 -3
  7. data/LICENSE.txt +9 -12
  8. data/Rakefile +10 -2
  9. data/changelog.md +72 -0
  10. data/examples/app/controllers/home_controller.rb +9 -0
  11. data/examples/app/views/home/index.html.erb.dist +20 -0
  12. data/examples/config/initializers/perimeterx.rb.dist +8 -0
  13. data/examples/{routes.rb → config/routes.rb} +0 -0
  14. data/lib/perimeter_x.rb +192 -37
  15. data/lib/perimeterx/configuration.rb +30 -18
  16. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +110 -0
  17. data/lib/perimeterx/internal/clients/perimeter_x_risk_client.rb +28 -0
  18. data/lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb +5 -0
  19. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb +42 -0
  20. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb +37 -0
  21. data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +148 -0
  22. data/lib/perimeterx/internal/payload/perimeter_x_token_v1.rb +38 -0
  23. data/lib/perimeterx/internal/payload/perimeter_x_token_v3.rb +36 -0
  24. data/lib/perimeterx/internal/perimeter_x_context.rb +112 -53
  25. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +103 -0
  26. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +128 -0
  27. data/lib/perimeterx/utils/px_constants.rb +62 -0
  28. data/lib/perimeterx/utils/px_http_client.rb +43 -28
  29. data/lib/perimeterx/utils/px_logger.rb +12 -6
  30. data/lib/perimeterx/utils/px_template_factory.rb +41 -0
  31. data/lib/perimeterx/utils/templates/block_template.mustache +175 -0
  32. data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
  33. data/lib/perimeterx/version.rb +2 -2
  34. data/perimeter_x.gemspec +10 -4
  35. data/readme.md +258 -42
  36. metadata +130 -24
  37. data/bin/console +0 -14
  38. data/bin/setup +0 -8
  39. data/examples/home_controller.rb.dist +0 -23
  40. data/lib/perimeterx/internal/perimeter_x_risk_client.rb +0 -29
  41. data/lib/perimeterx/internal/perimeter_x_s2s_validator.rb +0 -67
@@ -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
@@ -1,62 +1,121 @@
1
1
  require 'perimeterx/utils/px_logger'
2
+ require 'perimeterx/utils/px_constants'
2
3
 
3
- class PerimeterXContext
4
- L = PxLogger.instance
5
-
6
- attr_accessor :context
7
- attr_accessor :px_config
8
-
9
- def initialize(px_config, req)
10
- L.info("PerimeterXContext: initialize")
11
- @context = Hash.new
12
-
13
- @context[:px_cookies] = Hash.new
14
- @context[:headers] = Hash.new
15
- cookies = req.cookies
16
- if (!cookies.empty?)
17
- # Prepare hashed cookies
18
- cookies.each do |k, v|
19
- case k
20
- when "_px3"
21
- @context[:px_cookies] = v
22
- when "_px"
23
- @context[:px_cookies] = v
24
- when "_pxCaptcha"
25
- @context[:px_captcha] = v
4
+ module PxModule
5
+ class PerimeterXContext
6
+
7
+ attr_accessor :context
8
+ attr_accessor :px_config
9
+
10
+ def initialize(px_config, req)
11
+ @logger = px_config[:logger]
12
+ @logger.debug('PerimeterXContext[initialize]')
13
+ @context = Hash.new
14
+
15
+ @context[:px_cookie] = Hash.new
16
+ @context[:headers] = Hash.new
17
+ @context[:cookie_origin] = 'cookie'
18
+ @context[:made_s2s_risk_api_call] = false
19
+ cookies = req.cookies
20
+
21
+ # Get IP from header/custom function
22
+ if px_config[:ip_headers].length() > 0
23
+ px_config[:ip_headers].each do |ip_header|
24
+ if req.headers[ip_header]
25
+ @context[:ip] = force_utf8(req.headers[ip_header])
26
+ end
26
27
  end
27
- end #end case
28
- end #end empty cookies
29
-
30
- req.headers.each do |k, v|
31
- if (k.start_with? "HTTP_")
32
- header = k.to_s.gsub("HTTP_", "")
33
- header = header.gsub("_", "-").downcase
34
- @context[:headers][header.to_sym] = v
28
+ elsif px_config[:ip_header_function] != nil
29
+ @context[:ip] = px_config[:ip_header_function].call(req)
35
30
  end
36
- end #end headers foreach
37
-
38
- @context[:hostname]= req.headers['HTTP_HOST']
39
- @context[:user_agent] = req.headers['HTTP_USER_AGENT'] ? req.headers['HTTP_USER_AGENT'] : ''
40
- @context[:full_url] = req.original_url
41
- @context[:score] = 0
42
-
43
- if px_config.key?('custom_user_ip')
44
- @context[:ip] = px_config['custom_user_ip']
45
- elsif px_config.key?('px_custom_user_ip_method')
46
- puts "px_custom_user_ip_method triggered"
47
- @context[:ip] = px_config['px_custom_user_ip_method'].call(req)
48
- else
49
- @context[:ip] = req.headers['REMOTE_ADDR'];
50
- end
51
31
 
52
- if req.headers['SERVER_PROTOCOL']
53
- httpVer = req.headers['SERVER_PROTOCOL'].split("/")
54
- if httpVer.size > 0
55
- @context[:http_version] = httpVer[1];
32
+ if @context[:ip] == nil
33
+ @context[:ip] = req.ip
56
34
  end
35
+
36
+ # Get token from header
37
+ if req.headers[PxModule::TOKEN_HEADER]
38
+ @context[:cookie_origin] = 'header'
39
+ token = force_utf8(req.headers[PxModule::TOKEN_HEADER])
40
+ if token.include? ':'
41
+ exploded_token = token.split(':', 2)
42
+ cookie_sym = "v#{exploded_token[0]}".to_sym
43
+ @context[:px_cookie][cookie_sym] = exploded_token[1]
44
+ else # TOKEN_HEADER exists yet there's no ':' delimiter - may indicate an error (storing original value)
45
+ @context[:px_cookie] = force_utf8(req.headers[PxModule::TOKEN_HEADER])
46
+ end
47
+ elsif !cookies.empty? # Get cookie from jar
48
+ # Prepare hashed cookies
49
+ cookies.each do |k, v|
50
+ case k.to_s
51
+ when '_px3'
52
+ @context[:px_cookie][:v3] = force_utf8(v)
53
+ when '_px'
54
+ @context[:px_cookie][:v1] = force_utf8(v)
55
+ when '_pxvid'
56
+ if v.is_a?(String) && v.match(PxModule::VID_REGEX)
57
+ @context[:vid_source] = "vid_cookie"
58
+ @context[:vid] = force_utf8(v)
59
+ end
60
+ end
61
+ end #end case
62
+ end #end empty cookies
63
+
64
+ req.headers.each do |k, v|
65
+ if (k.start_with? 'HTTP_')
66
+ header = k.to_s.gsub('HTTP_', '')
67
+ header = header.gsub('_', '-').downcase
68
+ @context[:headers][header.to_sym] = force_utf8(v)
69
+ end
70
+ end #end headers foreach
71
+
72
+ @context[:hostname]= req.server_name
73
+ @context[:user_agent] = req.user_agent ? req.user_agent : ''
74
+ @context[:uri] = px_config[:custom_uri] ? px_config[:custom_uri].call(req) : req.fullpath
75
+ @context[:full_url] = req.original_url
76
+ @context[:format] = req.format.symbol
77
+ @context[:score] = 0
78
+
79
+ if req.server_protocol
80
+ httpVer = req.server_protocol.split('/')
81
+ if httpVer.size > 0
82
+ @context[:http_version] = httpVer[1]
83
+ end
84
+ end
85
+ @context[:http_method] = req.method
86
+ @context[:sensitive_route] = check_sensitive_route(px_config[:sensitive_routes], @context[:uri])
87
+ end #end init
88
+
89
+ def check_sensitive_route(sensitive_routes, uri)
90
+ sensitive_routes.each do |sensitive_route|
91
+ return true if uri.start_with? sensitive_route
92
+ end
93
+ false
94
+ end
95
+
96
+ def force_utf8(str)
97
+ return str.encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '')
98
+ end
99
+
100
+ def set_block_action_type(action)
101
+ @context[:block_action] = case action
102
+ when 'c'
103
+ 'captcha'
104
+ when 'b'
105
+ return 'block'
106
+ when 'j'
107
+ return 'challenge'
108
+ when 'r'
109
+ return 'rate_limit'
110
+ else
111
+ return captcha
112
+ end
57
113
  end
58
- @context[:http_method] = req.headers['REQUEST_METHOD'];
59
114
 
60
- end #end init
115
+ def get_px_cookie
116
+ cookie = @context[:px_cookie].key?(:v3) ? @context[:px_cookie][:v3] : @context[:px_cookie][:v1]
117
+ return cookie.tr(' ','+') if !cookie.nil?
118
+ end
61
119
 
62
- end #end class
120
+ end
121
+ end
@@ -0,0 +1,103 @@
1
+ require 'perimeterx/utils/px_constants'
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'
7
+
8
+ module PxModule
9
+ class PerimeterxCookieValidator
10
+
11
+ attr_accessor :px_config
12
+
13
+ def initialize(px_config)
14
+ @px_config = px_config
15
+ @logger = px_config[:logger]
16
+ end
17
+
18
+
19
+ def verify(px_ctx)
20
+ begin
21
+ # Mobile Error cases
22
+ if px_ctx.context[:cookie_origin] == 'header'
23
+ if px_ctx.context[:px_cookie].to_s.empty?
24
+ @logger.warn("PerimeterxCookieValidator:[verify]: Empty token value - decryption failed")
25
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
26
+ return false, px_ctx
27
+ elsif px_ctx.context[:px_cookie] == "1"
28
+ @logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
29
+ px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
30
+ return false, px_ctx
31
+ elsif px_ctx.context[:px_cookie] == "2" # Mobile SDK connection error
32
+ @logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk connection error")
33
+ px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_CONNECTION_ERROR
34
+ return false, px_ctx
35
+ elsif px_ctx.context[:px_cookie] == "3" # Mobile SDK pinning error
36
+ @logger.warn("PerimeterxCookieValidator:[verify]: mobile sdk pinning error")
37
+ px_ctx.context[:s2s_call_reason] = PxModule::MOBILE_SDK_PINNING_ERROR
38
+ return false, px_ctx
39
+ end
40
+ elsif px_ctx.context[:px_cookie].empty?
41
+ @logger.warn("PerimeterxCookieValidator:[verify]: no cookie")
42
+ px_ctx.context[:s2s_call_reason] = PxModule::NO_COOKIE
43
+ return false, px_ctx
44
+ end
45
+
46
+ # Deserialize cookie start
47
+ cookie = PerimeterxPayload.px_cookie_factory(px_ctx, @px_config)
48
+ if !cookie.deserialize()
49
+ @logger.warn("PerimeterxCookieValidator:[verify]: invalid cookie")
50
+ px_ctx.context[:px_orig_cookie] = px_ctx.get_px_cookie
51
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
52
+ return false, px_ctx
53
+ end
54
+ px_ctx.context[:decoded_cookie] = cookie.decoded_cookie
55
+ px_ctx.context[:score] = cookie.cookie_score()
56
+ px_ctx.context[:uuid] = cookie.decoded_cookie[:u]
57
+ px_ctx.context[:block_action] = px_ctx.set_block_action_type(cookie.cookie_block_action())
58
+ px_ctx.context[:cookie_hmac] = cookie.cookie_hmac()
59
+
60
+ vid = cookie.decoded_cookie[:v]
61
+ if vid.is_a?(String) && vid.match(PxModule::VID_REGEX)
62
+ px_ctx.context[:vid_source] = "risk_cookie"
63
+ px_ctx.context[:vid] = vid
64
+ end
65
+
66
+ if cookie.expired?
67
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie expired")
68
+ px_ctx.context[:s2s_call_reason] = PxModule::EXPIRED_COOKIE
69
+ return false, px_ctx
70
+ end
71
+
72
+ if cookie.high_score?
73
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie high score")
74
+ px_ctx.context[:blocking_reason] = 'cookie_high_score'
75
+ return true, px_ctx
76
+ end
77
+
78
+ if !cookie.secured?
79
+ @logger.warn("PerimeterxCookieValidator:[verify]: cookie invalid hmac")
80
+ px_ctx.context[:s2s_call_reason] = PxModule:: COOKIE_VALIDATION_FAILED
81
+ return false, px_ctx
82
+ end
83
+
84
+ if px_ctx.context[:sensitive_route]
85
+ @logger.info("PerimeterxCookieValidator:[verify]: cookie was verified but route is sensitive")
86
+ px_ctx.context[:s2s_call_reason] = PxModule::SENSITIVE_ROUTE
87
+ return false, px_ctx
88
+ end
89
+
90
+ @logger.debug("PerimeterxCookieValidator:[verify]: cookie validation passed succesfully")
91
+
92
+ px_ctx.context[:pass_reason] = 'cookie'
93
+ return true, px_ctx
94
+ rescue Exception => e
95
+ @logger.error("PerimeterxCookieValidator:[verify]: exception while verifying cookie => #{e.message}")
96
+ px_ctx.context[:px_orig_cookie] = px_ctx.context[:px_cookie]
97
+ px_ctx.context[:s2s_call_reason] = PxModule::COOKIE_DECRYPTION_FAILED
98
+ return false, px_ctx
99
+ end
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,128 @@
1
+ require 'perimeterx/internal/clients/perimeter_x_risk_client'
2
+
3
+ module PxModule
4
+ class PerimeterxS2SValidator < PerimeterxRiskClient
5
+
6
+ def initialize(px_config, http_client)
7
+ super(px_config, http_client)
8
+ @logger.debug('PerimeterxS2SValidator[initialize]')
9
+ end
10
+
11
+ def send_risk_request(px_ctx)
12
+ @logger.debug('PerimeterxS2SValidator[send_risk_request]: send_risk_request')
13
+
14
+ risk_mode = PxModule::RISK_MODE_ACTIVE
15
+ if @px_config[:module_mode] == PxModule::MONITOR_MODE
16
+ risk_mode = PxModule::RISK_MODE_MONITOR
17
+ end
18
+
19
+ request_body = {
20
+ :request => {
21
+ :ip => px_ctx.context[:ip],
22
+ :headers => format_headers(px_ctx),
23
+ :uri => px_ctx.context[:uri],
24
+ :url => px_ctx.context[:full_url]
25
+ },
26
+ :additional => {
27
+ :s2s_call_reason => px_ctx.context[:s2s_call_reason],
28
+ :module_version => @px_config[:sdk_name],
29
+ :cookie_origin => px_ctx.context[:cookie_origin],
30
+ :http_method => px_ctx.context[:http_method],
31
+ :http_version => px_ctx.context[:http_version],
32
+ :risk_mode => risk_mode
33
+ }
34
+ }
35
+ #Check for hmac
36
+ @logger.debug("px_ctx cookie_hmac key = #{px_ctx.context.key?(:cookie_hmac)}, value is: #{px_ctx.context[:cookie_hmac]}")
37
+ if px_ctx.context.key?(:cookie_hmac)
38
+ request_body[:additional][:px_cookie_hmac] = px_ctx.context[:cookie_hmac]
39
+ end
40
+
41
+
42
+ #Check for VID
43
+ if px_ctx.context.key?(:vid)
44
+ request_body[:vid] = px_ctx.context[:vid]
45
+ end
46
+
47
+ #Check for uuid
48
+ if px_ctx.context.key?(:uuid)
49
+ request_body[:uuid] = px_ctx.context[:uuid]
50
+ end
51
+
52
+ #S2S Call reason
53
+ decode_cookie_reasons = [PxModule::EXPIRED_COOKIE, PxModule::COOKIE_VALIDATION_FAILED]
54
+ if ( px_ctx.context[:s2s_call_reason] == PxModule::COOKIE_DECRYPTION_FAILED )
55
+ @logger.debug("PerimeterxS2SValidator[send_risk_request]: attaching px_orig_cookie to request")
56
+ request_body[:additional][:px_orig_cookie] = px_ctx.context[:px_orig_cookie]
57
+ elsif decode_cookie_reasons.include? (px_ctx.context[:s2s_call_reason])
58
+ if (px_ctx.context.key?(:decoded_cookie))
59
+ request_body[:additional][:px_cookie] = px_ctx.context[:decoded_cookie]
60
+ end
61
+ end
62
+
63
+ # Prepare request
64
+ headers = {
65
+ "Authorization" => "Bearer #{@px_config[:auth_token]}" ,
66
+ "Content-Type" => "application/json"
67
+ };
68
+
69
+ # Custom risk handler
70
+ risk_start = Time.now
71
+ if (risk_mode == PxModule::ACTIVE_MODE && @px_config.key?(:custom_risk_handler))
72
+ response = @px_config[:custom_risk_handler].call(PxModule::API_V3_RISK, request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
73
+ else
74
+ response = @http_client.post(PxModule::API_V3_RISK , request_body, headers, @px_config[:api_timeout], @px_config[:api_timeout_connection])
75
+ end
76
+
77
+ # Set risk_rtt
78
+ if(response)
79
+ risk_end = Time.now
80
+ px_ctx.context[:risk_rtt] = ((risk_end-risk_start)*1000).round
81
+ end
82
+
83
+ return response
84
+ end
85
+
86
+ def verify(px_ctx)
87
+ @logger.debug("PerimeterxS2SValidator[verify]")
88
+ response = send_risk_request(px_ctx)
89
+ if (!response)
90
+ px_ctx.context[:pass_reason] = "s2s_timeout"
91
+ return px_ctx
92
+ end
93
+ px_ctx.context[:made_s2s_risk_api_call] = true
94
+
95
+ # From here response should be valid, if success or error
96
+ response_body = eval(response.body);
97
+ # When success
98
+ if (response.code == 200 && response_body.key?(:score) && response_body.key?(:action) && response_body.key?(:status) && response_body[:status] == 0 )
99
+ @logger.debug("PerimeterxS2SValidator[verify]: response ok")
100
+ score = response_body[:score]
101
+ px_ctx.context[:score] = score
102
+ px_ctx.context[:uuid] = response_body[:uuid]
103
+ px_ctx.context[:block_action] = px_ctx.set_block_action_type(response_body[:action])
104
+ if (response_body[:action] == 'j' && response_body.key?(:action_data) && response_body[:action_data].key?(:body))
105
+ px_ctx.context[:block_action_data] = response_body[:action_data][:body]
106
+ px_ctx.context[:blocking_reason] = 'challenge'
107
+ elsif (score >= @px_config[:blocking_score])
108
+ px_ctx.context[:blocking_reason] = 's2s_high_score'
109
+ else
110
+ px_ctx.context[:pass_reason] = 's2s'
111
+ end #end challange or blocking score
112
+ end #end success response
113
+
114
+ # When error
115
+ risk_error_status = response_body && response_body.key?(:status) && response_body[:status] == -1
116
+ if(response.code != 200 || risk_error_status)
117
+ @logger.warn("PerimeterxS2SValidator[verify]: bad response, returned code #{response.code} #{risk_error_status ? "risk status: -1" : ""}")
118
+ px_ctx.context[:pass_reason] = 'request_failed'
119
+ px_ctx.context[:uuid] = (!response_body || response_body[:uuid].nil?)? "" : response_body[:uuid]
120
+ px_ctx.context[:s2s_error_msg] = !response_body || response_body[:message].nil? ? 'unknown' : response_body[:message]
121
+ end
122
+
123
+ @logger.debug("PerimeterxS2SValidator[verify]: done")
124
+ return px_ctx
125
+ end #end method
126
+
127
+ end
128
+ end
@@ -0,0 +1,62 @@
1
+ require 'perimeterx/version'
2
+
3
+ module PxModule
4
+ # Misc
5
+ MONITOR_MODE = 1
6
+ ACTIVE_MODE = 2
7
+ RISK_MODE_ACTIVE = 'active_blocking'
8
+ RISK_MODE_MONITOR = 'monitor'
9
+ SDK_NAME = "RUBY SDK v#{PxModule::VERSION}"
10
+
11
+ # Routes
12
+ API_V1_S2S = '/api/v1/collector/s2s'
13
+ API_V3_RISK = '/api/v3/risk'
14
+
15
+ # Activity Types
16
+ BLOCK_ACTIVITY = 'block'
17
+ PAGE_REQUESTED_ACTIVITY = 'page_requested'
18
+
19
+ # PxContext
20
+ NO_COOKIE = 'no_cookie'
21
+ INVALID_COOKIE = 'invalid_cookie'
22
+ EXPIRED_COOKIE = 'cookie_expired'
23
+ COOKIE_HIGH_SCORE = 'cookie_high_score'
24
+ COOKIE_VALIDATION_FAILED = 'cookie_validation_failed'
25
+ COOKIE_DECRYPTION_FAILED = 'cookie_decryption_failed'
26
+ SENSITIVE_ROUTE = 'sensitive_route'
27
+
28
+ # Templates
29
+ CHALLENGE_TEMPLATE = 'block_template'
30
+ TEMPLATE_EXT = '.mustache'
31
+ RATELIMIT_TEMPLATE = 'ratelimit'
32
+
33
+
34
+ # Template Props
35
+ PROP_REF_ID = :refId
36
+ PROP_APP_ID = :appId
37
+ PROP_VID = :vid
38
+ PROP_UUID = :uuid
39
+ PROP_LOGO_VISIBILITY = :logoVisibility
40
+ PROP_CUSTOM_LOGO = :customLogo
41
+ PROP_CSS_REF = :cssRef
42
+ PROP_JS_REF = :jsRef
43
+ PROP_BLOCK_SCRIPT = :blockScript
44
+ PROP_JS_CLIENT_SRC = :jsClientSrc
45
+ PROP_HOST_URL = :hostUrl
46
+ PROP_FIRST_PARTY_ENABLED = :firstPartyEnabled
47
+
48
+ # Hosts
49
+ CLIENT_HOST = 'client.px-cloud.net'
50
+ CAPTCHA_HOST = 'captcha.px-cloud.net'
51
+
52
+ VISIBLE = 'visible'
53
+ HIDDEN = 'hidden'
54
+
55
+ # Mobile SDK
56
+ TOKEN_HEADER = 'X-PX-AUTHORIZATION'
57
+ MOBILE_SDK_CONNECTION_ERROR = 'mobile_sdk_connection_error'
58
+ MOBILE_SDK_PINNING_ERROR = 'mobile_sdk_pinning_error'
59
+
60
+ # Regular Expressions
61
+ VID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
62
+ end