Skip to content

Commit acf8db4

Browse files
author
Johnny Tordgeman
authored
Merge pull request #70 from PerimeterX/dev
Version 3.1.0
2 parents 3c7a588 + 96e6613 commit acf8db4

File tree

12 files changed

+146
-17
lines changed

12 files changed

+146
-17
lines changed

CHANGELOG.md

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

3+
## [v3.1.0](https://github.com/PerimeterX/perimeterx-python-wsgi) (2019-02-26)
4+
- Refactor of px_logger to use native python logger
5+
- Added support for bypass monitor mode header
6+
37
## [v3.0.2](https://github.com/PerimeterX/perimeterx-python-wsgi) (2019-02-13)
48
- page requested pass_reason alignment
59
- better error handling for http errors

README.md

Lines changed: 21 additions & 2 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.0.2](https://pypi.org/project/perimeterx-python-wsgi/)
7+
> Latest stable version: [v3.1.0](https://pypi.org/project/perimeterx-python-wsgi/)
88
9-
> Latest GAE stable version: [v3.0.2](https://pypi.org/project/perimeterx-python-wsgi-gae/)
9+
> Latest GAE stable version: [v3.1.0](https://pypi.org/project/perimeterx-python-wsgi-gae/)
1010
1111
Table of Contents
1212
-----------------
@@ -28,6 +28,7 @@ Table of Contents
2828
* [Custom Request Handler](#custom_request_handler)
2929
* [Additional Activity Handler](#additional_activity_handler)
3030
* [Px Disable Request](#px_disable_request)
31+
* [Test Block Flow on Monitoring Mode](#bypass_monitor_header)
3132

3233
## <a name="installation"></a> Installation
3334

@@ -254,3 +255,21 @@ environ['px_disable_request'] = True #The enforcer shall be disabled for that re
254255

255256
```
256257

258+
#### <a name=“bypassMonitorHeader”></a> Test Block Flow on Monitoring Mode
259+
260+
Allows you to test an enforcer’s blocking flow while you are still in Monitor Mode.
261+
262+
When the header name is set(eg. `x-px-block`) and the value is set to `1`, when there is a block response (for example from using a User-Agent header with the value of `PhantomJS/1.0`) the Monitor Mode is bypassed and full block mode is applied. If one of the conditions is missing you will stay in Monitor Mode. This is done per request.
263+
To stay in Monitor Mode, set the header value to `0`.
264+
265+
The Header Name is configurable using the `bypass_monitor_header` property.
266+
267+
**Default:** Empty
268+
269+
```python
270+
config = {
271+
...
272+
bypass_monitor_header: 'x-px-block',
273+
...
274+
}
275+
```

perimeterx/px_blocker.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ def handle_blocking(self, ctx, config):
3535
headers = {'Content-Type': content_type}
3636

3737
if action is px_constants.ACTION_CHALLENGE:
38-
logger.debug('Challenge page is served')
38+
logger.debug('Enforcing action: Challenge page is served')
3939
blocking_props = ctx.block_action_data
4040
blocking_response = blocking_props
4141

4242
elif action is px_constants.ACTION_RATELIMIT:
43-
logger.debug('Rate limit page is served')
43+
logger.debug('Enforcing action: Rate limit page is served')
4444
blocking_props = None
4545
blocking_response = self.ratelimit_rendered_page
4646
status = '429 Too Many Requests'
4747

4848
else: # block
49-
logger.debug('Block page is served')
49+
logger.debug('Enforcing action: Block page is served')
5050
blocking_props = self.prepare_properties(ctx, config)
5151
blocking_response = self.mustache_renderer.render(px_template.get_template(px_constants.BLOCK_TEMPLATE),
5252
blocking_props)
@@ -63,6 +63,7 @@ def handle_blocking(self, ctx, config):
6363
return page_response, headers, status
6464

6565
if is_json_response:
66+
logger.debug('Serving advanced blocking response')
6667
blocking_response = json.dumps(blocking_props)
6768

6869
blocking_response = str(blocking_response)

perimeterx/px_config.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ def __init__(self, config_dict):
3737
self._ip_headers = config_dict.get('ip_headers', [])
3838
self._proxy_url = config_dict.get('proxy_url', None)
3939
self._max_buffer_len = config_dict.get('max_buffer_len', 30)
40+
self._bypass_monitor_header = config_dict.get('bypass_monitor_header','')
4041

4142
sensitive_routes = config_dict.get('sensitive_routes', [])
4243
if not isinstance(sensitive_routes, list):
@@ -191,6 +192,10 @@ def enrich_custom_parameters(self):
191192
def testing_mode(self):
192193
return self._testing_mode
193194

195+
@property
196+
def bypass_monitor_header(self):
197+
return self._bypass_monitor_header
198+
194199
def __instantiate_user_defined_handlers(self, config_dict):
195200
self._custom_request_handler = self.__set_handler('custom_request_handler', config_dict)
196201
self._get_user_ip = self.__set_handler('get_user_ip', config_dict)

perimeterx/px_constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
EMPTY_GIF_B64 = 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='
3131
COLLECTOR_HOST = 'collector.perimeterx.net'
3232
FIRST_PARTY_FORWARDED_FOR = 'X-FORWARDED-FOR'
33-
MODULE_VERSION = 'Python WSGI Module{} v3.0.2'
33+
MODULE_VERSION = 'Python WSGI Module{} v3.1.0'
3434
API_RISK = '/api/v3/risk'
3535
PAGE_REQUESTED_ACTIVITY = 'page_requested'
3636
BLOCK_ACTIVITY = 'block'

perimeterx/px_cookie_validator.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def verify(ctx, config):
1919
logger = config.logger
2020
try:
2121
if not ctx.px_cookies.keys():
22-
logger.debug('No risk cookie on the request')
22+
logger.debug('Cookie is missing')
2323
ctx.s2s_call_reason = 'no_cookie'
2424
return False
2525

@@ -46,7 +46,7 @@ def verify(ctx, config):
4646

4747
if not px_cookie.deserialize():
4848
cookie = px_cookie._hmac + ":" + px_cookie._raw_cookie
49-
logger.error('Cookie decryption failed, value: {}'.format(cookie))
49+
logger.debug('Cookie decryption failed, value: {}'.format(cookie))
5050
ctx.px_cookie_raw = cookie_version + "=" + cookie
5151
ctx.s2s_call_reason = 'cookie_decryption_failed'
5252
return False
@@ -67,7 +67,7 @@ def verify(ctx, config):
6767

6868
if px_cookie.is_cookie_expired():
6969
ctx.s2s_call_reason = 'cookie_expired'
70-
msg = 'Cookie TTL expired, value: {}, age: {}'
70+
msg = 'Cookie TTL is expired, value: {}, age: {}'
7171
logger.debug(msg.format(px_cookie.decoded_cookie, px_cookie.get_age()))
7272
return False
7373

@@ -86,7 +86,7 @@ def verify(ctx, config):
8686
return True
8787
except Exception, err:
8888
traceback.print_exc()
89-
logger.debug('Unexpected exception while evaluating Risk cookie. Error: {}'.format(err))
89+
logger.error('Unexpected exception while evaluating Risk cookie. Error: {}'.format(err))
9090
ctx.px_cookie_raw = px_cookie._raw_cookie
9191
ctx.s2s_call_reason = 'cookie_decryption_failed'
9292
return False

perimeterx/px_logger.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1+
import logging
2+
13
class Logger(object):
24
def __init__(self, debug, app_id):
35
self.debug_mode = debug
46
self.app_id = app_id
57

8+
# Setup logger
9+
self.logger = logging.getLogger(__name__)
10+
handler = logging.StreamHandler()
11+
formatter = logging.Formatter('[PerimeterX %(levelname)s][{}]: %(message)s'.format(self.app_id))
12+
handler.setFormatter(formatter)
13+
self.logger.addHandler(handler)
14+
self.logger.setLevel(logging.DEBUG)
15+
616
def debug(self, message):
7-
if self.debug_mode:
8-
print '[PerimeterX DEBUG][{}]: '.format(self.app_id) + message
17+
if self.debug_mode:
18+
self.logger.debug(message)
919

1020
def error(self, message):
11-
print '[PerimeterX ERROR][{}]: '.format(self.app_id) + message
21+
self.logger.error(message)

perimeterx/px_original_token_validator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def verify(ctx, config):
1717
cookie_version, px_cookie = px_cookie_builder.build_px_cookie({version: no_version_token}, '')
1818

1919
if not px_cookie.deserialize():
20-
logger.error('Original token decryption failed, value: {}'.format(px_cookie.raw_cookie))
20+
logger.debug('Original token decryption failed, value: {}'.format(px_cookie.raw_cookie))
2121
ctx.original_token_error = 'decryption_failed'
2222
return False
2323

perimeterx/px_request_verifier.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ def handle_verification(self, ctx, request):
5050
else:
5151
logger.debug('Risk score is higher or equal than blocking score')
5252
self.report_block_traffic(ctx)
53+
should_bypass_monitor = config.bypass_monitor_header and ctx.headers.get(config.bypass_monitor_header) == '1';
5354
if config.additional_activity_handler:
5455
config.additional_activity_handler(ctx, config)
55-
if config.module_mode == px_constants.MODULE_MODE_BLOCKING:
56+
if config.module_mode == px_constants.MODULE_MODE_BLOCKING or should_bypass_monitor:
5657
data, headers, status = self.px_blocker.handle_blocking(ctx=ctx, config=config)
5758
response_function = generate_blocking_response(data, headers, status)
5859
else:

setup-gae.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from setuptools import setup, find_packages
44

5-
version = 'v3.0.2'
5+
version = 'v3.1.0'
66
setup(name='perimeterx-python-wsgi-gae',
77
version=version,
88
license='MIT',

0 commit comments

Comments
 (0)