Skip to content

Commit 0a4612e

Browse files
author
Johnny Tordgeman
authored
Merge pull request #72 from PerimeterX/dev
Version 3.2.0
2 parents acf8db4 + 6c8a903 commit 0a4612e

File tree

10 files changed

+86
-9
lines changed

10 files changed

+86
-9
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.2.0](https://github.com/PerimeterX/perimeterx-python-wsgi) (2019-03-17)
4+
- Added support for enforced_specific_routes
5+
- Added .json to the list of whitelisted extensions
6+
37
## [v3.1.0](https://github.com/PerimeterX/perimeterx-python-wsgi) (2019-02-26)
48
- Refactor of px_logger to use native python logger
59
- Added support for bypass monitor mode header

README.md

Lines changed: 17 additions & 3 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.1.0](https://pypi.org/project/perimeterx-python-wsgi/)
7+
> Latest stable version: [v3.2.0](https://pypi.org/project/perimeterx-python-wsgi/)
88
9-
> Latest GAE stable version: [v3.1.0](https://pypi.org/project/perimeterx-python-wsgi-gae/)
9+
> Latest GAE stable version: [v3.2.0](https://pypi.org/project/perimeterx-python-wsgi-gae/)
1010
1111
Table of Contents
1212
-----------------
@@ -29,6 +29,7 @@ Table of Contents
2929
* [Additional Activity Handler](#additional_activity_handler)
3030
* [Px Disable Request](#px_disable_request)
3131
* [Test Block Flow on Monitoring Mode](#bypass_monitor_header)
32+
* [Enforce Specific Routes](#enforce_specific_routes)
3233

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

@@ -255,7 +256,7 @@ environ['px_disable_request'] = True #The enforcer shall be disabled for that re
255256

256257
```
257258

258-
#### <a name=“bypassMonitorHeader”></a> Test Block Flow on Monitoring Mode
259+
#### <a name="bypass_monitor_header"></a> Test Block Flow on Monitoring Mode
259260

260261
Allows you to test an enforcer’s blocking flow while you are still in Monitor Mode.
261262

@@ -273,3 +274,16 @@ config = {
273274
...
274275
}
275276
```
277+
278+
#### <a name="enforce_specific_routes"></a> Enforced Specific Routes
279+
An array of route prefixes that are always validated by the PerimeterX Worker (as opposed to whitelisted routes).
280+
When this property is set, any route which is not added - will be whitelisted.
281+
282+
**Default:** Empty
283+
```python
284+
config = {
285+
...
286+
enforced_specific_routes: ['/profile']
287+
...
288+
};
289+
```

perimeterx/px_config.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,13 @@ def __init__(self, config_dict):
4747
whitelist_routes = config_dict.get('whitelist_routes', [])
4848
if not isinstance(whitelist_routes, list):
4949
raise TypeError('whitelist_routes must be a list')
50-
5150
self._whitelist_routes = whitelist_routes
51+
52+
enforced_routes = config_dict.get('enforced_specific_routes', [])
53+
if not isinstance(enforced_routes, list):
54+
raise TypeError('enforced_specific_routes must be a list')
55+
self._enforced_specific_routes = enforced_routes
56+
5257
self._block_html = 'BLOCK'
5358
self._logo_visibility = 'visible' if custom_logo is not None else 'hidden'
5459
self._telemetry_config = self.__create_telemetry_config()
@@ -196,6 +201,10 @@ def testing_mode(self):
196201
def bypass_monitor_header(self):
197202
return self._bypass_monitor_header
198203

204+
@property
205+
def enforced_specific_routes(self):
206+
return self._enforced_specific_routes
207+
199208
def __instantiate_user_defined_handlers(self, config_dict):
200209
self._custom_request_handler = self.__set_handler('custom_request_handler', config_dict)
201210
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.1.0'
33+
MODULE_VERSION = 'Python WSGI Module{} v3.2.0'
3434
API_RISK = '/api/v3/risk'
3535
PAGE_REQUESTED_ACTIVITY = 'page_requested'
3636
BLOCK_ACTIVITY = 'block'

perimeterx/px_context.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def __init__(self, request, config):
5353
filter(lambda sensitive_route_item: uri.startswith(sensitive_route_item), config.sensitive_routes)) > 0
5454
whitelist_route = len(
5555
filter(lambda whitelist_route_item: uri.startswith(whitelist_route_item), config.whitelist_routes)) > 0
56+
enforced_route = len(
57+
filter(lambda enforced_route_item: uri.startswith(enforced_route_item), config.enforced_specific_routes)) > 0
5658

5759
protocol_split = request.environ.get('SERVER_PROTOCOL', '').split('/')
5860
if protocol_split[0].startswith('HTTP'):
@@ -78,6 +80,7 @@ def __init__(self, request, config):
7880
self._query_params = request.query_string
7981
self._sensitive_route = sensitive_route
8082
self._whitelist_route = whitelist_route
83+
self._enforced_route = enforced_route
8184
self._s2s_call_reason = 'none'
8285
self._cookie_origin = cookie_origin
8386
self._is_mobile = cookie_origin == "header"
@@ -123,6 +126,14 @@ def extract_ip(self, config, request):
123126
ip = config.get_user_ip
124127
return ip
125128

129+
@property
130+
def enforced_route(self):
131+
return self._enforced_route
132+
133+
@enforced_route.setter
134+
def enforced_route(self, enforced_route):
135+
self._enforced_route = enforced_route
136+
126137
@property
127138
def headers(self):
128139
return self._headers

perimeterx/px_request_verifier.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ def verify_request(self, ctx, request):
2727
if ctx.whitelist_route:
2828
self.logger.debug('The requested uri is whitelisted, passing request')
2929
return True
30+
if len(self.config.enforced_specific_routes) > 0 and not ctx.enforced_route:
31+
self.logger.debug('The request uri {} is not listed in specific routes to enforce, passing request.'.format(uri))
32+
return True
3033
# PX Cookie verification
3134
if not px_cookie_validator.verify(ctx, self.config):
3235
# Server-to-Server verification fallback

perimeterx/px_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ def is_static_file(ctx):
2828
static_extensions = ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot',
2929
'.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx',
3030
'.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png',
31-
'.ppt', '.mid', 'webp', '.jar']
31+
'.ppt', '.mid', 'webp', '.jar', '.json']
3232

3333
for ext in static_extensions:
3434
if uri.endswith(ext):

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.1.0'
5+
version = 'v3.2.0'
66
setup(name='perimeterx-python-wsgi-gae',
77
version=version,
88
license='MIT',

setup.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.1.0'
5+
version = 'v3.2.0'
66
setup(name='perimeterx-python-wsgi',
77
version=version,
88
license='MIT',

test/test_px_request_validator.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,4 +159,40 @@ def test_bypass_monitor_header_on_valid_request(self):
159159
context = PxContext(request, request_handler.config)
160160
context.score = 0
161161
response = request_handler.handle_verification(context, request)
162-
self.assertEqual(response, True)
162+
self.assertEqual(response, True)
163+
164+
def test_specific_enforced_routes_with_enforced_route(self):
165+
config = PxConfig({'app_id': 'PXfake_app_id',
166+
'auth_token': '',
167+
'module_mode': px_constants.MODULE_MODE_BLOCKING,
168+
'enforced_specific_routes': ['/profile'],
169+
});
170+
headers = {'X-FORWARDED-FOR': '127.0.0.1',
171+
'remote-addr': '127.0.0.1',
172+
'content_length': '100'}
173+
request_handler = PxRequestVerifier(config)
174+
builder = EnvironBuilder(headers=headers, path='/profile')
175+
env = builder.get_environ()
176+
request = Request(env)
177+
context = PxContext(request, request_handler.config)
178+
context.score = 100
179+
response = request_handler.handle_verification(context, request)
180+
self.assertEqual(response.status, '403 Forbidden')
181+
182+
def test_specific_enforced_routes_with_unenforced_route(self):
183+
config = PxConfig({'app_id': 'PXfake_app_id',
184+
'auth_token': '',
185+
'module_mode': px_constants.MODULE_MODE_BLOCKING,
186+
'enforced_specific_routes': ['/profile'],
187+
});
188+
headers = {'X-FORWARDED-FOR': '127.0.0.1',
189+
'remote-addr': '127.0.0.1',
190+
'content_length': '100'}
191+
request_handler = PxRequestVerifier(config)
192+
builder = EnvironBuilder(headers=headers, path='/')
193+
env = builder.get_environ()
194+
request = Request(env)
195+
context = PxContext(request, request_handler.config)
196+
context.score = 100
197+
response = request_handler.verify_request(context, request)
198+
self.assertEqual(response, True)

0 commit comments

Comments
 (0)