Module: PxModule

Defined in:
lib/perimeter_x.rb,
lib/perimeterx/version.rb,
lib/perimeterx/configuration.rb,
lib/perimeterx/utils/px_logger.rb,
lib/perimeterx/utils/px_constants.rb,
lib/perimeterx/utils/px_http_client.rb,
lib/perimeterx/utils/px_template_factory.rb,
lib/perimeterx/internal/perimeter_x_context.rb,
lib/perimeterx/internal/first_party/px_first_party.rb,
lib/perimeterx/internal/payload/perimeter_x_payload.rb,
lib/perimeterx/internal/payload/perimeter_x_token_v1.rb,
lib/perimeterx/internal/payload/perimeter_x_token_v3.rb,
lib/perimeterx/internal/payload/perimeter_x_cookie_v1.rb,
lib/perimeterx/internal/payload/perimeter_x_cookie_v3.rb,
lib/perimeterx/internal/clients/perimeter_x_risk_client.rb,
lib/perimeterx/internal/clients/perimeter_x_activity_client.rb,
lib/perimeterx/internal/validators/perimeter_x_s2s_validator.rb,
lib/perimeterx/internal/validators/perimeter_x_cookie_validator.rb

Defined Under Namespace

Modules: PxTemplateFactory Classes: Configuration, FirstPartyManager, PerimeterX, PerimeterXContext, PerimeterxActivitiesClient, PerimeterxCookieV1, PerimeterxCookieV3, PerimeterxCookieValidator, PerimeterxPayload, PerimeterxRiskClient, PerimeterxS2SValidator, PerimeterxTokenV1, PerimeterxTokenV3, PxHttpClient, PxLogger

Constant Summary collapse

VERSION =
'2.3.2'
MONITOR_MODE =

Misc

1
ACTIVE_MODE =
2
RISK_MODE_ACTIVE =
'active_blocking'
RISK_MODE_MONITOR =
'monitor'
SDK_NAME =
"RUBY SDK v#{PxModule::VERSION}"
API_V1_S2S =

Routes

'/api/v1/collector/s2s'
API_V3_RISK =
'/api/v3/risk'
BLOCK_ACTIVITY =

Activity Types

'block'
PAGE_REQUESTED_ACTIVITY =
'page_requested'
'no_cookie'
'invalid_cookie'
'cookie_expired'
'cookie_high_score'
'cookie_validation_failed'
'cookie_decryption_failed'
SENSITIVE_ROUTE =
'sensitive_route'
CHALLENGE_TEMPLATE =

Templates

'block_template'
TEMPLATE_EXT =
'.mustache'
RATELIMIT_TEMPLATE =
'ratelimit'
PROP_REF_ID =

Template Props

:refId
PROP_APP_ID =
:appId
PROP_VID =
:vid
PROP_UUID =
:uuid
PROP_CUSTOM_LOGO =
:customLogo
PROP_CSS_REF =
:cssRef
PROP_JS_REF =
:jsRef
PROP_BLOCK_SCRIPT =
:blockScript
PROP_ALT_BLOCK_SCRIPT =
:altBlockScript
PROP_JS_CLIENT_SRC =
:jsClientSrc
PROP_HOST_URL =
:hostUrl
PROP_FIRST_PARTY_ENABLED =
:firstPartyEnabled
CLIENT_HOST =

Hosts

'client.perimeterx.net'
CAPTCHA_HOST =
'captcha.px-cdn.net'
ALT_CAPTCHA_HOST =
'captcha.px-cloud.net'
VISIBLE =
'visible'
HIDDEN =
'hidden'
TOKEN_HEADER =

Mobile SDK

'X-PX-AUTHORIZATION'
ORIGINAL_TOKEN_HEADER =
'X-PX-ORIGINAL-TOKEN'
VID_REGEX =

Regular Expressions

/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/
MOBILE_TOKEN_V3_REGEX =
/\A3:(.*)\z/
MOBILE_ERROR_REGEX =
/\A[0-9]\z/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.configure(basic_config) ⇒ Object



160
161
162
# File 'lib/perimeter_x.rb', line 160

def self.configure(basic_config)
  PerimeterX.set_basic_config(basic_config)
end

Instance Method Details

#px_verify_request(request_config = {}) ⇒ Object

Module expose API



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/perimeter_x.rb', line 19

def px_verify_request(request_config={})    
  begin
    px_instance = PerimeterX.new(request_config)
    req = ActionDispatch::Request.new(request.env)

    # handle first party requests
    if px_instance.first_party.is_first_party_request(req)
      render_first_party_response(req, px_instance)
      return true
    end

    # verify request
    px_ctx = px_instance.verify(req)
    px_config = px_instance.px_config

    msg_title = 'PxModule[px_verify_request]'

    # In case custom verification handler is in use
    if px_config.key?(:custom_verification_handler)
      px_config[:logger].debug("#{msg_title}: custom_verification_handler triggered")
      return instance_exec(px_ctx, &px_config[:custom_verification_handler])
    end

    unless px_ctx.nil? || px_ctx.context[:verified] || (px_config[:module_mode] == PxModule::MONITOR_MODE && !px_ctx.context[:should_bypass_monitor])
      # In case custom block handler exists (soon to be deprecated)
      if px_config.key?(:custom_block_handler)
        px_config[:logger].debug("#{msg_title}: custom_block_handler triggered")
        px_config[:logger].debug(
            "#{msg_title}: Please note that custom_block_handler is deprecated. Use custom_verification_handler instead.")
        return instance_exec(px_ctx, &px_config[:custom_block_handler])
      else
        if px_ctx.context[:block_action]== 'rate_limit'
          px_config[:logger].debug("#{msg_title}: sending rate limit page")
          response.status = 429
        else
          px_config[:logger].debug("#{msg_title}: sending default block page")
          response.status = 403
        end

        is_mobile = px_ctx.context[:cookie_origin] == 'header' ? '1' : '0'
        action = px_ctx.context[:block_action][0,1]
        block_script_uri = "/captcha.js?a=#{action}&u=#{px_ctx.context[:uuid]}&v=#{px_ctx.context[:vid]}&m=#{is_mobile}"

        if px_config[:first_party_enabled]
          px_template_object = {
          js_client_src:  "/#{px_config[:app_id][2..-1]}/init.js",
          block_script: "/#{px_config[:app_id][2..-1]}/captcha/#{px_config[:app_id]}#{block_script_uri}",
          host_url: "/#{px_config[:app_id][2..-1]}/xhr",
          alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
          }
        else
          px_template_object = {
          js_client_src: "//#{PxModule::CLIENT_HOST}/#{px_config[:app_id]}/main.min.js",
          block_script: "//#{PxModule::CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}",
          host_url: "https://collector-#{px_config[:app_id]}.perimeterx.net",
          alt_block_script: "//#{PxModule::ALT_CAPTCHA_HOST}/#{px_config[:app_id]}#{block_script_uri}"
          }
        end

        html = PxTemplateFactory.get_template(px_ctx, px_config, px_template_object)

        # Web handler
        if px_ctx.context[:cookie_origin] == 'cookie'

          accept_header_value = request.headers['accept'] || request.headers['content-type'];
          is_json_response = px_ctx.context[:block_action] != 'rate_limit' && accept_header_value && accept_header_value.split(',').select {|e| e.downcase.include? 'application/json'}.length > 0;

          if (is_json_response)
            px_config[:logger].debug("#{msg_title}: advanced blocking response response")
            response.headers['Content-Type'] = 'application/json'

            hash_json = {
                :appId => px_config[:app_id],
                :jsClientSrc => px_template_object[:js_client_src],
                :firstPartyEnabled => px_ctx.context[:first_party_enabled],
                :uuid => px_ctx.context[:uuid],
                :vid => px_ctx.context[:vid],
                :hostUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net",
                :blockScript => px_template_object[:block_script],
                :altBlockScript => px_template_object[:alt_block_script],
                :customLogo => px_config[:custom_logo]
            }

            render :json => hash_json
          else
            px_config[:logger].debug('#{msg_title}: web block')
            response.headers['Content-Type'] = 'text/html'
            render :html => html
          end
        else # Mobile SDK
          px_config[:logger].debug("#{msg_title}: mobile sdk block")
          response.headers['Content-Type'] = 'application/json'
          hash_json = {
              :action => px_ctx.context[:block_action],
              :uuid => px_ctx.context[:uuid],
              :vid => px_ctx.context[:vid],
              :appId => px_config[:app_id],
              :page => Base64.strict_encode64(html),
              :collectorUrl => "https://collector-#{px_config[:app_id]}.perimeterx.net"
          }
          render :json => hash_json
        end
      end
    end

    # Request was verified
    return px_ctx.nil? ? true : px_ctx.context[:verified]
    
  rescue PxConfigurationException
    raise
  rescue Exception => e
    error_logger = PxLogger.new(true)
    error_logger.error("#{e.backtrace.first}: #{e.message} (#{e.class})")
    e.backtrace.drop(1).map {|s| error_logger.error("\t#{s}")}
    return nil
  end
end

#render_first_party_response(req, px_instance) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/perimeter_x.rb', line 137

def render_first_party_response(req, px_instance)
  fp = px_instance.first_party
  px_config = px_instance.px_config
  
  if px_config[:first_party_enabled]
    # first party enabled - proxy response
    fp_response = fp.send_first_party_request(req)
    response.status = fp_response.code
    fp_response.to_hash.each do |header_name, header_value_arr| 
      if header_name!="content-length"
        response.headers[header_name] = header_value_arr[0]
      end
    end
    res_type = fp.get_response_content_type(req)
    render res_type => fp_response.body
  else
    # first party disabled - return empty response
    response.status = 200
    res_type = fp.get_response_content_type(req)
    render res_type => ""
  end
end