Skip to content

Commit 57b4ffe

Browse files
Merge pull request #31 from PerimeterX/release/v2.0.0
Release/v2.0.0 to master
2 parents 07f6112 + e998208 commit 57b4ffe

37 files changed

+796
-444
lines changed

.github/workflows/ci.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: ci
2+
on: pull_request
3+
jobs:
4+
lint:
5+
runs-on: ubuntu-latest
6+
steps:
7+
- name: Checkout code
8+
uses: actions/checkout@v2
9+
- name: Setup Python
10+
uses: actions/setup-python@v4
11+
- name: Install dependencies
12+
run: pip install -r dev-requirements.txt && pip install -r requirements.txt
13+
- name: Run linter
14+
run: pylint -E perimeterx/
15+
unit-test:
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python: [ "3.6", "3.8", "3.x" ]
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v2
23+
- name: Setup Python
24+
uses: actions/setup-python@v4
25+
with:
26+
python-version: ${{ matrix.python }}
27+
- name: Install dependencies
28+
run: pip install -r dev-requirements.txt && pip install -r requirements.txt
29+
- name: Run unit tests
30+
run: python -m unittest discover -s ./test -p 'test*'

.travis.yml

Lines changed: 0 additions & 11 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,23 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](http://keepachangelog.com/)
66
and this project adheres to [Semantic Versioning](http://semver.org/).
77

8+
## [2.0.0] - 2022-10-25
9+
### Changed
10+
- Configuration fields' names align with the spec
11+
- Async activities fields align with the spec
12+
- Risk activity fields align with the spec
13+
- Monitored and enforced routes functionality aligned with the spec
14+
- `px_custom_verification_handler` function return `Response` object instead of data, headers and status
15+
- `px_sensitive_routes_regex`, `px_sensitive_routes`, `px_whitelist_routes_regex` and `px_whitelist_routes` configuration fields should contain the exact path
16+
- Changed `debug_mode` configuration field to `px_logger_severity` and change the expected values to `error` and `debug`
17+
18+
### Fixed
19+
- Remove unnecessary risk api activity fields which might cause a bad request response from the collector
20+
21+
### Added
22+
- Added `request_id` field to the context
23+
- Added implementation for handling s2s_error and enforcer_error.
24+
825
## [1.2.0] - 2022-04-11
926
### Added
1027
- New block page implementation

README.md

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
[PerimeterX](http://www.perimeterx.com) Python 3 Middleware
77
=============================================================
8-
> Latest stable version: [v1.2.0](https://pypi.org/project/perimeterx-python-3-wsgi/)
8+
> Latest stable version: [v2.0.0](https://pypi.org/project/perimeterx-python-3-wsgi/)
99
1010
Table of Contents
1111
-----------------
@@ -30,7 +30,7 @@ Table of Contents
3030
* [Sensitive Headers](#sensitive_headers)
3131
* [IP Headers](#ip_headers)
3232
* [First-Party Enabled](#first_party_enabled)
33-
* [Custom Request Handler](#custom_request_handler)
33+
* [Custom Request Handler](#custom_verification_handler)
3434
* [Additional Activity Handler](#additional_activity_handler)
3535
* [Px Disable Request](#px_disable_request)
3636
* [Test Block Flow on Monitoring Mode](#bypass_monitor_header)
@@ -58,9 +58,9 @@ To use PerimeterX middleware on a specific route follow this example:
5858
from perimeterx.middleware import PerimeterX
5959

6060
px_config = {
61-
'app_id': 'APP_ID',
62-
'cookie_key': 'COOKIE_KEY',
63-
'auth_token': 'AUTH_TOKEN',
61+
'px_app_id': 'APP_ID',
62+
'px_cookie_secret': 'COOKIE_SECRET',
63+
'px_auth_token': 'AUTH_TOKEN',
6464
}
6565
application = get_wsgi_application()
6666
application = PerimeterX(application, px_config)
@@ -81,7 +81,7 @@ A boolean flag to enable/disable the PerimeterX Enforcer.
8181
```python
8282
config = {
8383
...
84-
module_enabled: False
84+
px_module_enabled: False
8585
...
8686
}
8787
```
@@ -98,7 +98,7 @@ Possible values:
9898
```python
9999
config = {
100100
...
101-
module_mode: 'active_blocking'
101+
px_module_mode: 'active_blocking'
102102
...
103103
}
104104
```
@@ -113,7 +113,7 @@ Possible values:
113113
```python
114114
config = {
115115
...
116-
blocking_score: 100
116+
px_blocking_score: 100
117117
...
118118
}
119119
```
@@ -132,16 +132,18 @@ config = {
132132
}
133133
```
134134

135-
#### <a name="debug_mode"></a>Debug Mode
135+
#### <a name="logger_severity"></a>Logger Severity
136136

137-
Enable/disable the debug log messages.
137+
The severity level at which the logger should output logs
138+
'error' - PerimeterX logger will log errors only on fatal events (e.g., uncaught errors)
139+
'debug' - PerimeterX logger will output detailed logs for debugging purposes
138140

139-
**Default:** False
141+
**Default:** 'error'
140142

141143
```python
142144
config = {
143145
...
144-
debug_mode: True
146+
px_logger_severity: 'debug'
145147
...
146148
}
147149
```
@@ -154,7 +156,7 @@ An array of route prefixes that trigger a server call to PerimeterX servers ever
154156
```python
155157
config = {
156158
...
157-
sensitive_routes: ['/login', '/user/checkout']
159+
px_sensitive_routes: ['/login', '/user/checkout']
158160
...
159161
}
160162
```
@@ -168,12 +170,12 @@ An array of regex patterns that trigger a server call to PerimeterX servers ever
168170
```python
169171
config = {
170172
...
171-
sensitive_routes_regex: [r'^/login$', r'^/user']
173+
px_sensitive_routes_regex: [r'^/login$', r'^/user']
172174
...
173175
}
174176
```
175177

176-
#### <a name="whitelist_routes"></a> Whitelist Routes
178+
#### <a name="filtered_routes"></a> Filter By Routes
177179

178180
An array of route prefixes which will bypass enforcement (will never get scored).
179181

@@ -182,7 +184,7 @@ An array of route prefixes which will bypass enforcement (will never get scored)
182184
```python
183185
config = {
184186
...
185-
whitelist_routes: ['/about-us', '/careers']
187+
px_filter_by_route: ['/about-us', '/careers']
186188
...
187189
}
188190
```
@@ -211,7 +213,7 @@ When this property is set, any route which is not added - will be whitelisted.
211213
```python
212214
config = {
213215
...
214-
enforced_specific_routes: ['/profile']
216+
px_enforced_routes: ['/profile']
215217
...
216218
};
217219
```
@@ -226,7 +228,7 @@ When this property is set, any route which is not added - will be whitelisted.
226228
```python
227229
config = {
228230
...
229-
enforced_specific_routes_regex: [r'^/profile$']
231+
px_enforced_routes_regex: [r'^/profile$']
230232
...
231233
};
232234
```
@@ -240,7 +242,7 @@ An array of route prefixes that are always set to be in [monitor mode](#module_m
240242
```python
241243
config = {
242244
...
243-
monitored_specific_routes: ['/profile']
245+
px_monitored_routes: ['/profile']
244246
...
245247
};
246248
```
@@ -254,7 +256,7 @@ An array of regex patterns that are always set to be in [monitor mode](#module_m
254256
```python
255257
config = {
256258
...
257-
monitored_specific_routes_regex: [r'^/profile/me$']
259+
px_monitored_routes_regex: [r'^/profile/me$']
258260
...
259261
};
260262
```
@@ -268,7 +270,7 @@ An array of headers that are not sent to PerimeterX servers on API calls.
268270
```python
269271
config = {
270272
...
271-
sensitive_headers: ['cookie', 'cookies', 'x-sensitive-header']
273+
px_sensitive_headers: ['cookie', 'cookies', 'x-sensitive-header']
272274
...
273275
}
274276
```
@@ -282,7 +284,7 @@ An array of trusted headers that specify an IP to be extracted.
282284
```python
283285
config = {
284286
...
285-
ip_headers: ['x-user-real-ip']
287+
px_ip_headers: ['x-user-real-ip']
286288
...
287289
}
288290
```
@@ -296,12 +298,12 @@ Enable/disable First-Party mode.
296298
```python
297299
config = {
298300
...
299-
first_party: False
301+
px_first_party_enabled: False
300302
...
301303
}
302304
```
303305

304-
#### <a name="custom_request_handler"></a>Custom Request Handler
306+
#### <a name="custom_verification_handler"></a>Custom Verification Handler
305307

306308
A Python function that adds a custom response handler to the request.</br>
307309
You must declare the function before using it in the config.</br>
@@ -313,7 +315,7 @@ The custom function should handle the response (most likely it will create a new
313315
```python
314316
config = {
315317
...
316-
custom_request_handler: custom_request_handler_function,
318+
px_custom_verification_handler: custom_verification_handler_function,
317319
...
318320
}
319321
```
@@ -326,7 +328,7 @@ A Python function that allows interaction with the request data collected by Per
326328
```python
327329
config = {
328330
...
329-
additional_activity_handler: additional_activity_handler_function,
331+
px_additional_activity_handler: additional_activity_handler_function,
330332
...
331333
}
332334
```
@@ -362,14 +364,14 @@ Allows you to test an enforcer’s blocking flow while you are still in Monitor
362364
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.
363365
To stay in Monitor Mode, set the header value to `0`.
364366

365-
The Header Name is configurable using the `bypass_monitor_header` property.
367+
The Header Name is configurable using the `px_bypass_monitor_header` property.
366368

367369
**Default:** Empty
368370

369371
```python
370372
config = {
371373
...
372-
bypass_monitor_header: 'x-px-block',
374+
px_bypass_monitor_header: 'x-px-block',
373375
...
374376
}
375377
```

perimeterx/enums/pass_reason.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from enum import Enum
2+
3+
4+
class PassReason(Enum):
5+
COOKIE = 'cookie'
6+
S2S = 's2s'
7+
S2S_TIMEOUT = 's2s_timeout'
8+
S2S_ERROR = 's2s_error'
9+
ENFORCER_ERROR = 'enforcer_error'
10+
11+
def __str__(self):
12+
return str(self.value)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from enum import Enum
2+
3+
4+
class S2SErrorReason(Enum):
5+
NO_ERROR = ''
6+
BAD_REQUEST = 'bad_request'
7+
SERVER_ERROR = 'server_error'
8+
INVALID_RESPONSE = 'invalid_response'
9+
REQUEST_FAILED_ON_SERVER = 'request_failed_on_server'
10+
UNKNOWN_ERROR = 'unknown_error'
11+
12+
def __str__(self):
13+
return str(self.value)

perimeterx/middleware.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1+
import sys
12
import time
23

34
from werkzeug.wrappers import Request
45

56
from perimeterx import px_activities_client
67
from perimeterx import px_utils
8+
from perimeterx.enums.pass_reason import PassReason
9+
from perimeterx.enums.s2s_error_reason import S2SErrorReason
710
from perimeterx.px_config import PxConfig
811
from perimeterx.px_context import PxContext
912
from perimeterx.px_request_verifier import PxRequestVerifier
@@ -24,7 +27,7 @@ def __init__(self, app, config=None):
2427
logger.error('Unable to initialize module, missing mandatory configuration: auth_token')
2528
raise ValueError('PX Auth Token is missing')
2629

27-
if not px_config.cookie_key:
30+
if not px_config.cookie_secret:
2831
logger.error('Unable to initialize module, missing mandatory configuration: px_cookie')
2932
raise ValueError('PX Cookie Key is missing')
3033

@@ -52,6 +55,7 @@ def __call__(self, environ, start_response):
5255
return verified_response(environ, pxhd_callback)
5356

5457
except Exception as err:
58+
self._config.logger.error(generate_exception())
5559
self._config.logger.error("Caught exception, passing request. Exception: {}".format(err))
5660
if context:
5761
self.report_pass_traffic(context)
@@ -65,14 +69,18 @@ def verify(self, request):
6569
logger.debug("Starting request verification {}".format(request.path))
6670
ctx = None
6771
try:
68-
if not config._module_enabled:
72+
if not config.module_enabled:
6973
logger.debug('Request will not be verified, module is disabled')
7074
return ctx, True
7175
ctx = PxContext(request, config)
7276
return ctx, self._request_verifier.verify_request(ctx, request)
7377
except Exception as err:
7478
logger.error("Caught exception in verify, passing request. Exception: {}".format(err))
7579
if ctx:
80+
if ctx.s2s_error_reason == str(S2SErrorReason.NO_ERROR):
81+
ctx.pass_reason = str(PassReason.ENFORCER_ERROR)
82+
ctx.error_message = generate_exception()
83+
7684
self.report_pass_traffic(ctx)
7785
else:
7886
self.report_pass_traffic(PxContext(Request({}), config))
@@ -97,3 +105,11 @@ def custom_start_response(status, headers, exc_info=None):
97105
return start_response(status, headers, exc_info)
98106

99107
return custom_start_response
108+
109+
110+
def generate_exception():
111+
exc_type, exc_obj, tb = sys.exc_info()
112+
f = tb.tb_frame
113+
lineno = tb.tb_lineno
114+
filename = f.f_code.co_filename
115+
return 'EXCEPTION IN ({}, LINE {}): {}'.format(filename, lineno, exc_obj)

0 commit comments

Comments
 (0)