Skip to content

Commit 2fe0fb8

Browse files
Merge pull request #91 from PerimeterX/release/v3.3.0
Release/v3.3.0
2 parents 945a874 + bcf8826 commit 2fe0fb8

16 files changed

Lines changed: 141 additions & 355 deletions

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Change Log
22

3+
## [v3.3.0](https://github.com/PerimeterX/perimeterx-python-wsgi) (2022-04-11)
4+
- Changed to new version of block page
5+
- Configurable max buffer length
6+
- Configurable px_backend_url
7+
- Sending activities at the end of request cycle rather than beginning of the next one
8+
- Removed problematic fields in Risk API request
9+
310
## [v3.2.1](https://github.com/PerimeterX/perimeterx-python-wsgi) (2019-08-22)
411
- Upgrade dependency for security issue.
512

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
[PerimeterX](http://www.perimeterx.com) Python Middleware
66
=============================================================
7-
> Latest stable version: [v3.2.1](https://pypi.org/project/perimeterx-python-wsgi/)
7+
> Latest stable version: [v3.3.0](https://pypi.org/project/perimeterx-python-wsgi/)
88
9-
> Latest GAE stable version: [v3.2.1](https://pypi.org/project/perimeterx-python-wsgi-gae/)
9+
> Latest GAE stable version: [v3.3.0](https://pypi.org/project/perimeterx-python-wsgi-gae/)
1010
1111
Table of Contents
1212
-----------------
@@ -100,8 +100,8 @@ px_config = {
100100
application = get_wsgi_application()
101101
application = PerimeterX(application, px_config)
102102
```
103-
- The PerimeterX **Application ID** / **AppId** and PerimeterX **Token** / **Auth Token** can be found in the Portal, in [Applications](https://console.perimeterx.com/#/app/applicationsmgmt).
104-
- PerimeterX **Risk Cookie** / **Cookie Key** can be found in the portal, in [Policies](https://console.perimeterx.com/#/app/policiesmgmt).
103+
- The PerimeterX **Application ID** / **AppId** and PerimeterX **Token** / **Auth Token** can be found in the Portal, in [Applications](https://console.perimeterx.com/botDefender/admin?page=applicationsmgmt).
104+
- PerimeterX **Risk Cookie** / **Cookie Key** can be found in the portal, in [Policies](https://console.perimeterx.com/botDefender/admin?page=policiesmgmt).
105105
The Policy from where the **Risk Cookie** / **Cookie Key** is taken must correspond with the Application from where the **Application ID** / **AppId** and PerimeterX **Token** / **Auth Token**.
106106
For details on how to create a custom Captcha page, refer to the [documentation](https://console.perimeterx.com/docs/server_integration_new.html#custom-captcha-section)
107107
## <a name="configuration"></a>Optional Configuration

perimeterx/middleware.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ def __init__(self, app, config=None):
3535
px_activities_client.send_enforcer_telemetry_activity(config=px_config, update_reason='initial_config')
3636

3737
def __call__(self, environ, start_response):
38-
px_activities_client.send_activities_in_thread()
3938
context = None
4039
try:
4140
start = time.time()
@@ -47,6 +46,7 @@ def __call__(self, environ, start_response):
4746
context, verified_response = self.verify(request)
4847
pxhd_callback = create_custom_pxhd_callback(context, start_response)
4948
self._config.logger.debug("PerimeterX Enforcer took: {} ms".format((time.time() - start) * 1000))
49+
px_activities_client.send_activities_in_thread()
5050
if verified_response is True:
5151
return self.app(environ, pxhd_callback)
5252

perimeterx/px_activities_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ def _send_activities_chunk():
2424
'Content-Type': 'application/json'
2525
}
2626
full_url = CONFIG.server_host + px_constants.API_ACTIVITIES
27-
chunk = ACTIVITIES_BUFFER[:10]
27+
chunk = ACTIVITIES_BUFFER[:CONFIG.max_buffer_len]
2828
for _ in range(len(chunk)):
2929
ACTIVITIES_BUFFER.pop(0)
3030
px_httpc.send(full_url=full_url, body=json.dumps(chunk), headers=default_headers, config=CONFIG, method='POST')
3131

3232
def send_activities_in_thread():
33-
if len(ACTIVITIES_BUFFER) >= 10:
33+
if len(ACTIVITIES_BUFFER) >= CONFIG.max_buffer_len:
3434
CONFIG.logger.debug('Posting {} Activities'.format(len(ACTIVITIES_BUFFER)))
3535
t1 = threading.Thread(target=_send_activities_chunk)
3636
t1.daemon = True

perimeterx/px_api.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,7 @@ def prepare_risk_body(ctx, config):
115115
'request': {
116116
'ip': ctx.ip,
117117
'headers': format_headers(ctx.headers),
118-
'uri': ctx.uri,
119118
'url': ctx.full_url,
120-
'firstParty': 'true' if config.first_party else 'false'
121119
},
122120
'additional': {
123121
's2s_call_reason': ctx.s2s_call_reason,

perimeterx/px_blocker.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ def prepare_properties(self, ctx, config):
7676
custom_logo = config.custom_logo
7777
is_mobile_num = 1 if ctx.is_mobile else 0
7878
captcha_uri = 'captcha.js?a={}&u={}&v={}&m={}'.format(ctx.block_action, uuid, vid, is_mobile_num)
79+
alt_captcha_src = '//{}/{}/{}'.format(px_constants.ALT_CAPTCHA_HOST, app_id, captcha_uri)
7980

8081
if config.first_party and not ctx.is_mobile:
8182
prefix = app_id[2:]
@@ -88,18 +89,17 @@ def prepare_properties(self, ctx, config):
8889
host_url = px_constants.COLLECTOR_URL.format(app_id.lower())
8990

9091
return {
91-
'refId': uuid,
9292
'appId': app_id,
9393
'vid': vid,
9494
'uuid': uuid,
95-
'customLogo': custom_logo,
95+
'customLogo': custom_logo if custom_logo else '',
9696
'cssRef': config.css_ref,
9797
'jsRef': config.js_ref,
98-
'logoVisibility': 'visible' if custom_logo is not None else 'hidden',
9998
'hostUrl': host_url,
10099
'jsClientSrc': js_client_src,
101100
'firstPartyEnabled': 'true' if config.first_party else 'false',
102-
'blockScript': captcha_src
101+
'blockScript': captcha_src,
102+
'altBlockScript': alt_captcha_src
103103
}
104104

105105
def is_json_response(self, ctx):
@@ -118,7 +118,7 @@ def parse_action(action):
118118
if 'b' == action:
119119
return 'block'
120120
elif 'j' == action:
121-
return 'challege'
121+
return 'challenge'
122122
elif 'r' == action:
123123
return 'ratelimit'
124124
else:

perimeterx/px_config.py

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,16 @@ def __init__(self, config_dict):
1010
module_mode = config_dict.get('module_mode', px_constants.MODULE_MODE_MONITORING)
1111
custom_logo = config_dict.get('custom_logo', None)
1212
testing_mode = config_dict.get('testing_mode', False)
13+
px_backend_host = config_dict.get('px_backend_url', None)
14+
max_buffer_len = config_dict.get('max_buffer_len', 30)
1315
self._px_app_id = app_id
1416
self._blocking_score = config_dict.get('blocking_score', 100)
1517
self._debug_mode = debug_mode
1618
self._module_version = config_dict.get('module_version', px_constants.MODULE_VERSION)
1719
self._module_version = px_constants.MODULE_VERSION.format(' GAE') if os.environ.get('SERVER_SOFTWARE','').startswith('Google') else px_constants.MODULE_VERSION.format('')
1820
self._module_mode = module_mode
19-
self._server_host = 'sapi.perimeterx.net' if app_id is None else px_constants.SERVER_URL.format(app_id.lower())
20-
self._collector_host = 'collector.perimeterx.net' if app_id is None else px_constants.COLLECTOR_URL.format(
21-
app_id.lower())
21+
self._server_host = px_backend_host if px_backend_host else 'sapi.perimeterx.net' if app_id is None else px_constants.SERVER_URL.format(app_id.lower())
22+
self._collector_host = px_backend_host if px_backend_host else 'collector.perimeterx.net' if app_id is None else px_constants.COLLECTOR_URL.format(app_id.lower())
2223
self._encryption_enabled = config_dict.get('encryption_enabled', True)
2324
self._sensitive_headers = map(lambda header: header.lower(),
2425
config_dict.get('sensitive_headers', ['cookie', 'cookies']))
@@ -36,7 +37,7 @@ def __init__(self, config_dict):
3637
self._first_party_xhr_enabled = config_dict.get('first_party_xhr_enabled', True)
3738
self._ip_headers = config_dict.get('ip_headers', [])
3839
self._proxy_url = config_dict.get('proxy_url', None)
39-
self._max_buffer_len = config_dict.get('max_buffer_len', 30)
40+
self._max_buffer_len = max_buffer_len if max_buffer_len > 0 else 1
4041
self._bypass_monitor_header = config_dict.get('bypass_monitor_header','')
4142

4243
sensitive_routes = config_dict.get('sensitive_routes', [])
@@ -55,7 +56,6 @@ def __init__(self, config_dict):
5556
self._enforced_specific_routes = enforced_routes
5657

5758
self._block_html = 'BLOCK'
58-
self._logo_visibility = 'visible' if custom_logo is not None else 'hidden'
5959
self._telemetry_config = self.__create_telemetry_config()
6060
self._testing_mode = testing_mode
6161
self._auth_token = config_dict.get('auth_token', None)
@@ -169,10 +169,6 @@ def whitelist_routes(self):
169169
def block_html(self):
170170
return self._block_html
171171

172-
@property
173-
def logo_visibility(self):
174-
return self._logo_visibility
175-
176172
@property
177173
def additional_activity_handler(self):
178174
return self._additional_activity_handler

perimeterx/px_constants.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
RATELIMIT_TEMPLATE = 'ratelimit.mustache'
1818
CLIENT_HOST = 'client.perimeterx.net'
1919
CAPTCHA_HOST = 'captcha.px-cdn.net'
20+
ALT_CAPTCHA_HOST = 'captcha.px-cloud.net'
2021
COLLECTOR_URL = 'collector-{}.perimeterx.net'
2122
SERVER_URL = 'sapi-{}.perimeterx.net'
2223
CLIENT_FP_PATH = 'init.js'
@@ -30,7 +31,7 @@
3031
EMPTY_GIF_B64 = 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
3132
COLLECTOR_HOST = 'collector.perimeterx.net'
3233
FIRST_PARTY_FORWARDED_FOR = 'X-FORWARDED-FOR'
33-
MODULE_VERSION = 'Python WSGI Module{} v3.2.1'
34+
MODULE_VERSION = 'Python WSGI Module{} v3.3.0'
3435
API_RISK = '/api/v3/risk'
3536
PAGE_REQUESTED_ACTIVITY = 'page_requested'
3637
BLOCK_ACTIVITY = 'block'
@@ -41,3 +42,4 @@
4142
ACTION_BLOCK = 'b'
4243
ACTION_RATELIMIT = 'r'
4344
ACTION_CAPTCHA = 'c'
45+

perimeterx/px_httpc.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ def send(full_url, body, headers, config, method, raise_error = False):
2323
try:
2424
start = time.time()
2525
if method == 'GET':
26-
response = requests.get(url='https://' + full_url, headers=headers, timeout=config.api_timeout, stream=True)
26+
response = requests.get(url=normalize_url(full_url), headers=headers, timeout=config.api_timeout, stream=True)
2727
else:
28-
response = requests.post(url='https://' + full_url, headers=headers, data=body, timeout=config.api_timeout)
28+
response = requests.post(url=normalize_url(full_url), headers=headers, data=body, timeout=config.api_timeout)
2929

3030
if response.status_code >= 400:
31-
logger.debug('PerimeterX server call failed')
31+
logger.debug('PerimeterX server call failed with status ' + str(response.status_code))
3232
return False
3333
finish = time.time()
3434
request_time = finish - start
@@ -38,3 +38,10 @@ def send(full_url, body, headers, config, method, raise_error = False):
3838
logger.debug('PerimeterX Received Request Exception. Error: {}'.format(err))
3939
if raise_error:
4040
raise err
41+
42+
43+
def normalize_url(url):
44+
if url.startswith("http://") or url.startswith("https://"):
45+
return url
46+
else:
47+
return "https://" + url

perimeterx/px_request_verifier.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111

1212
class PxRequestVerifier(object):
13-
1413
def __init__(self, config):
1514
self.config = config
1615
self.logger = config.logger

0 commit comments

Comments
 (0)