Skip to content
This repository was archived by the owner on Oct 29, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).

## [1.8.0] - 2018-02-25
### Added
- Support for testing blocking flow in monitor mode

### Fixed
- VID validity check

## [1.7.0] - 2018-01-11
### Fixed
- Full refactor of proxy support
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
=============================================================

> Latest stable version: [v1.7.0](https://www.npmjs.com/package/perimeterx-node-core)
> Latest stable version: [v1.8.0](https://www.npmjs.com/package/perimeterx-node-core)

This is a shared base implementation for PerimeterX Express enforcer and future NodeJS enforcers. For a fully functioning implementation example, see the [Node-Express enforcer](https://github.com/PerimeterX/perimeterx-node-express/) implementation.

Expand Down
2 changes: 2 additions & 0 deletions lib/pxconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ PX_DEFAULT.FIRST_PARTY_ENABLED = true;
PX_DEFAULT.FIRST_PARTY_XHR_ENABLED = true;
PX_DEFAULT.TESTING_MODE = false;
PX_DEFAULT.WHITELIST_EXT = [];
PX_DEFAULT.BYPASS_MONITOR_HEADER = '';

class PxConfig {
static init(params, pxClient) {
Expand Down Expand Up @@ -157,6 +158,7 @@ class PxConfig {
PX_DEFAULT.ENRICH_CUSTOM_PARAMETERS = this.configurationsOverriding(PX_DEFAULT, params, 'ENRICH_CUSTOM_PARAMETERS', 'enrichCustomParameters');
PX_DEFAULT.TESTING_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'TESTING_MODE', 'testingMode');
PX_DEFAULT.WHITELIST_EXT = this.configurationsOverriding(PX_DEFAULT, params, 'WHITELIST_EXT', 'whitelistExt');
PX_DEFAULT.BYPASS_MONITOR_HEADER = this.configurationsOverriding(PX_DEFAULT, params, 'BYPASS_MONITOR_HEADER', 'bypassMonitorHeader');
return Object.assign(PX_DEFAULT, PX_INTERNAL);
}

Expand Down
3 changes: 2 additions & 1 deletion lib/pxcontext.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class PxContext {
const userAgent = request.get('user-agent') || request.get('User-Agent') || 'none';
const mobileSdkHeader = 'x-px-authorization';
const mobileSdkOriginalTokenHeader = 'x-px-original-token';
const vidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;

this.cookies = {};
this.score = 0;
Expand Down Expand Up @@ -37,7 +38,7 @@ class PxContext {
this.cookies[key] = request.cookies[key];
} else if (key === '_pxhd') {
this.pxhd = request.cookies[key];
} else if (key === '_pxvid' || key === 'pxvid') {
} else if ((key === '_pxvid' || key === 'pxvid') && vidRegex.test(request.cookies[key])) {
this.vid = request.cookies[key];
this.vidSource = 'vid_cookie';
}
Expand Down
3 changes: 2 additions & 1 deletion lib/pxenforcer.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,8 @@ class PxEnforcer {
}

// If verified, pass the request here
if (verified || pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR) {
const shouldBypassMonitor = pxConfig.BYPASS_MONITOR_HEADER && req.headers[pxConfig.BYPASS_MONITOR_HEADER] === '1';
if (verified || (pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR && !shouldBypassMonitor)) {
return cb();
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "perimeterx-node-core",
"version": "1.7.0",
"version": "1.8.0",
"description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
"main": "index.js",
"scripts": {
Expand Down
99 changes: 98 additions & 1 deletion test/pxenforcer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,103 @@ describe('PX Enforcer - pxenforcer.js', () => {
done();
});
});

it('should bypass monitor mode by header', (done) => {
stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
});
const curParams = Object.assign({
moduleMode: 0,
bypassMonitorHeader: 'x-px-block'
}, params);
req.headers = {
'x-px-block':'1'
};
const reqStub = sinon.stub(req, 'post').callsFake((data, callback) => {
callback(null, {body:'hello buddy'});
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
enforcer = new PxEnforcer(curParams, new PxClient());
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
(response.body.indexOf('Please verify you are a human') > -1).should.equal(true);
reqStub.restore();
done();
});
});
it('should ignore bypass monitor mode by header', (done) => {
stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
});
const curParams = Object.assign({
moduleMode: 0,
bypassMonitorHeader: 'x-px-block'
}, params);
req.headers = {
'x-px-block':'0'
};
const reqStub = sinon.stub(req, 'post').callsFake((data, callback) => {
callback(null, {body:'hello buddy'});
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
enforcer = new PxEnforcer(curParams, new PxClient());
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
done();
});
});
it('should ignore bypass monitor header as its not present', (done) => {
stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
});
const curParams = Object.assign({
moduleMode: 0,
bypassMonitorHeader: 'x-px-block'
}, params);
const reqStub = sinon.stub(req, 'post').callsFake((data, callback) => {
callback(null, { body:'hello buddy'});
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
enforcer = new PxEnforcer(curParams, new PxClient());
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
done();
});
});
it('should ignore bypass monitor header as cookie is valid', (done) => {
stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
data.score = 0;
data.action = 'b';
return callback ? callback(null, data) : '';
});
const curParams = Object.assign({
moduleMode: 0,
bypassMonitorHeader: 'x-px-block'
}, params);
req.headers = {
'x-px-block':'1'
};
const reqStub = sinon.stub(req, 'post').callsFake((data, callback) => {
callback(null, { body:'hello buddy'});
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
enforcer = new PxEnforcer(curParams, new PxClient());
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
done();
});
});
});