perimeter_x 1.0.6.pre.alpha → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +6 -3
  3. data/.travis.yml +3 -0
  4. data/Dockerfile +19 -41
  5. data/Gemfile +1 -1
  6. data/Gemfile.lock +51 -3
  7. data/LICENSE.txt +9 -12
  8. data/Rakefile +10 -2
  9. data/changelog.md +72 -0
  10. data/examples/app/controllers/home_controller.rb +9 -0
  11. data/examples/app/views/home/index.html.erb.dist +20 -0
  12. data/examples/config/initializers/perimeterx.rb.dist +8 -0
  13. data/examples/{routes.rb → config/routes.rb} +0 -0
  14. data/lib/perimeter_x.rb +192 -37
  15. data/lib/perimeterx/configuration.rb +30 -18
  16. data/lib/perimeterx/internal/clients/perimeter_x_activity_client.rb +110 -0
  17. data/lib/perimeterx/internal/clients/perimeter_x_risk_client.rb +28 -0
  18. data/lib/perimeterx/internal/exceptions/px_cookie_decryption_exception.rb +5 -0
  19. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb +42 -0
  20. data/lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb +37 -0
  21. data/lib/perimeterx/internal/payload/perimeter_x_payload.rb +148 -0
  22. data/lib/perimeterx/internal/payload/perimeter_x_token_v1.rb +38 -0
  23. data/lib/perimeterx/internal/payload/perimeter_x_token_v3.rb +36 -0
  24. data/lib/perimeterx/internal/perimeter_x_context.rb +112 -53
  25. data/lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb +103 -0
  26. data/lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb +128 -0
  27. data/lib/perimeterx/utils/px_constants.rb +62 -0
  28. data/lib/perimeterx/utils/px_http_client.rb +43 -28
  29. data/lib/perimeterx/utils/px_logger.rb +12 -6
  30. data/lib/perimeterx/utils/px_template_factory.rb +41 -0
  31. data/lib/perimeterx/utils/templates/block_template.mustache +175 -0
  32. data/lib/perimeterx/utils/templates/ratelimit.mustache +9 -0
  33. data/lib/perimeterx/version.rb +2 -2
  34. data/perimeter_x.gemspec +10 -4
  35. data/readme.md +258 -42
  36. metadata +130 -24
  37. data/bin/console +0 -14
  38. data/bin/setup +0 -8
  39. data/examples/home_controller.rb.dist +0 -23
  40. data/lib/perimeterx/internal/perimeter_x_risk_client.rb +0 -29
  41. data/lib/perimeterx/internal/perimeter_x_s2s_validator.rb +0 -67
@@ -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