diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bef7351..71a57fb2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,17 @@ 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
+## [2.0.0] - 2019-03-14
+### Added
+- Support for multiple instances of PxEnforcer (for multi px-app in same web app)
+
+### Refactored
+- Major parts of the code to inject an instance of PxLogger and PX config.
+
+### Changed
+- Changed PxClient.submitActivities() signature to receive a config object.
+
+## [1.8.0] - 2019-02-25
### Added
- Support for testing blocking flow in monitor mode
diff --git a/README.md b/README.md
index 2798d94a..91e18a20 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
[PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
=============================================================
-> Latest stable version: [v1.8.0](https://www.npmjs.com/package/perimeterx-node-core)
+> Latest stable version: [v2.0.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.
@@ -28,9 +28,9 @@ Table of Contents
### Basic Usage Example
To integrate this module into an enforcer, users should initialize the enforcer.
```javascript
-function initPXModule(params) {
+function initPXModule(params, client) {
params.moduleVersion = '';
- enforcer = new PxEnforcer(params);
+ enforcer = new PxEnforcer(params, client);
//if dynamic configurations is configured
if (enforcer.config.conf.DYNAMIC_CONFIGURATIONS) {
setInterval(enforcer.config.confManager.loadData.bind(enforcer.config.confManager), enforcer.config.conf.CONFIGURATION_LOAD_INTERVAL);
@@ -64,9 +64,9 @@ Extend the `PxClient` class to send activities to PerimeterX.
```javascript
const PxClient = require('perimeterx-node-core').PxClient;
class MyClient extends PxClient {
- init() {
+ init(config) {
setInterval(() => {
- this.submitActivities();
+ this.submitActivities(config);
}, 1000);
}
}
@@ -77,7 +77,7 @@ Make sure to pass the client instance when initializing the enforcer.
```javascript
function initPXModule(params) {
params.moduleVersion = '';
- let pxClient = new MyClient();
+ const pxClient = new MyClient();
enforcer = new PxEnforcer(params, pxClient);
//if dynamic configurations is configured
if (enforcer.config.conf.DYNAMIC_CONFIGURATIONS) {
diff --git a/index.js b/index.js
index cc807570..c04c1402 100644
--- a/index.js
+++ b/index.js
@@ -26,5 +26,4 @@
module.exports = {
PxEnforcer: require('./lib/pxenforcer'),
PxClient: require('./lib/pxclient'),
- request: require('./lib/request')
};
\ No newline at end of file
diff --git a/lib/configloader.js b/lib/configloader.js
index 008bfc6a..01e8e129 100644
--- a/lib/configloader.js
+++ b/lib/configloader.js
@@ -1,10 +1,10 @@
-
-const logger = require('./pxlogger');
+const request = require('./request');
class ConfigLoader {
- constructor(config, pxClient) {
- this.config = config;
+ constructor(pxConfig, pxClient) {
+ this.pxConfig = pxConfig;
+ this.config = pxConfig.conf;
this.pxClient = pxClient;
}
@@ -13,18 +13,17 @@ class ConfigLoader {
}
loadData() {
- const request = require('./request');
const checksum = this.config.checksum;
const callData = {
'url': `https://${this.config.CONFIGURATIONS_HOST}${this.config.CONFIGURATIONS_URI + (checksum ? `?checksum=${checksum}` : '')}`,
'headers': {Authorization: 'Bearer ' + this.config.AUTH_TOKEN},
'timeout': this.config.API_TIMEOUT_MS
};
- request.get(callData, (error, response) => {
+ request.get(callData, this.config, (error, response) => {
if (error || !response || !(response.statusCode === 200 || response.statusCode === 204)) {
- logger.error(`Failed to get configurations: ${error}`);
+ this.config.logger.error(`Failed to get configurations: ${error}`);
if (!checksum) { //no configuration loaded and we can't get configuration - disable module
- logger.debug('Failed to pull initial config, switching module to disable until remote configuration found');
+ this.config.logger.debug('Failed to pull initial config, switching module to disable until remote configuration found');
this.config.ENABLE_MODULE = false;
}
return;
@@ -33,7 +32,7 @@ class ConfigLoader {
// new configuration available
if (response.statusCode === 200) {
const body = JSON.parse(response.body.toString());
- logger.debug(`Found new configuration - checksum: ${body.checksum}, new configuration: ${JSON.stringify(body)}`);
+ this.config.logger.debug(`Found new configuration - checksum: ${body.checksum}, new configuration: ${JSON.stringify(body)}`);
this.config.checksum = body.checksum;
this.config.COOKIE_SECRET_KEY = body.cookieKey;
this.config.PX_APP_ID = body.appId;
@@ -47,7 +46,7 @@ class ConfigLoader {
this.config.MODULE_MODE = body.moduleMode === 'blocking' ? this.config.MONITOR_MODE.BLOCK : this.config.MONITOR_MODE.MONITOR;
this.config.FIRST_PARTY_ENABLED = body.firstPartyEnabled;
this.config.FIRST_PARTY_XHR_ENABLED = body.firstPartyXhrEnabled;
- this.pxClient.sendEnforcerTelemetry('remote_config');
+ this.pxClient.sendEnforcerTelemetry('remote_config', this.config);
}
});
}
diff --git a/lib/cookie/cookieV1.js b/lib/cookie/cookieV1.js
index fef789f0..a65fbe05 100644
--- a/lib/cookie/cookieV1.js
+++ b/lib/cookie/cookieV1.js
@@ -5,10 +5,9 @@ const Payload = require('../pxpayload');
class CookieV1 extends Payload {
constructor(ctx, config) {
- super();
+ super(config);
this.pxCookie = ctx.cookies['_px'];
- this.pxConfig = config;
- this.pxContext = ctx;
+ this.ctx = ctx;
this.cookieSecret = config.COOKIE_SECRET_KEY;
}
@@ -33,10 +32,10 @@ class CookieV1 extends Payload {
const baseHmacStr = '' + this.getTime() + this.decodedCookie.s.a + this.getScore() + this.getUuid() + this.getVid();
// hmac string with IP - for backward support
- const hmacWithIp = baseHmacStr + this.pxContext.ip + this.pxContext.userAgent;
+ const hmacWithIp = baseHmacStr + this.ctx.ip + this.ctx.userAgent;
// hmac string without IP
- const hmacWithoutIp = baseHmacStr + this.pxContext.userAgent;
+ const hmacWithoutIp = baseHmacStr + this.ctx.userAgent;
return this.isHmacValid(hmacWithoutIp, this.getHmac()) || this.isHmacValid(hmacWithIp, this.getHmac());
}
diff --git a/lib/cookie/cookieV3.js b/lib/cookie/cookieV3.js
index cb705034..bdcfc003 100644
--- a/lib/cookie/cookieV3.js
+++ b/lib/cookie/cookieV3.js
@@ -4,13 +4,12 @@ const Payload = require('../pxpayload');
class CookieV3 extends Payload {
constructor(ctx, config) {
- super();
+ super(config);
let [hash, ...cookie] = ctx.cookies['_px3'].split(':');
cookie = cookie.join(':');
this.pxCookie = cookie;
this.cookieHash = hash;
- this.pxConfig = config;
- this.pxContext = ctx;
+ this.ctx = ctx;
this.cookieSecret = config.COOKIE_SECRET_KEY;
}
@@ -31,7 +30,7 @@ class CookieV3 extends Payload {
}
isSecure() {
- const hmacStr = this.pxCookie + this.pxContext.userAgent;
+ const hmacStr = this.pxCookie + this.ctx.userAgent;
return this.isHmacValid(hmacStr, this.getHmac());
}
}
diff --git a/lib/cookie/tokenV1.js b/lib/cookie/tokenV1.js
index f3598288..78bf1b12 100644
--- a/lib/cookie/tokenV1.js
+++ b/lib/cookie/tokenV1.js
@@ -5,10 +5,9 @@ const Payload = require('../pxpayload');
class TokenV1 extends Payload {
constructor(ctx, config, token) {
- super();
+ super(config);
this.pxCookie = token;
- this.pxConfig = config;
- this.pxContext = ctx;
+ this.ctx = ctx;
this.cookieSecret = config.COOKIE_SECRET_KEY;
}
@@ -33,7 +32,7 @@ class TokenV1 extends Payload {
const baseHmacStr = '' + this.getTime() + this.decodedCookie.s.a + this.getScore() + this.getUuid() + this.getVid();
// hmac string with IP - for backward support
- const hmacWithIp = baseHmacStr + this.pxContext.ip;
+ const hmacWithIp = baseHmacStr + this.ctx.ip;
// hmac string without IP
const hmacWithoutIp = baseHmacStr;
diff --git a/lib/cookie/tokenV3.js b/lib/cookie/tokenV3.js
index 8e3b71d6..13ba0346 100644
--- a/lib/cookie/tokenV3.js
+++ b/lib/cookie/tokenV3.js
@@ -4,13 +4,12 @@ const Payload = require('../pxpayload');
class TokenV3 extends Payload {
constructor(ctx, config, token) {
- super();
+ super(config);
let [hash, ...cookie] = token.split(':');
cookie = cookie.join(':');
this.pxCookie = cookie;
this.cookieHash = hash;
- this.pxConfig = config;
- this.pxContext = ctx;
+ this.ctx = ctx;
this.cookieSecret = config.COOKIE_SECRET_KEY;
}
diff --git a/lib/pxapi.js b/lib/pxapi.js
index 37c75da1..d20b9307 100644
--- a/lib/pxapi.js
+++ b/lib/pxapi.js
@@ -1,28 +1,25 @@
'use strict';
const pxUtil = require('./pxutil');
const pxHttpc = require('./pxhttpc');
-const pxLogger = require('./pxlogger');
-const pxConfig = require('./pxconfig');
exports.evalByServerCall = evalByServerCall;
/**
* callServer - call the perimeterx api server to receive a score for a given user.
*
- * @param {Object} pxCtx - current request context
+ * @param {Object} ctx - current request context
* @param {Function} callback - callback function.
*/
-function callServer(pxCtx, callback) {
- const config = pxConfig.conf;
- const ip = pxCtx.ip;
- const fullUrl = pxCtx.fullUrl;
- const vid = pxCtx.vid || '';
- const pxhd = pxCtx.pxhd || '';
- const vidSource = pxCtx.vidSource || '';
- const uuid = pxCtx.uuid || '';
- const uri = pxCtx.uri || '/';
- const headers = pxUtil.formatHeaders(pxCtx.headers);
- const httpVersion = pxCtx.httpVersion;
+function callServer(ctx, config, callback) {
+ const ip = ctx.ip;
+ const fullUrl = ctx.fullUrl;
+ const vid = ctx.vid || '';
+ const pxhd = ctx.pxhd || '';
+ const vidSource = ctx.vidSource || '';
+ const uuid = ctx.uuid || '';
+ const uri = ctx.uri || '/';
+ const headers = pxUtil.formatHeaders(ctx.headers, config.SENSITIVE_HEADERS);
+ const httpVersion = ctx.httpVersion;
const riskMode = config.MODULE_MODE === config.MONITOR_MODE.MONITOR ? 'monitor' : 'active_blocking';
const data = {
@@ -34,22 +31,22 @@ function callServer(pxCtx, callback) {
firstParty: config.FIRST_PARTY_ENABLED
},
additional: {
- s2s_call_reason: pxCtx.s2sCallReason,
+ s2s_call_reason: ctx.s2sCallReason,
http_version: httpVersion,
- http_method: pxCtx.httpMethod,
+ http_method: ctx.httpMethod,
risk_mode: riskMode,
module_version: config.MODULE_VERSION,
- cookie_origin: pxCtx.cookieOrigin,
- request_cookie_names: pxCtx.requestCookieNames
+ cookie_origin: ctx.cookieOrigin,
+ request_cookie_names: ctx.requestCookieNames
}
};
- if (pxCtx.s2sCallReason === 'cookie_decryption_failed') {
- data.additional.px_orig_cookie = pxCtx.getCookie(); //No need strigify, already a string
+ if (ctx.s2sCallReason === 'cookie_decryption_failed') {
+ data.additional.px_orig_cookie = ctx.getCookie(); //No need strigify, already a string
}
- if (pxCtx.s2sCallReason === 'cookie_expired' || pxCtx.s2sCallReason === 'cookie_validation_failed') {
- data.additional.px_cookie = JSON.stringify(pxCtx.decodedCookie);
+ if (ctx.s2sCallReason === 'cookie_expired' || ctx.s2sCallReason === 'cookie_validation_failed') {
+ data.additional.px_cookie = JSON.stringify(ctx.decodedCookie);
}
pxUtil.prepareCustomParams(config, data.additional);
@@ -74,59 +71,57 @@ function callServer(pxCtx, callback) {
if (pxhd && data.additional.s2s_call_reason === 'no_cookie') {
data.additional.s2s_call_reason = 'no_cookie_w_vid';
}
- if (pxCtx.originalUuid) {
- data.additional['original_uuid'] = pxCtx.originalUuid;
+ if (ctx.originalUuid) {
+ data.additional['original_uuid'] = ctx.originalUuid;
}
- if (pxCtx.originalTokenError) {
- data.additional['original_token_error'] = pxCtx.originalTokenError;
+ if (ctx.originalTokenError) {
+ data.additional['original_token_error'] = ctx.originalTokenError;
}
- if (pxCtx.originalToken) {
- data.additional['original_token'] = pxCtx.originalToken;
+ if (ctx.originalToken) {
+ data.additional['original_token'] = ctx.originalToken;
}
- if (pxCtx.decodedOriginalToken) {
- data.additional['px_decoded_original_token'] = pxCtx.decodedOriginalToken;
+ if (ctx.decodedOriginalToken) {
+ data.additional['px_decoded_original_token'] = ctx.decodedOriginalToken;
}
- if(pxCtx.hmac) {
- data.additional['px_cookie_hmac'] = pxCtx.hmac;
+ if(ctx.hmac) {
+ data.additional['px_cookie_hmac'] = ctx.hmac;
}
- pxCtx.hasMadeServerCall = true;
- return pxHttpc.callServer(data, reqHeaders, config.SERVER_TO_SERVER_API_URI, 'query', callback);
+ ctx.hasMadeServerCall = true;
+ return pxHttpc.callServer(data, reqHeaders, config.SERVER_TO_SERVER_API_URI, 'query', config, callback);
}
/**
* evalByServerCall - main server to server function, execute a server call for score and process its value to make blocking decisions.
* '
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
* @param {Function} callback - callback function.
*/
-function evalByServerCall(pxCtx, callback) {
-
- const config = pxConfig.conf;
- if (!pxCtx.ip || !pxCtx.headers) {
- pxLogger.error('perimeterx score evaluation failed. bad parameters.');
+function evalByServerCall(ctx, config, callback) {
+ if (!ctx.ip || !ctx.headers) {
+ config.logger.error('perimeterx score evaluation failed. bad parameters.');
return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
}
- pxLogger.debug(`Evaluating Risk API request, call reason: ${pxCtx.s2sCallReason}`);
- callServer(pxCtx, (err, res) => {
+ config.logger.debug(`Evaluating Risk API request, call reason: ${ctx.s2sCallReason}`);
+ callServer(ctx, config, (err, res) => {
if (err) {
if (err === 'timeout') {
- pxCtx.passReason = config.PASS_REASON.S2S_TIMEOUT;
+ ctx.passReason = config.PASS_REASON.S2S_TIMEOUT;
return callback(config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
}
- pxLogger.error(`Unexpected exception while evaluating Risk cookie. ${err}`);
- pxCtx.passReason = pxConfig.conf.PASS_REASON.REQUEST_FAILED;
+ config.logger.error(`Unexpected exception while evaluating Risk cookie. ${err}`);
+ ctx.passReason = config.PASS_REASON.REQUEST_FAILED;
return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
}
- pxCtx.pxhd = res.pxhd;
- const action = isBadRiskScore(res, pxCtx);
+ ctx.pxhd = res.pxhd;
+ const action = isBadRiskScore(res, ctx, config);
/* score response invalid - pass traffic */
if (action === -1) {
- pxLogger.error('perimeterx server query response is invalid');
+ config.logger.error('perimeterx server query response is invalid');
return callback(config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT);
}
@@ -137,7 +132,7 @@ function evalByServerCall(pxCtx, callback) {
/* score crossed threshold - block traffic */
if (action === 0) {
- pxCtx.uuid = res.uuid || '';
+ ctx.uuid = res.uuid || '';
return callback(config.SCORE_EVALUATE_ACTION.BAD_SCORE);
}
@@ -150,7 +145,7 @@ function evalByServerCall(pxCtx, callback) {
* isBadRiskScore - processing response score and return a block indicator.
*
* @param {object} res - perimeterx response object.
- * @param {object} pxCtx - current request context.
+ * @param {object} ctx - current request context.
*
* @return {Number} indicator to the validity of the cookie.
* -1 response object is not valid
@@ -158,23 +153,22 @@ function evalByServerCall(pxCtx, callback) {
* 1 response valid with good score
*
*/
-function isBadRiskScore(res, pxCtx) {
+function isBadRiskScore(res, ctx, config) {
if (!res || !pxUtil.verifyDefined(res.score) || !res.action) {
- pxCtx.passReason = pxConfig.conf.PASS_REASON.INVALID_RESPONSE;
+ ctx.passReason = config.PASS_REASON.INVALID_RESPONSE;
return -1;
}
const score = res.score;
- pxCtx.score = score;
- pxCtx.uuid = res.uuid;
- if (score >= pxConfig.conf.BLOCKING_SCORE) {
- pxCtx.blockAction = res.action;
+ ctx.score = score;
+ ctx.uuid = res.uuid;
+ if (score >= config.BLOCKING_SCORE) {
+ ctx.blockAction = res.action;
if (res.action === 'j' && res.action_data && res.action_data.body) {
- pxCtx.blockActionData = res.action_data.body;
+ ctx.blockActionData = res.action_data.body;
}
return 0;
} else {
- pxCtx.passReason = pxConfig.conf.PASS_REASON.S2S;
+ ctx.passReason = config.PASS_REASON.S2S;
return 1;
}
}
-
diff --git a/lib/pxclient.js b/lib/pxclient.js
index fff950ec..a8bbc884 100644
--- a/lib/pxclient.js
+++ b/lib/pxclient.js
@@ -1,10 +1,7 @@
'use strict';
-const pxLogger = require('./pxlogger');
-const pxConfig = require('./pxconfig');
const pxUtil = require('./pxutil');
const pxHttpc = require('./pxhttpc');
const os = require('os');
-
class PxClient {
constructor() {
this.activitiesBuffer = [];
@@ -19,32 +16,32 @@ class PxClient {
*
* @param {string} activityType - name of the activity
* @param {object} details - activities details in key-val format
- * @param {object} pxCtx - request context
+ * @param {object} ctx - request context
+ * @param {object} config - perimeterx config
*/
- sendToPerimeterX(activityType, details, pxCtx) {
- const config = pxConfig.conf;
+ sendToPerimeterX(activityType, details, ctx, config) {
if (activityType === 'page_requested' && !config.SEND_PAGE_ACTIVITIES) {
return;
}
- details['cookie_origin'] = pxCtx.cookieOrigin;
+ details['cookie_origin'] = ctx.cookieOrigin;
details['module_version'] = config.MODULE_VERSION;
- if (pxCtx.blockAction && activityType === 'block') {
- details['block_action'] = pxCtx.blockAction;
+ if (ctx.blockAction && activityType === 'block') {
+ details['block_action'] = ctx.blockAction;
}
const pxData = {};
pxData.type = activityType;
- pxData.headers = pxCtx.headers;
+ pxData.headers = ctx.headers;
pxData.timestamp = Date.now();
- pxData.socket_ip = pxCtx.ip;
+ pxData.socket_ip = ctx.ip;
pxData.px_app_id = config.PX_APP_ID;
- pxData.url = pxCtx.fullUrl;
- if (pxCtx.vid) {
- pxData.vid = pxCtx.vid;
+ pxData.url = ctx.fullUrl;
+ if (ctx.vid) {
+ pxData.vid = ctx.vid;
}
- if (pxCtx.pxhd && (activityType === 'page_requested') || activityType === 'block') {
- pxData.pxhd = pxCtx.pxhd;
+ if (ctx.pxhd && (activityType === 'page_requested') || activityType === 'block') {
+ pxData.pxhd = ctx.pxhd;
pxUtil.prepareCustomParams(config, details);
}
pxData.details = details;
@@ -52,8 +49,7 @@ class PxClient {
this.activitiesBuffer.push(pxData);
}
- sendEnforcerTelemetry(updateReason) {
- const config = pxConfig.conf;
+ sendEnforcerTelemetry(updateReason, config) {
const headers = {
'Authorization': 'Bearer ' + config.AUTH_TOKEN,
'Content-Type': 'application/json'
@@ -73,31 +69,31 @@ class PxClient {
pxData.px_app_id = config.PX_APP_ID;
pxData.details = details;
- pxLogger.debug('Sending telemetry activity to perimeterx servers');
+ config.logger.debug('Sending telemetry activity to perimeterx servers');
- this.callServer(pxData, pxConfig.conf.TELEMETRY_URI, headers);
+ this.callServer(pxData, config.TELEMETRY_URI, headers, config);
}
/**
* submitActivities - flash activities buffer and send to px servers for processing
*
*/
- submitActivities(cb) {
+ submitActivities(config, cb) {
if (this.activitiesBuffer.length > 0) {
const headers = {
'Content-Type': 'application/json'
};
- pxLogger.debug('Sending activities to perimeterx servers');
+ config.logger.debug('Sending activities to perimeterx servers');
const tempActivities = this.activitiesBuffer.concat(); //duplicate
this.activitiesBuffer.splice(0, tempActivities.length);
- this.callServer(tempActivities, pxConfig.conf.SERVER_COLLECT_URI, headers, cb);
+ this.callServer(tempActivities, config.SERVER_COLLECT_URI, headers, config, cb);
}
}
- callServer(data, path, headers, cb) {
- pxHttpc.callServer(data, headers, path, 'activities');
+ callServer(data, path, headers, config, cb) {
+ pxHttpc.callServer(data, headers, path, 'activities', config);
if (cb) {
cb();
}
diff --git a/lib/pxconfig.js b/lib/pxconfig.js
index e01bbb4c..b26fb4c8 100644
--- a/lib/pxconfig.js
+++ b/lib/pxconfig.js
@@ -1,165 +1,44 @@
'use strict';
-const ConfigLoader = require('./configloader');
const TestModeRequestHandler = require('./pxtesthandler');
const HttpsProxyAgent = require('https-proxy-agent');
-let config = {};
-let configLoader = null;
-const PX_DEFAULT = {};
-const PX_INTERNAL = {};
-
-PX_INTERNAL.MODULE_VERSION = 'NodeJS Module';
-/* internal configurations */
-PX_INTERNAL.SERVER_HOST = 'sapi.perimeterx.net';
-PX_INTERNAL.COLLECTOR_HOST = 'collector.perimeterx.net';
-PX_INTERNAL.CAPTCHA_HOST = 'captcha.px-cdn.net';
-PX_INTERNAL.CLIENT_HOST = 'client.perimeterx.net';
-PX_INTERNAL.CONFIGURATIONS_HOST = 'px-conf.perimeterx.net';
-PX_INTERNAL.SERVER_TO_SERVER_API_URI = '/api/v3/risk';
-PX_INTERNAL.SERVER_CAPTCHA_URI = '/api/v2/risk/captcha';
-PX_INTERNAL.SERVER_COLLECT_URI = '/api/v1/collector/s2s';
-PX_INTERNAL.CONFIGURATIONS_URI = '/api/v1/enforcer';
-PX_INTERNAL.TELEMETRY_URI = '/api/v2/risk/telemetry';
-PX_INTERNAL.ENFORCER_TRUE_IP_HEADER = 'x-px-enforcer-true-ip';
-PX_INTERNAL.FIRST_PARTY_HEADER = 'x-px-first-party';
-PX_INTERNAL.FORWARDED_FOR_HEADER = 'x-forwarded-for';
-PX_INTERNAL.FIRST_PARTY_VENDOR_PATH = '/init.js';
-PX_INTERNAL.FIRST_PARTY_XHR_PATH = '/xhr';
-PX_INTERNAL.FIRST_PARTY_CAPTCHA_PATH = '/captcha';
-PX_INTERNAL.EMPTY_GIF_B64 = 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==';
-
-// Cookie encryption configurations
-PX_INTERNAL.COOKIE_ENCRYPTION = true;
-PX_INTERNAL.CE_KEYLEN = 32;
-PX_INTERNAL.CE_IVLEN = 16;
-PX_INTERNAL.CE_ITERATIONS = 1000;
-PX_INTERNAL.CE_DIGEST = 'sha256';
-PX_INTERNAL.CE_ALGO = 'aes-256-cbc';
-
-PX_INTERNAL.STATIC_FILES_EXT = ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot', '.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx', '.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png', '.ppt', '.mid', 'webp', '.jar'];
-
-/* actions */
-PX_INTERNAL.SCORE_EVALUATE_ACTION = {
- SPECIAL_TOKEN: -6,
- SENSITIVE_ROUTE: -5,
- UNEXPECTED_RESULT: -4,
- NO_COOKIE: -3,
- COOKIE_INVALID: -2,
- COOKIE_EXPIRED: -1,
-
- S2S_PASS_TRAFFIC: 11,
- COOKIE_PASS_TRAFFIC: 10,
- S2S_TIMEOUT_PASS: 9,
- COOKIE_BLOCK_TRAFFIC: -10,
- S2S_BLOCK_TRAFFIC: -11,
- CAPTCHA_BLOCK_TRAFFIC: -12,
- CHALLENGE_BLOCK_TRAFFIC: -13,
-
- CAPTCHA_PASS: 0,
- CAPTCHA_BLOCK: 1,
-
- GOOD_SCORE: 1,
- BAD_SCORE: 0
-};
-
-PX_INTERNAL.PASS_REASON = {
- CAPTCHA_TIMEOUT: 'captcha_timeout',
- CAPTCHA: 'captcha',
- COOKIE: 'cookie',
- S2S: 's2s',
- S2S_TIMEOUT: 's2s_timeout',
- MONITOR_MODE: 'monitor_mode',
- INVALID_RESPONSE: 'invalid_response',
- REQUEST_FAILED: 'request_failed'
-};
-
-PX_INTERNAL.MONITOR_MODE = {
- MONITOR: 0,
- BLOCK: 1
-};
+class PxConfig {
+ constructor(params, logger) {
+ this.PX_INTERNAL = pxInternalConfig();
+ this.PX_DEFAULT = pxDefaultConfig(this.PX_INTERNAL);
+ this.config = this.mergeDefaults(params);
+ this.logger = logger;
+ this.config.logger = this.logger;
-/* to be defined by the initiating user or dynamically via configuration service */
-PX_DEFAULT.PX_APP_ID = 'PX_APP_ID';
-PX_DEFAULT.ENABLE_MODULE = true;
-PX_DEFAULT.API_TIMEOUT_MS = 1000;
-PX_DEFAULT.BLOCKING_SCORE = 100;
-PX_DEFAULT.COOKIE_SECRET_KEY = 'cookie_secret_key';
-PX_DEFAULT.AUTH_TOKEN = 'auth_token';
-PX_DEFAULT.IP_HEADERS = [];
-PX_DEFAULT.BLOCK_HTML = 'BLOCK';
-PX_DEFAULT.SENSITIVE_HEADERS = ['cookie', 'cookies'];
-PX_DEFAULT.PROXY_URL = '';
-PX_DEFAULT.SEND_PAGE_ACTIVITIES = true;
-PX_DEFAULT.DEBUG_MODE = false;
-PX_DEFAULT.CUSTOM_REQUEST_HANDLER = '';
-PX_DEFAULT.MAX_BUFFER_LEN = 30;
-PX_DEFAULT.GET_USER_IP = '';
-PX_DEFAULT.CSS_REF = [];
-PX_DEFAULT.JS_REF = [];
-PX_DEFAULT.CUSTOM_LOGO = '';
-PX_DEFAULT.LOGO_VISIBILITY = 'hidden';
-PX_DEFAULT.SENSITIVE_ROUTES = [];
-PX_DEFAULT.WHITELIST_ROUTES = [];
-PX_DEFAULT.DYNAMIC_CONFIGURATIONS = false;
-PX_DEFAULT.CONFIGURATION_LOAD_INTERVAL = 5000;
-PX_DEFAULT.MODULE_MODE = PX_INTERNAL.MONITOR_MODE.MONITOR;
-PX_DEFAULT.ADDITIONAL_ACTIVITY_HANDLER = '';
-PX_DEFAULT.ENRICH_CUSTOM_PARAMETERS = '';
-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 = '';
+ this.config.WHITELIST_EXT = [...this.PX_INTERNAL.STATIC_FILES_EXT, ...this.PX_DEFAULT.WHITELIST_EXT];
-class PxConfig {
- static init(params, pxClient) {
- config = PxConfig.mergeDefaults(params);
- config.WHITELIST_EXT = [...PX_INTERNAL.STATIC_FILES_EXT, ...PX_DEFAULT.WHITELIST_EXT];
- if (PX_DEFAULT.TESTING_MODE) {
- PX_DEFAULT.CUSTOM_REQUEST_HANDLER = TestModeRequestHandler.testModeRequestHandler;
- }
- if (config.PROXY_URL) {
- config.agent = new HttpsProxyAgent(config.PROXY_URL);
+ if (this.PX_DEFAULT.TESTING_MODE) {
+ this.PX_DEFAULT.CUSTOM_REQUEST_HANDLER = TestModeRequestHandler.testModeRequestHandler;
}
- pxClient.sendEnforcerTelemetry('initial_config');
- if (PX_DEFAULT.DYNAMIC_CONFIGURATIONS) {
- configLoader = new ConfigLoader(config, pxClient);
- configLoader.init();
+
+ if (this.config.PROXY_URL) {
+ this.config.agent = new HttpsProxyAgent(this._config.PROXY_URL);
}
+
+ this.configLoader = null;
}
- static mergeDefaults(params) {
- PX_DEFAULT.ENABLE_MODULE = this.configurationsOverriding(PX_DEFAULT, params, 'ENABLE_MODULE', 'enableModule');
- PX_DEFAULT.PX_APP_ID = this.configurationsOverriding(PX_DEFAULT, params, 'PX_APP_ID', 'pxAppId');
- //Set SEVER_HOST if app_id is set.
- PX_INTERNAL.SERVER_HOST = PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? PX_INTERNAL.SERVER_HOST = 'sapi-' + PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'sapi.perimeterx.net';
- PX_INTERNAL.COLLECTOR_HOST = PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? PX_INTERNAL.COLLECTOR_HOST = 'collector-' + PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'collector.perimeterx.net';
- PX_DEFAULT.COOKIE_SECRET_KEY = this.configurationsOverriding(PX_DEFAULT, params, 'COOKIE_SECRET_KEY', 'cookieSecretKey');
- PX_DEFAULT.AUTH_TOKEN = this.configurationsOverriding(PX_DEFAULT, params, 'AUTH_TOKEN', 'authToken');
- PX_DEFAULT.PROXY_URL = this.configurationsOverriding(PX_DEFAULT, params, 'PROXY_URL', 'proxy');
- PX_DEFAULT.API_TIMEOUT_MS = this.configurationsOverriding(PX_DEFAULT, params, 'API_TIMEOUT_MS', 'apiTimeoutMS');
- PX_DEFAULT.CUSTOM_REQUEST_HANDLER = this.configurationsOverriding(PX_DEFAULT, params, 'CUSTOM_REQUEST_HANDLER', 'customRequestHandler');
- PX_DEFAULT.GET_USER_IP = this.configurationsOverriding(PX_DEFAULT, params, 'GET_USER_IP', 'getUserIp');
- PX_DEFAULT.BLOCKING_SCORE = this.configurationsOverriding(PX_DEFAULT, params, 'BLOCKING_SCORE', 'blockingScore');
- PX_DEFAULT.IP_HEADERS = this.configurationsOverriding(PX_DEFAULT, params, 'IP_HEADERS', 'ipHeaders');
- PX_DEFAULT.SEND_PAGE_ACTIVITIES = this.configurationsOverriding(PX_DEFAULT, params, 'SEND_PAGE_ACTIVITIES', 'sendPageActivities');
- PX_DEFAULT.SENSITIVE_HEADERS = this.configurationsOverriding(PX_DEFAULT, params, 'SENSITIVE_HEADERS', 'sensitiveHeaders');
- PX_DEFAULT.DEBUG_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'DEBUG_MODE', 'debugMode');
- PX_DEFAULT.MAX_BUFFER_LEN = this.configurationsOverriding(PX_DEFAULT, params, 'MAX_BUFFER_LEN', 'maxBufferLength');
- PX_DEFAULT.JS_REF = this.configurationsOverriding(PX_DEFAULT, params, 'JS_REF', 'jsRef');
- PX_DEFAULT.CSS_REF = this.configurationsOverriding(PX_DEFAULT, params, 'CSS_REF', 'cssRef');
- PX_DEFAULT.CUSTOM_LOGO = this.configurationsOverriding(PX_DEFAULT, params, 'CUSTOM_LOGO', 'customLogo');
- PX_DEFAULT.SENSITIVE_ROUTES = this.configurationsOverriding(PX_DEFAULT, params, 'SENSITIVE_ROUTES', 'sensitiveRoutes');
- PX_DEFAULT.WHITELIST_ROUTES = this.configurationsOverriding(PX_DEFAULT, params, 'WHITELIST_ROUTES', 'whitelistRoutes');
- PX_DEFAULT.DYNAMIC_CONFIGURATIONS = this.configurationsOverriding(PX_DEFAULT, params, 'DYNAMIC_CONFIGURATIONS', 'dynamicConfigurations');
- PX_DEFAULT.MODULE_MODE = this.configurationsOverriding(PX_DEFAULT, params, 'MODULE_MODE', 'moduleMode');
- PX_DEFAULT.FIRST_PARTY_ENABLED = this.configurationsOverriding(PX_DEFAULT, params, 'FIRST_PARTY_ENABLED', 'firstPartyEnabled');
- PX_INTERNAL.MODULE_VERSION = this.configurationsOverriding(PX_INTERNAL, params, 'MODULE_VERSION', 'moduleVersion');
- PX_DEFAULT.ADDITIONAL_ACTIVITY_HANDLER = this.configurationsOverriding(PX_DEFAULT, params, 'ADDITIONAL_ACTIVITY_HANDLER', 'additionalActivityHandler');
- 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);
+ mergeDefaults(params) {
+ const configKeyMapping = [['ENABLE_MODULE', 'enableModule'], ['PX_APP_ID', 'pxAppId'], ['COOKIE_SECRET_KEY', 'cookieSecretKey'], ['AUTH_TOKEN', 'authToken'], ['PROXY_URL', 'proxy'],
+ ['API_TIMEOUT_MS', 'apiTimeoutMS'], ['CUSTOM_REQUEST_HANDLER', 'customRequestHandler'], ['GET_USER_IP', 'getUserIp'], ['BLOCKING_SCORE', 'blockingScore'], ['IP_HEADERS', 'ipHeaders'],
+ ['SEND_PAGE_ACTIVITIES', 'sendPageActivities'], ['SENSITIVE_HEADERS', 'sensitiveHeaders'], ['DEBUG_MODE', 'debugMode'], ['MAX_BUFFER_LEN', 'maxBufferLength'], ['JS_REF', 'jsRef'],
+ ['CSS_REF', 'cssRef'], ['CUSTOM_LOGO', 'customLogo'], ['SENSITIVE_ROUTES', 'sensitiveRoutes'], ['WHITELIST_ROUTES', 'whitelistRoutes'], ['DYNAMIC_CONFIGURATIONS', 'dynamicConfigurations'],
+ ['MODULE_MODE', 'moduleMode'], ['FIRST_PARTY_ENABLED', 'firstPartyEnabled'], ['ADDITIONAL_ACTIVITY_HANDLER', 'additionalActivityHandler'], ['ENRICH_CUSTOM_PARAMETERS', 'enrichCustomParameters'],
+ ['TESTING_MODE', 'testingMode'], ['WHITELIST_EXT', 'whitelistExt'], ['BYPASS_MONITOR_HEADER', 'bypassMonitorHeader']];
+
+ configKeyMapping.forEach(([targetKey, sourceKey]) => {
+ this.PX_DEFAULT[targetKey] = PxConfig.configurationsOverriding(this.PX_DEFAULT, params, targetKey, sourceKey);
+ });
+
+ this.PX_INTERNAL.SERVER_HOST = this.PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? this.PX_INTERNAL.SERVER_HOST = 'sapi-' + this.PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'sapi.perimeterx.net';
+ this.PX_INTERNAL.COLLECTOR_HOST = this.PX_DEFAULT.PX_APP_ID !== 'PX_APP_ID' ? this.PX_INTERNAL.COLLECTOR_HOST = 'collector-' + this.PX_DEFAULT.PX_APP_ID.toLowerCase() + '.perimeterx.net' : 'collector.perimeterx.net';
+ this.PX_INTERNAL.MODULE_VERSION = PxConfig.configurationsOverriding(this.PX_INTERNAL, params, 'MODULE_VERSION', 'moduleVersion');
+
+ return Object.assign(this.PX_DEFAULT, this.PX_INTERNAL);
}
static configurationsOverriding(conf, params, defaultName, userInput) {
@@ -184,14 +63,122 @@ class PxConfig {
return params[userInput];
}
- static get conf() {
- return config;
+ get conf() {
+ return this.config;
}
- static get confManager() {
- return configLoader;
+ get confManager() {
+ return this.configLoader;
}
}
-module.exports = PxConfig;
+function pxInternalConfig() {
+ return {
+ MODULE_VERSION: 'NodeJS Module',
+ SERVER_HOST: 'sapi.perimeterx.net',
+ COLLECTOR_HOST: 'collector.perimeterx.net',
+ CAPTCHA_HOST: 'captcha.px-cdn.net',
+ CLIENT_HOST: 'client.perimeterx.net',
+ CONFIGURATIONS_HOST: 'px-conf.perimeterx.net',
+ SERVER_TO_SERVER_API_URI: '/api/v3/risk',
+ SERVER_CAPTCHA_URI: '/api/v2/risk/captcha',
+ SERVER_COLLECT_URI: '/api/v1/collector/s2s',
+ CONFIGURATIONS_URI: '/api/v1/enforcer',
+ TELEMETRY_URI: '/api/v2/risk/telemetry',
+ ENFORCER_TRUE_IP_HEADER: 'x-px-enforcer-true-ip',
+ FIRST_PARTY_HEADER: 'x-px-first-party',
+ FORWARDED_FOR_HEADER: 'x-forwarded-for',
+ FIRST_PARTY_VENDOR_PATH: '/init.js',
+ FIRST_PARTY_XHR_PATH: '/xhr',
+ FIRST_PARTY_CAPTCHA_PATH: '/captcha',
+ EMPTY_GIF_B64: 'R0lGODlhAQABAPAAAAAAAAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
+ // Cookie encryption configurations
+ COOKIE_ENCRYPTION: true,
+ CE_KEYLEN: 32,
+ CE_IVLEN: 16,
+ CE_ITERATIONS: 1000,
+ CE_DIGEST: 'sha256',
+ CE_ALGO: 'aes-256-cbc',
+
+ STATIC_FILES_EXT: ['.css', '.bmp', '.tif', '.ttf', '.docx', '.woff2', '.js', '.pict', '.tiff', '.eot', '.xlsx', '.jpg', '.csv', '.eps', '.woff', '.xls', '.jpeg', '.doc', '.ejs', '.otf', '.pptx', '.gif', '.pdf', '.swf', '.svg', '.ps', '.ico', '.pls', '.midi', '.svgz', '.class', '.png', '.ppt', '.mid', 'webp', '.jar'],
+
+ /* actions */
+ SCORE_EVALUATE_ACTION: {
+ SPECIAL_TOKEN: -6,
+ SENSITIVE_ROUTE: -5,
+ UNEXPECTED_RESULT: -4,
+ NO_COOKIE: -3,
+ COOKIE_INVALID: -2,
+ COOKIE_EXPIRED: -1,
+
+ S2S_PASS_TRAFFIC: 11,
+ COOKIE_PASS_TRAFFIC: 10,
+ S2S_TIMEOUT_PASS: 9,
+ COOKIE_BLOCK_TRAFFIC: -10,
+ S2S_BLOCK_TRAFFIC: -11,
+ CAPTCHA_BLOCK_TRAFFIC: -12,
+ CHALLENGE_BLOCK_TRAFFIC: -13,
+
+ CAPTCHA_PASS: 0,
+ CAPTCHA_BLOCK: 1,
+
+ GOOD_SCORE: 1,
+ BAD_SCORE: 0
+ },
+
+ PASS_REASON: {
+ CAPTCHA_TIMEOUT: 'captcha_timeout',
+ CAPTCHA: 'captcha',
+ COOKIE: 'cookie',
+ S2S: 's2s',
+ S2S_TIMEOUT: 's2s_timeout',
+ MONITOR_MODE: 'monitor_mode',
+ INVALID_RESPONSE: 'invalid_response',
+ REQUEST_FAILED: 'request_failed'
+ },
+
+ MONITOR_MODE: {
+ MONITOR: 0,
+ BLOCK: 1
+ },
+ };
+}
+
+/* to be defined by the initiating user or dynamically via configuration service */
+function pxDefaultConfig(PX_INTERNAL) {
+ return {
+ PX_APP_ID: 'PX_APP_ID',
+ ENABLE_MODULE: true,
+ API_TIMEOUT_MS: 1000,
+ BLOCKING_SCORE: 100,
+ COOKIE_SECRET_KEY: 'cookie_secret_key',
+ AUTH_TOKEN: 'auth_token',
+ IP_HEADERS: [],
+ BLOCK_HTML: 'BLOCK',
+ SENSITIVE_HEADERS: ['cookie', 'cookies'],
+ PROXY_URL: '',
+ SEND_PAGE_ACTIVITIES: true,
+ DEBUG_MODE: false,
+ CUSTOM_REQUEST_HANDLER: '',
+ MAX_BUFFER_LEN: 30,
+ GET_USER_IP: '',
+ CSS_REF: [],
+ JS_REF: [],
+ CUSTOM_LOGO: '',
+ LOGO_VISIBILITY: 'hidden',
+ SENSITIVE_ROUTES: [],
+ WHITELIST_ROUTES: [],
+ DYNAMIC_CONFIGURATIONS: false,
+ CONFIGURATION_LOAD_INTERVAL: 5000,
+ MODULE_MODE: PX_INTERNAL.MONITOR_MODE.MONITOR,
+ ADDITIONAL_ACTIVITY_HANDLER: '',
+ ENRICH_CUSTOM_PARAMETERS: '',
+ FIRST_PARTY_ENABLED: true,
+ FIRST_PARTY_XHR_ENABLED: true,
+ TESTING_MODE: false,
+ WHITELIST_EXT: [],
+ BYPASS_MONITOR_HEADER: '',
+ };
+}
+module.exports = PxConfig;
\ No newline at end of file
diff --git a/lib/pxcontext.js b/lib/pxcontext.js
index dc2eb8d4..84f15f9d 100644
--- a/lib/pxcontext.js
+++ b/lib/pxcontext.js
@@ -1,74 +1,48 @@
-const net = require('net');
-const pxLogger = require('./pxlogger');
const pxUtil = require('./pxutil');
class PxContext {
-
- constructor(config, request) {
- const userAgent = request.get('user-agent') || request.get('User-Agent') || 'none';
+ constructor(config, req) {
+ const userAgent = req.get('user-agent') || req.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;
- this.ip = PxContext.extractIP(config, request);
+ this.ip = pxUtil.extractIP(config, req);
- this.headers = pxUtil.filterSensitiveHeaders(request.headers);
- this.hostname = request.hostname || request.get('host');
+ this.headers = pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS);
+ this.hostname = req.hostname || req.get('host');
this.userAgent = userAgent;
- this.uri = request.originalUrl || '/';
- this.fullUrl = request.protocol + '://' + request.get('host') + request.originalUrl;
- this.httpVersion = request.httpVersion || '';
- this.httpMethod = request.method || '';
+ this.uri = req.originalUrl || '/';
+ this.fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl;
+ this.httpVersion = req.httpVersion || '';
+ this.httpMethod = req.method || '';
this.sensitiveRoute = this.isSpecialRoute(config.SENSITIVE_ROUTES, this.uri);
this.whitelistRoute = this.isSpecialRoute(config.WHITELIST_ROUTES, this.uri);
this.cookieOrigin = 'cookie';
const mobileHeader = this.headers[mobileSdkHeader];
if (mobileHeader !== undefined) {
this.cookieOrigin = 'header';
- pxLogger.debug('Mobile SDK token detected');
+ config.logger.debug('Mobile SDK token detected');
this.originalToken = this.headers[mobileSdkOriginalTokenHeader];
const tokenObject = this.getTokenObject(mobileHeader);
this.cookies[tokenObject.key] = tokenObject.value;
} else {
- this.requestCookieNames = Object.keys(request.cookies);
- Object.keys(request.cookies).forEach(key => {
+ this.requestCookieNames = Object.keys(req.cookies);
+ Object.keys(req.cookies).forEach(key => {
if (key.match(/^_px\d?$/)) {
- this.cookies[key] = request.cookies[key];
+ this.cookies[key] = req.cookies[key];
} else if (key === '_pxhd') {
- this.pxhd = request.cookies[key];
- } else if ((key === '_pxvid' || key === 'pxvid') && vidRegex.test(request.cookies[key])) {
- this.vid = request.cookies[key];
+ this.pxhd = req.cookies[key];
+ } else if ((key === '_pxvid' || key === 'pxvid') && vidRegex.test(req.cookies[key])) {
+ this.vid = req.cookies[key];
this.vidSource = 'vid_cookie';
}
});
}
}
- static extractIP(config, request) {
- let ip;
- if (Array.isArray(config.IP_HEADERS)) {
- config.IP_HEADERS.some(ipHeader => {
- try {
- const headerValue = request.get(ipHeader);
- if (headerValue) {
- ip = headerValue;
- return true;
- }
- } catch (e) {
- pxLogger.debug('Failed to use IP_HEADERS from config.');
- }
- });
- } else {
- ip = typeof config.GET_USER_IP === 'function' && config.GET_USER_IP(request);
- }
- if (ip && net.isIP(ip) > 0) {
- return ip;
- }
- return request.ip;
- }
-
getCookie() {
return this.cookies['_px3'] ? this.cookies['_px3'] : this.cookies['_px'];
}
diff --git a/lib/pxcookie.js b/lib/pxcookie.js
index ea790c9f..c50d32a7 100644
--- a/lib/pxcookie.js
+++ b/lib/pxcookie.js
@@ -1,6 +1,4 @@
'use strict';
-const pxConfig = require('./pxconfig');
-const pxLogger = require('./pxlogger');
const CookieV1 = require('./cookie/cookieV1');
const CookieV3 = require('./cookie/cookieV3');
const TokenV1 = require('./cookie/tokenV1');
@@ -12,86 +10,84 @@ exports.evalCookie = evalCookie;
/**
* evalCookie - main cookie evaluation function. dectypt, decode and verify cookie content. if score.
*
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
*
* @return {Number} evaluation results, derived from configured enum on PX_DEFAULT.COOKIE_EVAL. possible values: (NO_COOKIE, COOKIE_INVALID, COOKIE_EXPIRED, UNEXPECTED_RESULT, BAD_SCORE, GOOD_SCORE).
*
*/
-function evalCookie(pxCtx) {
-
- const config = pxConfig.conf;
- const pxCookie = pxCtx.getCookie();
+function evalCookie(ctx, config) {
+ const pxCookie = ctx.getCookie();
try {
- if (Object.keys(pxCtx.cookies).length === 0) {
- pxLogger.debug('Cookie is missing');
- pxCtx.s2sCallReason = 'no_cookie';
+ if (Object.keys(ctx.cookies).length === 0) {
+ config.logger.debug('Cookie is missing');
+ ctx.s2sCallReason = 'no_cookie';
return config.SCORE_EVALUATE_ACTION.NO_COOKIE;
}
if (!config.COOKIE_SECRET_KEY) {
- pxLogger.debug('No cookie key found, pause cookie evaluation');
- pxCtx.s2sCallReason = 'no_cookie_key';
+ config.logger.debug('No cookie key found, pause cookie evaluation');
+ ctx.s2sCallReason = 'no_cookie_key';
return config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
}
// Mobile SDK traffic
- if (pxCookie && pxCtx.cookieOrigin === 'header') {
+ if (pxCookie && ctx.cookieOrigin === 'header') {
if (pxCookie.match(/^\d+$/)) {
- pxCtx.s2sCallReason = `mobile_error_${pxCookie}`;
- if (pxCtx.originalToken) {
- originalTokenValidator.evalCookie(pxCtx, config);
+ ctx.s2sCallReason = `mobile_error_${pxCookie}`;
+ if (ctx.originalToken) {
+ originalTokenValidator.evalCookie(ctx, config);
}
return config.SCORE_EVALUATE_ACTION.SPECIAL_TOKEN;
}
}
- const cookie = pxCookieFactory(pxCtx, config);
- pxLogger.debug(`Cookie ${getCookieVersion(pxCtx)} found, Evaluating`);
+ const cookie = pxCookieFactory(ctx, config);
+ config.logger.debug(`Cookie ${getCookieVersion(ctx)} found, Evaluating`);
if (!cookie.deserialize()) {
- pxCtx.s2sCallReason = 'cookie_decryption_failed';
- pxCtx.px_orig_cookie = getPxCookieFromContext(pxCtx);
- pxLogger.debug(`Cookie decryption failed, value: ${pxCtx.px_orig_cookie}`);
+ ctx.s2sCallReason = 'cookie_decryption_failed';
+ ctx.px_orig_cookie = getPxCookieFromContext(ctx);
+ config.logger.debug(`Cookie decryption failed, value: ${ctx.px_orig_cookie}`);
return config.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
}
- pxCtx.decodedCookie = cookie.decodedCookie;
- pxCtx.score = cookie.getScore();
- pxCtx.vid = cookie.getVid();
- pxCtx.vidSource = 'risk_cookie';
- pxCtx.uuid = cookie.getUuid();
- pxCtx.hmac = cookie.getHmac();
- pxCtx.blockAction = cookie.getBlockAction();
+ ctx.decodedCookie = cookie.decodedCookie;
+ ctx.score = cookie.getScore();
+ ctx.vid = cookie.getVid();
+ ctx.vidSource = 'risk_cookie';
+ ctx.uuid = cookie.getUuid();
+ ctx.hmac = cookie.getHmac();
+ ctx.blockAction = cookie.getBlockAction();
if (cookie.isExpired()) {
- pxLogger.debug(`Cookie TTL is expired, value: ${JSON.stringify(cookie.decodedCookie)}, age: ${Date.now() - cookie.getTime()}`);
- pxCtx.s2sCallReason = 'cookie_expired';
+ config.logger.debug(`Cookie TTL is expired, value: ${JSON.stringify(cookie.decodedCookie)}, age: ${Date.now() - cookie.getTime()}`);
+ ctx.s2sCallReason = 'cookie_expired';
return config.SCORE_EVALUATE_ACTION.COOKIE_EXPIRED;
}
if (cookie.isHighScore()) {
- pxLogger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
+ config.logger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
return config.SCORE_EVALUATE_ACTION.BAD_SCORE;
}
if (!cookie.isSecure()) {
- pxLogger.debug(`Cookie HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${pxCtx.userAgent}`);
- pxCtx.s2sCallReason = 'cookie_validation_failed';
+ config.logger.debug(`Cookie HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${ctx.userAgent}`);
+ ctx.s2sCallReason = 'cookie_validation_failed';
return config.SCORE_EVALUATE_ACTION.COOKIE_INVALID;
}
- if (pxCtx.sensitiveRoute) {
- pxLogger.debug(`Sensitive route match, sending Risk API. path: ${pxCtx.uri}`);
- pxCtx.s2sCallReason = 'sensitive_route';
+ if (ctx.sensitiveRoute) {
+ config.logger.debug(`Sensitive route match, sending Risk API. path: ${ctx.uri}`);
+ ctx.s2sCallReason = 'sensitive_route';
return config.SCORE_EVALUATE_ACTION.SENSITIVE_ROUTE;
}
- pxCtx.passReason = config.PASS_REASON.COOKIE;
- pxLogger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
+ ctx.passReason = config.PASS_REASON.COOKIE;
+ config.logger.debug(`Cookie evaluation ended successfully, risk score: ${cookie.getScore()}`);
return config.SCORE_EVALUATE_ACTION.GOOD_SCORE;
} catch (e) {
- pxLogger.error('Error while evaluating perimeterx cookie: ' + e.message);
- pxCtx.s2sCallReason = 'cookie_decryption_failed';
+ config.logger.error('Error while evaluating perimeterx cookie: ' + e.message);
+ ctx.s2sCallReason = 'cookie_decryption_failed';
return config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT;
}
}
@@ -99,20 +95,20 @@ function evalCookie(pxCtx) {
/**
* Factory method for creating PX Cookie object according to cookie version and type found on the request
*/
-function pxCookieFactory(pxCtx, pxConfig) {
- if (pxCtx.cookieOrigin == 'cookie') {
- return (pxCtx.cookies['_px3'] ? new CookieV3(pxCtx, pxConfig) : new CookieV1(pxCtx, pxConfig));
+function pxCookieFactory(ctx, config) {
+ if (ctx.cookieOrigin == 'cookie') {
+ return (ctx.cookies['_px3'] ? new CookieV3(ctx, config, config.logger) : new CookieV1(ctx, config, config.logger));
} else {
- return (pxCtx.cookies['_px3'] ? new TokenV3(pxCtx, pxConfig, pxCtx.cookies['_px3']) : new TokenV1(pxCtx, pxConfig, pxCtx.cookies['_px']));
+ return (ctx.cookies['_px3'] ? new TokenV3(ctx, config, ctx.cookies['_px3'], config.logger) : new TokenV1(ctx, config, ctx.cookies['_px'], config.logger));
}
}
-function getCookieVersion(pxCtx) {
- return pxCtx.cookies['_px3'] ? 'V3' : 'V1';
+function getCookieVersion(ctx) {
+ return ctx.cookies['_px3'] ? 'V3' : 'V1';
}
-function getPxCookieFromContext(pxCtx) {
- if (Object.keys(pxCtx.cookies).length) {
- return pxCtx.cookies['_px3'] ? pxCtx.cookies['_px3'] : pxCtx.cookies['_px'];
+function getPxCookieFromContext(ctx) {
+ if (Object.keys(ctx.cookies).length) {
+ return ctx.cookies['_px3'] ? ctx.cookies['_px3'] : ctx.cookies['_px'];
}
}
diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js
index e2903a62..954123c3 100644
--- a/lib/pxenforcer.js
+++ b/lib/pxenforcer.js
@@ -1,141 +1,148 @@
'use strict';
+const mu = require('mu2');
const PxClient = require('./pxclient');
-const pxLogger = require('./pxlogger');
const PxContext = require('./pxcontext');
const PxConfig = require('./pxconfig');
-const mu = require('mu2');
const pxProxy = require('./pxproxy');
const pxUtil = require('./pxutil');
const pxApi = require('./pxapi');
const pxCookie = require('./pxcookie');
+const PxLogger = require('./pxlogger');
+const ConfigLoader = require('./configloader');
class PxEnforcer {
-
constructor(params, client) {
- if (client) {
- this.pxClient = client;
- } else {
- this.pxClient = new PxClient();
+ this.logger = new PxLogger();
+
+ this.pxConfig = new PxConfig(params, this.logger);
+ this._config = this.pxConfig.conf;
+
+ this.logger.init(this.pxConfig);
+
+ this.pxClient = (client) ? client : new PxClient();
+ this.pxClient.init(this._config);
+ this.pxClient.sendEnforcerTelemetry('initial_config', this);
+ if (this._config.DYNAMIC_CONFIGURATIONS) {
+ this.config.configLoader = new ConfigLoader(this.pxConfig, this.pxClient);
+ this.config.configLoader.init();
}
- this.pxClient.init();
- PxConfig.init(params, this.pxClient);
- this.reversePrefix = PxConfig.conf.PX_APP_ID.substring(2);
+
+ this.reversePrefix = this.pxConfig.conf.PX_APP_ID.substring(2);
}
enforce(req, res, cb) {
- const pxConfig = PxConfig.conf;
const requestUrl = req.originalUrl;
- pxLogger.debug('Starting request verification');
+ this.logger.debug('Starting request verification');
- if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_VENDOR_PATH}`)) {
+ if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_VENDOR_PATH}`)) {
// reverse proxy client
- return pxProxy.getClient(req, pxConfig, PxContext.extractIP(pxConfig, req), cb);
+ return pxProxy.getClient(req, this._config, pxUtil.extractIP(this._config, req), cb);
}
- if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_XHR_PATH}`)) {
+ if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_XHR_PATH}`)) {
//reverse proxy xhr
- return pxProxy.sendXHR(req, pxConfig, PxContext.extractIP(pxConfig, req), this.reversePrefix, cb);
+ return pxProxy.sendXHR(req, this._config, pxUtil.extractIP(this._config, req), this.reversePrefix, cb);
}
- if (requestUrl.startsWith(`/${this.reversePrefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}`)) {
+ if (requestUrl.startsWith(`/${this.reversePrefix}${this._config.FIRST_PARTY_CAPTCHA_PATH}`)) {
// reverse proxy captcha
- return pxProxy.getCaptcha(req, pxConfig, PxContext.extractIP(pxConfig, req), this.reversePrefix, cb);
+ return pxProxy.getCaptcha(req, this._config, pxUtil.extractIP(this._config, req), this.reversePrefix, cb);
}
- if (!pxConfig.ENABLE_MODULE || pxUtil.checkForStatic(req, pxConfig.STATIC_FILES_EXT)) {
- pxLogger.debug('Request will not be verified, module is disabled');
+ if (!this._config.ENABLE_MODULE || pxUtil.checkForStatic(req, this._config.STATIC_FILES_EXT)) {
+ this.logger.debug('Request will not be verified, module is disabled');
return cb();
}
try {
- const pxCtx = new PxContext(pxConfig, req);
- pxLogger.debug('Request context created successfully');
- pxCtx.collectorUrl = `https://collector-${pxConfig.PX_APP_ID}.perimeterx.net`;
+ const ctx = new PxContext(this._config, req, this.logger);
+ this.logger.debug('Request context created successfully');
+ ctx.collectorUrl = `https://collector-${this._config.PX_APP_ID}.perimeterx.net`;
- if (pxCtx.whitelistRoute) {
- pxLogger.debug(`Whitelist route match: ${pxCtx.uri}`);
+ if (ctx.whitelistRoute) {
+ this.logger.debug(`Whitelist route match: ${ctx.uri}`);
return cb();
}
- this.verifyUserScore(pxCtx, pxConfig, () => {
- this.handleVerification(pxCtx, pxConfig, req, res, cb);
+ this.verifyUserScore(ctx, () => {
+ this.handleVerification(ctx, req, res, cb);
});
} catch (err) {
return cb();
}
}
- verifyUserScore(pxCtx, pxConfig, callback) {
+ verifyUserScore(ctx, callback) {
const startRiskRtt = Date.now();
- pxCtx.riskRtt = 0;
+ ctx.riskRtt = 0;
try {
- if (!pxCtx.ip || !pxCtx.uri) {
- pxLogger.error('perimeterx score evaluation failed. bad parameters.');
- return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
+ if (!ctx.ip || !ctx.uri) {
+ this.logger.error('perimeterx score evaluation failed. bad parameters.');
+ return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
}
- const action = pxCookie.evalCookie(pxCtx);
+ const action = pxCookie.evalCookie(ctx, this._config);
/* score did not cross threshold - pass traffic */
- if (action === pxConfig.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
- return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
+ if (action === this._config.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
+ return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_PASS_TRAFFIC);
}
/* score crossed threshold - block traffic */
- if (action === pxConfig.SCORE_EVALUATE_ACTION.BAD_SCORE) {
- pxCtx.blockReason = 'cookie_high_score';
- return callback(pxConfig.SCORE_EVALUATE_ACTION.COOKIE_BLOCK_TRAFFIC);
+ if (action === this._config.SCORE_EVALUATE_ACTION.BAD_SCORE) {
+ ctx.blockReason = 'cookie_high_score';
+ return callback(this._config.SCORE_EVALUATE_ACTION.COOKIE_BLOCK_TRAFFIC);
}
/* when no fallback to s2s call if cookie does not exist or failed on evaluation */
- pxApi.evalByServerCall(pxCtx, (action) => {
- pxCtx.riskRtt = Date.now() - startRiskRtt;
+ pxApi.evalByServerCall(ctx, this._config, (action) => {
+ ctx.riskRtt = Date.now() - startRiskRtt;
- if (action === pxConfig.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT) {
- pxLogger.error('perimeterx score evaluation failed. unexpected error. passing traffic');
- return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+ if (action === this._config.SCORE_EVALUATE_ACTION.UNEXPECTED_RESULT) {
+ this.logger.error('perimeterx score evaluation failed. unexpected error. passing traffic');
+ return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
}
- pxLogger.debug(`Risk API response returned successfully, risk score: ${pxCtx.score}, round_trip_time: ${pxCtx.riskRtt}ms`);
+ this.logger.debug(`Risk API response returned successfully, risk score: ${ctx.score}, round_trip_time: ${ctx.riskRtt}ms`);
- if (action === pxConfig.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
- pxLogger.debug(`Risk score is lower than blocking score. score: ${pxCtx.score} blocking score: ${pxConfig.BLOCKING_SCORE}`);
- return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+ if (action === this._config.SCORE_EVALUATE_ACTION.GOOD_SCORE) {
+ this.logger.debug(`Risk score is lower than blocking score. score: ${ctx.score} blocking score: ${this._config.BLOCKING_SCORE}`);
+ return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
}
- if (action === pxConfig.SCORE_EVALUATE_ACTION.BAD_SCORE) {
- pxLogger.debug(`Risk score is higher or equal to blocking score. score: ${pxCtx.score} blocking score: ${pxConfig.BLOCKING_SCORE}`);
- switch (pxCtx.blockAction) {
+ if (action === this._config.SCORE_EVALUATE_ACTION.BAD_SCORE) {
+ this.logger.debug(`Risk score is higher or equal to blocking score. score: ${ctx.score} blocking score: ${this._config.BLOCKING_SCORE}`);
+ switch (ctx.blockAction) {
case 'j':
- pxCtx.blockReason = 'challenge';
+ ctx.blockReason = 'challenge';
break;
case 'r':
- pxCtx.blockReason = 'exceeded_rate_limit';
+ ctx.blockReason = 'exceeded_rate_limit';
break;
default:
- pxCtx.blockReason = 's2s_high_score';
+ ctx.blockReason = 's2s_high_score';
}
- return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_BLOCK_TRAFFIC);
+ return callback(this._config.SCORE_EVALUATE_ACTION.S2S_BLOCK_TRAFFIC);
}
- if(action === pxConfig.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS) {
- pxLogger.debug(`Risk API timed out , round_trip_time: ${pxCtx.riskRtt}ms`);
- return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
+ if(action === this._config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS) {
+ this.logger.debug(`Risk API timed out , round_trip_time: ${ctx.riskRtt}ms`);
+ return callback(this._config.SCORE_EVALUATE_ACTION.S2S_TIMEOUT_PASS);
}
});
} catch (e) {
- pxLogger.error('perimeterx score evaluation failed. unexpected error. ' + e.message);
- pxCtx.riskRtt = Date.now() - startRiskRtt;
- return callback(pxConfig.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
+ this.logger.error('perimeterx score evaluation failed. unexpected error. ' + e.message);
+ ctx.riskRtt = Date.now() - startRiskRtt;
+ return callback(this._config.SCORE_EVALUATE_ACTION.S2S_PASS_TRAFFIC);
}
}
- handleVerification(pxCtx, pxConfig, req, res, cb) {
- const verified = pxCtx.score < pxConfig.BLOCKING_SCORE;
+ handleVerification(ctx, req, res, cb) {
+ const verified = ctx.score < this._config.BLOCKING_SCORE;
if (res) {
const setCookie = res.getHeader('Set-Cookie') ? res.getHeader('Set-Cookie') : '';
- const pxhdCookie = pxCtx.pxhd ? '_pxhd=' + pxCtx.pxhd : '';
+ const pxhdCookie = ctx.pxhd ? '_pxhd=' + ctx.pxhd : '';
const setCookieModified = [setCookie, pxhdCookie].filter(Boolean);
if (setCookieModified.length > 0) {
res.setHeader('Set-Cookie', setCookieModified);
@@ -143,24 +150,24 @@ class PxEnforcer {
}
// Handle async activities
if (verified) {
- this.pxPass(pxCtx);
+ this.pxPass(ctx);
} else {
- this.pxBlock(pxCtx, pxConfig);
+ this.pxBlock(ctx);
}
// check for additional activity handler
- if (pxConfig.ADDITIONAL_ACTIVITY_HANDLER) {
- pxConfig.ADDITIONAL_ACTIVITY_HANDLER(pxCtx, pxConfig);
+ if (this._config.ADDITIONAL_ACTIVITY_HANDLER) {
+ this._config.ADDITIONAL_ACTIVITY_HANDLER(ctx, this._config);
}
- if (pxConfig.CUSTOM_REQUEST_HANDLER) {
+ if (this._config.CUSTOM_REQUEST_HANDLER) {
if (res) {
- pxConfig.CUSTOM_REQUEST_HANDLER(pxCtx, pxConfig, req, res);
+ this._config.CUSTOM_REQUEST_HANDLER(ctx, this._config, req, res);
if (res.headersSent) {
return;
}
} else {
- const result = pxConfig.CUSTOM_REQUEST_HANDLER(pxCtx, pxConfig, req);
+ const result = this._config.CUSTOM_REQUEST_HANDLER(ctx, this._config, req);
if (result) {
return cb(null, result);
}
@@ -168,22 +175,23 @@ class PxEnforcer {
}
// If verified, pass the request here
- const shouldBypassMonitor = pxConfig.BYPASS_MONITOR_HEADER && req.headers[pxConfig.BYPASS_MONITOR_HEADER] === '1';
- if (verified || (pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR && !shouldBypassMonitor)) {
+ const shouldBypassMonitor = this._config.BYPASS_MONITOR_HEADER && req.headers[this._config.BYPASS_MONITOR_HEADER] === '1';
+ if (verified || (this._config.MODULE_MODE === this._config.MONITOR_MODE.MONITOR && !shouldBypassMonitor)) {
return cb();
}
const acceptHeaderValue = req.headers['accept'] || req.headers['content-type'];
- const isJsonResponse = acceptHeaderValue && acceptHeaderValue.split(',').find((value) => value.toLowerCase() === 'application/json') && pxCtx.cookieOrigin === 'cookie' && pxCtx.blockAction !== 'r';
+ const isJsonResponse = acceptHeaderValue && acceptHeaderValue.split(',').find((value) => value.toLowerCase() === 'application/json') && ctx.cookieOrigin === 'cookie' && ctx.blockAction !== 'r';
- pxLogger.debug(`Enforcing action: ${pxUtil.parseAction(pxCtx.blockAction)} page is served ${isJsonResponse ? 'using advanced protection mode' : ''}`);
- this.generateResponse(pxCtx, pxConfig, isJsonResponse, function (responseObject) {
+ this.logger.debug(`Enforcing action: ${pxUtil.parseAction(ctx.blockAction)} page is served ${isJsonResponse ? 'using advanced protection mode' : ''}`);
+ const config = this._config;
+ this.generateResponse(ctx, isJsonResponse, function (responseObject) {
const response = {
status: '403',
statusDescription: 'Forbidden'
};
- if (pxCtx.blockAction === 'r') {
+ if (ctx.blockAction === 'r') {
response.status = '429';
response.statusDescription = 'Too Many Requests';
}
@@ -205,79 +213,80 @@ class PxEnforcer {
response.header = {key: 'Content-Type', value: 'text/html'};
response.body = responseObject;
- if (pxCtx.cookieOrigin !== 'cookie') {
+ if (ctx.cookieOrigin !== 'cookie') {
response.header = {key: 'Content-Type', value: 'application/json'};
response.body = {
- action: pxUtil.parseAction(pxCtx.blockAction),
- uuid: pxCtx.uuid,
- vid: pxCtx.vid,
- appId: pxConfig.PX_APP_ID,
+ action: pxUtil.parseAction(ctx.blockAction),
+ uuid: ctx.uuid,
+ vid: ctx.vid,
+ appId: config.PX_APP_ID,
page: new Buffer(responseObject).toString('base64'),
- collectorUrl: pxCtx.collectorUrl
+ collectorUrl: ctx.collectorUrl
};
}
cb(null, response);
});
}
-
+
get config() {
- return PxConfig;
+ return this.pxConfig;
}
/**
* pxPass - pass handler, sends page_requested activity and passes the request using next()
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
+ * @param {Object} config - perimeterx config.
*/
- pxPass(pxCtx) {
+ pxPass(ctx) {
const details = {
- 'px_cookie': pxCtx.decodedCookie,
- 'client_uuid': pxCtx.uuid,
- 'pass_reason': pxCtx.passReason,
- 'risk_rtt': pxCtx.riskRtt,
- 'module_version': PxConfig.conf.MODULE_VERSION
+ 'px_cookie': ctx.decodedCookie,
+ 'client_uuid': ctx.uuid,
+ 'pass_reason': ctx.passReason,
+ 'risk_rtt': ctx.riskRtt,
+ 'module_version': this.pxConfig.conf.MODULE_VERSION
};
- pxLogger.debug('Sending page requested activity');
- this.pxClient.sendToPerimeterX('page_requested', details, pxCtx);
+ this.logger.debug('Sending page requested activity');
+ this.pxClient.sendToPerimeterX('page_requested', details, ctx, this._config);
}
/**
* pxBlock - block handler, send blocking activity to px and render the block html back to screen
*
- * @param pxConfig
- * @param {Object} pxCtx - current request context.
+ * @param {Object} ctx - current request context.
+ * @param config
*/
- pxBlock(pxCtx, pxConfig) {
+ pxBlock(ctx) {
const details = {
- block_reason: pxCtx.blockReason,
- client_uuid: pxCtx.uuid,
+ block_reason: ctx.blockReason,
+ client_uuid: ctx.uuid,
block_module: 'px-node-express',
- block_score: pxCtx.score,
- module_version: PxConfig.conf.MODULE_VERSION,
- simulated_block: pxConfig.MODULE_MODE === pxConfig.MONITOR_MODE.MONITOR
+ block_score: ctx.score,
+ module_version: this.pxConfig.conf.MODULE_VERSION,
+ simulated_block: this._config.MODULE_MODE === this._config.MONITOR_MODE.MONITOR
};
- pxLogger.debug(`Sending block activity`);
- this.pxClient.sendToPerimeterX('block', details, pxCtx);
+ this.logger.debug(`Sending block activity`);
+ this.pxClient.sendToPerimeterX('block', details, ctx, this._config);
}
/**
- * generateResponse - genarating HTML string from pxContext and pxConfig in case
+ * generateResponse - genarating HTML string from ctx in case
* action was to block the request
*
- * @param {Object} pxContext - current request context.
- * @param {Object} pxConfig - current Px configs
+ * @param {Object} ctx - current request context.
+ * @param {Object} config - current Px configs
* @param {Function} cb - send the generated html value.
*/
- generateResponse(pxContext, pxConfig, jsonResponse, cb) {
+ generateResponse(ctx, jsonResponse, cb) {
let template;
- switch (pxContext.blockAction) {
+ switch (ctx.blockAction) {
case 'j':
- return cb(pxContext.blockActionData);
+ return cb(ctx.blockActionData);
case 'r':
return this.compileMustache('ratelimit', {}, cb);
}
- const props = this.getProps(pxContext, pxConfig, template);
+ const props = this.getProps(ctx, template);
if (jsonResponse) {
return cb(props);
} else {
@@ -285,30 +294,30 @@ class PxEnforcer {
}
}
- getProps(pxContext, pxConfig) {
- let jsClientSrc = `//${pxConfig.CLIENT_HOST}/${pxConfig.PX_APP_ID}/main.min.js`;
- let captchaSrc = `//${pxConfig.CAPTCHA_HOST}/${pxConfig.PX_APP_ID}/captcha.js?a=${pxContext.blockAction}&u=${pxContext.uuid}&v=${pxContext.vid || ''}&m=${pxContext.isMobile() ? '1' :'0'}`;
- let hostUrl = pxContext.collectorUrl;
+ getProps(ctx) {
+ let jsClientSrc = `//${this._config.CLIENT_HOST}/${this._config.PX_APP_ID}/main.min.js`;
+ let captchaSrc = `//${this._config.CAPTCHA_HOST}/${this._config.PX_APP_ID}/captcha.js?a=${ctx.blockAction}&u=${ctx.uuid}&v=${ctx.vid || ''}&m=${ctx.isMobile() ? '1' :'0'}`;
+ let hostUrl = ctx.collectorUrl;
- if (pxConfig.FIRST_PARTY_ENABLED && !pxContext.isMobile()) {
- const prefix = pxConfig.PX_APP_ID.substring(2);
- jsClientSrc = `/${prefix}${pxConfig.FIRST_PARTY_VENDOR_PATH}`;
- captchaSrc = `/${prefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}/captcha.js?a=${pxContext.blockAction}&u=${pxContext.uuid}&v=${pxContext.vid || ''}&m=${pxContext.isMobile() ? '1' :'0'}`;
- hostUrl = `/${prefix}${pxConfig.FIRST_PARTY_XHR_PATH}`;
+ if (this._config.FIRST_PARTY_ENABLED && !ctx.isMobile()) {
+ const prefix = this._config.PX_APP_ID.substring(2);
+ jsClientSrc = `/${prefix}${this._config.FIRST_PARTY_VENDOR_PATH}`;
+ captchaSrc = `/${prefix}${this._config.FIRST_PARTY_CAPTCHA_PATH}/captcha.js?a=${ctx.blockAction}&u=${ctx.uuid}&v=${ctx.vid || ''}&m=${ctx.isMobile() ? '1' :'0'}`;
+ hostUrl = `/${prefix}${this._config.FIRST_PARTY_XHR_PATH}`;
}
return {
- refId: pxContext.uuid,
- appId: pxConfig.PX_APP_ID,
- vid: pxContext.vid,
- uuid: pxContext.uuid,
- customLogo: pxConfig.CUSTOM_LOGO,
- cssRef: pxConfig.CSS_REF,
- jsRef: pxConfig.JS_REF,
- logoVisibility: pxConfig.CUSTOM_LOGO ? 'visible' : 'hidden',
+ refId: ctx.uuid,
+ appId: this._config.PX_APP_ID,
+ vid: ctx.vid,
+ uuid: ctx.uuid,
+ customLogo: this._config.CUSTOM_LOGO,
+ cssRef: this._config.CSS_REF,
+ jsRef: this._config.JS_REF,
+ logoVisibility: this._config.CUSTOM_LOGO ? 'visible' : 'hidden',
hostUrl: hostUrl,
jsClientSrc: jsClientSrc,
- firstPartyEnabled: pxConfig.FIRST_PARTY_ENABLED,
+ firstPartyEnabled: this._config.FIRST_PARTY_ENABLED,
blockScript: captchaSrc
};
}
diff --git a/lib/pxhttpc.js b/lib/pxhttpc.js
index b87426d3..f0d1c4cb 100644
--- a/lib/pxhttpc.js
+++ b/lib/pxhttpc.js
@@ -1,7 +1,6 @@
'use strict';
+
const request = require('./request');
-const pxConfig = require('./pxconfig');
-const pxLogger = require('./pxlogger');
module.exports = {
callServer
@@ -16,9 +15,8 @@ module.exports = {
* @param {string} callType - indication for a query or activities sending
* @param {Function} callback - callback function.
*/
-function callServer(data, headers, uri, callType, callback) {
- callback = callback || ((err) => { err && pxLogger.debug(`callServer default callback. Error: ${err}`); });
- const config = pxConfig.conf;
+function callServer(data, headers, uri, callType, config, callback) {
+ callback = callback || ((err) => { err && config.logger.debug(`callServer default callback. Error: ${err}`); });
const callData = {
'url': `https://${config.SERVER_HOST}${uri}`,
'data': JSON.stringify(data),
@@ -28,7 +26,7 @@ function callServer(data, headers, uri, callType, callback) {
callData.timeout = callType === 'query' ? config.API_TIMEOUT_MS : config.ACTIVITIES_TIMEOUT;
try {
- request.post(callData, function (err, response) {
+ request.post(callData, config, function (err, response) {
let data;
if (err) {
if (err == 'Error: Timeout has been reached.') {
diff --git a/lib/pxlogger.js b/lib/pxlogger.js
index bf7e6aaf..eff1ddb6 100644
--- a/lib/pxlogger.js
+++ b/lib/pxlogger.js
@@ -3,22 +3,28 @@
* Version 1.0 Published 12 May 2016
*/
-module.exports = {
- error: error,
- debug: debug
-};
+class PxLogger {
+ constructor() {
+ this.debugMode = false;
+ this.appId = '';
+ }
-function debug(msg) {
- const pxConfig = require('./pxconfig');
- if (pxConfig.conf.DEBUG_MODE && msg) {
- console.info(`[PerimeterX - DEBUG][${pxConfig.conf.PX_APP_ID}] - ${msg}`);
+ init(pxConfig) {
+ this.debugMode = pxConfig.conf.DEBUG_MODE;
+ this.appId = pxConfig.conf.PX_APP_ID;
}
-}
-function error(msg) {
- const pxConfig = require('./pxconfig');
- if (typeof msg === 'string') {
- console.error(new Error(`[PerimeterX - ERROR][${pxConfig.conf.PX_APP_ID}] - ${msg}`).stack);
+ debug(msg) {
+ if (this.debugMode && msg) {
+ console.info(`[PerimeterX - DEBUG][${this.appId}] - ${msg}`);
+ }
+ }
+
+ error(msg) {
+ if (typeof msg === 'string') {
+ console.error(new Error(`[PerimeterX - ERROR][${this.appId}] - ${msg}`).stack);
+ }
}
}
+module.exports = PxLogger;
diff --git a/lib/pxoriginaltoken.js b/lib/pxoriginaltoken.js
index b3792491..b38a76f4 100644
--- a/lib/pxoriginaltoken.js
+++ b/lib/pxoriginaltoken.js
@@ -1,33 +1,32 @@
const TokenV3 = require('./cookie/tokenV3');
const TokenV1 = require('./cookie/tokenV1');
-const pxLogger = require('./pxlogger');
-function evalCookie(pxCtx, pxConfig) {
+function evalCookie(ctx, config) {
try {
- const noVersionOriginalToken = pxCtx.originalToken;
- const cookie = (pxCtx.cookies['_px3'] ? new TokenV3(pxCtx, pxConfig, noVersionOriginalToken) : new TokenV1(pxCtx, pxConfig, noVersionOriginalToken));
- pxLogger.debug('Original token found, Evaluating');
+ const noVersionOriginalToken = ctx.originalToken;
+ const cookie = (ctx.cookies['_px3'] ? new TokenV3(ctx, config, noVersionOriginalToken) : new TokenV1(ctx, config, noVersionOriginalToken));
+ config.logger.debug('Original token found, Evaluating');
if (!cookie.deserialize()) {
- pxLogger.debug(`Original token decryption failed, value: ${pxCtx.originalToken}`);
- pxCtx.originalTokenError = 'decryption_failed';
+ config.logger.debug(`Original token decryption failed, value: ${ctx.originalToken}`);
+ ctx.originalTokenError = 'decryption_failed';
return;
}
- pxCtx.decodedOriginalToken = cookie.decodedCookie;
- pxCtx.vid = cookie.getVid();
- pxCtx.originalUuid = cookie.getUuid();
+ ctx.decodedOriginalToken = cookie.decodedCookie;
+ ctx.vid = cookie.getVid();
+ ctx.originalUuid = cookie.getUuid();
if (!cookie.isSecure()) {
- pxLogger.debug(`Original token HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${pxCtx.userAgent}`);
- pxCtx.originalTokenError = 'validation_failed';
+ config.logger.debug(`Original token HMAC validation failed, value: ${JSON.stringify(cookie.decodedCookie)} user-agent: ${ctx.userAgent}`);
+ ctx.originalTokenError = 'validation_failed';
return;
}
return;
} catch (e) {
- pxLogger.error(`Error while evaluating perimeterx original token: ${e.message}`);
- pxCtx.originalTokenError = 'decryption_failed';
+ config.logger.error(`Error while evaluating perimeterx original token: ${e.message}`);
+ ctx.originalTokenError = 'decryption_failed';
return;
}
}
diff --git a/lib/pxpayload.js b/lib/pxpayload.js
index 96ca6998..2d89e8da 100644
--- a/lib/pxpayload.js
+++ b/lib/pxpayload.js
@@ -1,11 +1,9 @@
'use strict';
-const pxLogger = require('./pxlogger');
const crypto = require('crypto');
-
class Payload {
- constructor() {
+ constructor(config) {
//cookie string
this.pxCookie = '';
@@ -13,10 +11,10 @@ class Payload {
this.decodedCookie = '';
//PerimeterX configuration object
- this.pxConfig = {};
+ this.config = config;
//PerimeterX context
- this.pxContext = {};
+ this.ctx = {};
//cookie secret string
this.cookieSecret = '';
@@ -27,7 +25,7 @@ class Payload {
* @returns {boolean}
*/
isHighScore() {
- return this.getScore() >= this.pxConfig.BLOCKING_SCORE;
+ return this.getScore() >= this.config.BLOCKING_SCORE;
}
/**
@@ -69,7 +67,7 @@ class Payload {
return true;
}
let cookie;
- if (this.pxConfig.COOKIE_ENCRYPTION) {
+ if (this.config.COOKIE_ENCRYPTION) {
cookie = this.decrypt();
} else {
cookie = this.decode();
@@ -90,47 +88,47 @@ class Payload {
try {
const data = this.pxCookie.split(':');
if (data.length !== 3) {
- pxLogger.debug('invalid cookie format - wrong number of parts');
+ this.config.logger.debug('invalid cookie format - wrong number of parts');
return '';
}
const iterations = Number(data[1]);
const encryptedCookie = data[2];
/* iterations value is not a number */
if (!iterations) {
- pxLogger.debug('invalid cookie format - iterations value is not a number');
+ this.config.logger.debug('invalid cookie format - iterations value is not a number');
return '';
}
/* iterations value is not in the legit range */
if (iterations > 5000 || iterations < 500) {
- pxLogger.debug('invalid cookie format - iterations out of bounds');
+ this.config.logger.debug('invalid cookie format - iterations out of bounds');
return '';
}
/* salt value is not as expected */
if (!data[0] || typeof data[0] !== 'string' || data[0].length > 100) {
- pxLogger.debug('invalid cookie format - invalid salt value');
+ this.config.logger.debug('invalid cookie format - invalid salt value');
return '';
}
/* cookie value is not as expected */
if (!encryptedCookie || typeof encryptedCookie !== 'string') {
- pxLogger.debug('invalid cookie format - no cookie value');
+ this.config.logger.debug('invalid cookie format - no cookie value');
return '';
}
const salt = new Buffer(data[0], 'base64');
- const derivation = crypto.pbkdf2Sync(this.cookieSecret, salt, iterations, this.pxConfig.CE_KEYLEN + this.pxConfig.CE_IVLEN, this.pxConfig.CE_DIGEST);
- const key = derivation.slice(0, this.pxConfig.CE_KEYLEN);
- const iv = derivation.slice(this.pxConfig.CE_KEYLEN);
+ const derivation = crypto.pbkdf2Sync(this.cookieSecret, salt, iterations, this.config.CE_KEYLEN + this.config.CE_IVLEN, this.config.CE_DIGEST);
+ const key = derivation.slice(0, this.config.CE_KEYLEN);
+ const iv = derivation.slice(this.config.CE_KEYLEN);
- const cipher = crypto.createDecipheriv(this.pxConfig.CE_ALGO, key, iv);
+ const cipher = crypto.createDecipheriv(this.config.CE_ALGO, key, iv);
let decrypted = cipher.update(encryptedCookie, 'base64', 'utf8');
decrypted += cipher.final('utf8');
return JSON.parse(decrypted);
} catch (err) {
- pxLogger.error('Error while decrypting Perimeterx cookie: ' + err.message);
+ this.config.logger.error('Error while decrypting Perimeterx cookie: ' + err.message);
return '';
}
}
@@ -145,14 +143,14 @@ class Payload {
isHmacValid(hmacStr, cookieHmac) {
try {
- const hmac = crypto.createHmac(this.pxConfig.CE_DIGEST, this.cookieSecret);
+ const hmac = crypto.createHmac(this.config.CE_DIGEST, this.cookieSecret);
hmac.setEncoding('hex');
hmac.write(hmacStr);
hmac.end();
const h = hmac.read();
return h === cookieHmac;
} catch (err) {
- pxLogger.error('Error while validating Perimeterx cookie: ' + err.stack);
+ this.config.logger.error('Error while validating Perimeterx cookie: ' + err.stack);
}
}
}
diff --git a/lib/pxproxy.js b/lib/pxproxy.js
index 98d3055c..64d25759 100644
--- a/lib/pxproxy.js
+++ b/lib/pxproxy.js
@@ -1,5 +1,4 @@
const request = require('./request');
-const pxLogger = require('./pxlogger');
const pxUtil = require('./pxutil');
const rawBody = require('raw-body');
const contentType = require('content-type');
@@ -8,7 +7,7 @@ const contentType = require('content-type');
* getClient - process the proxy request to get the captcha script file from PX servers.
*
* @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
* @param {string} ip - the ip that initiated the call.
* @param {string} reversePrefix - the prefix of the xhr request.
* @param {function} cb - the callback function to call at the end of the process.
@@ -16,9 +15,9 @@ const contentType = require('content-type');
* @return {function} the callback function passed as param with the server response (captcha file) or error.
*
*/
-function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
+function getCaptcha(req, config, ip, reversePrefix, cb) {
let res = {};
- if (!pxConfig.FIRST_PARTY_ENABLED) {
+ if (!config.FIRST_PARTY_ENABLED) {
res = {
status: 200,
header: {key: 'Content-Type', value:'application/javascript'},
@@ -26,22 +25,23 @@ function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
};
return cb(null, res);
} else {
- const searchMask = `/${reversePrefix}${pxConfig.FIRST_PARTY_CAPTCHA_PATH}`;
+ const searchMask = `/${reversePrefix}${config.FIRST_PARTY_CAPTCHA_PATH}`;
const regEx = new RegExp(searchMask, 'ig');
- const pxRequestUri = `/${pxConfig.PX_APP_ID}${req.originalUrl.replace(regEx, '')}`;
- pxLogger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${pxConfig.CAPTCHA_HOST}${pxRequestUri}`);
+ const pxRequestUri = `/${config.PX_APP_ID}${req.originalUrl.replace(regEx, '')}`;
+ config.logger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${config.CAPTCHA_HOST}${pxRequestUri}`);
const callData = {
- url: `https://${pxConfig.CAPTCHA_HOST}${pxRequestUri}`,
- headers: pxUtil.filterSensitiveHeaders(req.headers),
- timeout: pxConfig.API_TIMEOUT_MS
+ url: `https://${config.CAPTCHA_HOST}${pxRequestUri}`,
+ headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
+ timeout: config.API_TIMEOUT_MS
};
- callData.headers['host'] = pxConfig.CAPTCHA_HOST;
- callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
- callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
- request.get(callData, (error, response) => {
+ callData.headers['host'] = config.CAPTCHA_HOST;
+ callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+ callData.headers[config.FIRST_PARTY_HEADER] = 1;
+ request.get(callData, config, (error, response) => {
if (error || !response) {
- pxLogger.error(`Error while fetching first party captcha: ${error}`);
+ config.logger.error(`Error while fetching first party captcha: ${error}`);
}
+
response = response || {statusCode: 200, headers: {}};
res = {
status: response.statusCode,
@@ -58,16 +58,16 @@ function getCaptcha(req, pxConfig, ip, reversePrefix, cb) {
* getClient - process the proxy request to get the client file from PX servers.
*
* @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
* @param {string} ip - the ip that initiated the call.
* @param {function} cb - the callback function to call at the end of the process.
*
* @return {function} the callback function passed as param with the server response (client file) or error.
*
*/
-function getClient(req, pxConfig, ip, cb) {
+function getClient(req, config, ip, cb) {
let res = {};
- if (!pxConfig.FIRST_PARTY_ENABLED) {
+ if (!config.FIRST_PARTY_ENABLED) {
res = {
status: 200,
header: {key: 'Content-Type', value:'application/javascript'},
@@ -75,19 +75,19 @@ function getClient(req, pxConfig, ip, cb) {
};
return cb(null, res);
} else {
- const clientRequestUri = `/${pxConfig.PX_APP_ID}/main.min.js`;
- pxLogger.debug(`Forwarding request from ${req.originalUrl.toLowerCase()} to client at ${pxConfig.CLIENT_HOST}${clientRequestUri}`);
+ const clientRequestUri = `/${config.PX_APP_ID}/main.min.js`;
+ config.logger.debug(`Forwarding request from ${req.originalUrl.toLowerCase()} to client at ${config.CLIENT_HOST}${clientRequestUri}`);
const callData = {
- url: `https://${pxConfig.CLIENT_HOST}${clientRequestUri}`,
- headers: pxUtil.filterSensitiveHeaders(req.headers),
- timeout: pxConfig.API_TIMEOUT_MS
+ url: `https://${config.CLIENT_HOST}${clientRequestUri}`,
+ headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
+ timeout: config.API_TIMEOUT_MS
};
- callData.headers['host'] = pxConfig.CLIENT_HOST;
- callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
- callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
- request.get(callData, (error, response) => {
+ callData.headers['host'] = config.CLIENT_HOST;
+ callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+ callData.headers[config.FIRST_PARTY_HEADER] = 1;
+ request.get(callData, config, (error, response) => {
if (error || !response) {
- pxLogger.error(`Error while fetching first party client: ${error}`);
+ config.logger.error(`Error while fetching first party client: ${error}`);
}
response = response || {statusCode: 200, headers: {}};
@@ -106,7 +106,7 @@ function getClient(req, pxConfig, ip, cb) {
* sendXHR - process the proxy request for sending activities to PX servers.
*
* @param {object} req - the request object.
- * @param {object} pxConfig - the PerimeterX config object.
+ * @param {object} config - the PerimeterX config object.
* @param {string} ip - the ip that initiated the call.
* @param {string} reversePrefix - the prefix of the xhr request.
* @param {function} cb - the callback function to call at the end of the process.
@@ -114,15 +114,15 @@ function getClient(req, pxConfig, ip, cb) {
* @return {function} the callback function passed as param with the server response or error.
*
*/
-function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
+function sendXHR(req, config, ip, reversePrefix, cb) {
let res = {};
let vid;
- if (!pxConfig.FIRST_PARTY_ENABLED || !pxConfig.FIRST_PARTY_XHR_ENABLED) {
+ if (!config.FIRST_PARTY_ENABLED || !config.FIRST_PARTY_XHR_ENABLED) {
if (req.originalUrl.toLowerCase().includes('gif')) {
res = {
status: 200,
header: {key: 'Content-Type', value:'image/gif'},
- body: Buffer.from(pxConfig.EMPTY_GIF_B64, 'base64')
+ body: Buffer.from(config.EMPTY_GIF_B64, 'base64')
};
} else {
res = {
@@ -135,21 +135,21 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
}
// handle proxy
- parseBody(req).then(() => {
- const searchMask = `/${reversePrefix}${pxConfig.FIRST_PARTY_XHR_PATH}`;
+ parseBody(req, config.logger).then(() => {
+ const searchMask = `/${reversePrefix}${config.FIRST_PARTY_XHR_PATH}`;
const regEx = new RegExp(searchMask, 'ig');
const pxRequestUri = req.originalUrl.replace(regEx, '');
- pxLogger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${pxConfig.COLLECTOR_HOST}${pxRequestUri}`);
+ config.logger.debug(`Forwarding request from ${req.originalUrl} to xhr at ${config.COLLECTOR_HOST}${pxRequestUri}`);
const callData = {
- url: `https://${pxConfig.COLLECTOR_HOST}${pxRequestUri}`,
- headers: pxUtil.generateProxyHeaders(req.headers, req.ip),
- timeout: pxConfig.API_TIMEOUT_MS
+ url: `https://${config.COLLECTOR_HOST}${pxRequestUri}`,
+ headers: pxUtil.generateProxyHeaders(req.headers, req.ip, config.SENSITIVE_HEADERS, config.FORWARDED_FOR_HEADER),
+ timeout: config.API_TIMEOUT_MS
};
- callData.headers['host'] = pxConfig.COLLECTOR_HOST;
- callData.headers[pxConfig.ENFORCER_TRUE_IP_HEADER] = ip;
- callData.headers[pxConfig.FIRST_PARTY_HEADER] = 1;
+ callData.headers['host'] = config.COLLECTOR_HOST;
+ callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
+ callData.headers[config.FIRST_PARTY_HEADER] = 1;
if (req.rawBody) {
callData.headers['content-length'] = Buffer.byteLength(req.rawBody); // makes sure the length of the body is correct, as the body might go trough different body parsers.
callData.data = req.rawBody;
@@ -173,7 +173,7 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
}
if (req.method === 'POST') {
- request.post(callData, (error, response) => {
+ request.post(callData, config, (error, response) => {
if (error || !response || response.statusCode >= 400) {
if (req.originalUrl.toLowerCase().includes('/beacon')) {
res = {
@@ -197,13 +197,13 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
return cb(null, res);
});
} else if (req.method === 'GET') {
- request.get(callData, (error, response) => {
+ request.get(callData, config, (error, response) => {
if (error || !response || response.statusCode >= 400) {
if (req.originalUrl.toLowerCase().includes('.gif')) {
res = {
status: 200,
header: {key: 'Content-Type', value:'image/gif'},
- body: Buffer.from(pxConfig.EMPTY_GIF_B64, 'base64')
+ body: Buffer.from(config.EMPTY_GIF_B64, 'base64')
};
} else {
res = {
@@ -225,7 +225,7 @@ function sendXHR(req, pxConfig, ip, reversePrefix, cb) {
});
}
-function parseBody(req) {
+function parseBody(req, pxLogger) {
return new Promise((resolve) => {
try {
if (req.method == 'GET') {
diff --git a/lib/pxtesthandler.js b/lib/pxtesthandler.js
index fe1065a9..9ff1b41c 100644
--- a/lib/pxtesthandler.js
+++ b/lib/pxtesthandler.js
@@ -2,37 +2,37 @@
module.exports = {
testModeRequestHandler: customRequestHandler};
-function customRequestHandler(pxCtx, pxconfig, req, res) {
+function customRequestHandler(ctx, config, req, res) {
const result = {
- px_cookies : pxCtx.cookies,
- uuid: pxCtx.uuid,
- vid: pxCtx.vid,
- ip: pxCtx.ip,
- full_url: pxCtx.fullUrl,
- score: pxCtx.score,
- px_cookie_hmac : pxCtx.hmac,
- block_action: pxCtx.blockAction,
- http_method: pxCtx.httpMethod,
- hostname: pxCtx.hostname,
- headers: pxCtx.headers,
- user_agent: pxCtx.userAgent,
- uri: pxCtx.uri,
- is_made_s2s_api_call: pxCtx.hasMadeServerCall || false,
- sensitive_route: pxCtx.sensitiveRoute,
- sensitive_routes_list: pxconfig.SENSITIVE_ROUTES,
- whitelist_routes: pxconfig.WHITELIST_ROUTES,
- decoded_px_cookie: pxCtx.decodedCookie,
- cookie_origin: pxCtx.cookieOrigin,
- http_version: pxCtx.httpVersion,
- s2s_call_reason: pxCtx.s2sCallReason || 'none',
- block_reason: pxCtx.blockReason || 'none',
- module_mode: pxconfig.MODULE_MODE
+ px_cookies : ctx.cookies,
+ uuid: ctx.uuid,
+ vid: ctx.vid,
+ ip: ctx.ip,
+ full_url: ctx.fullUrl,
+ score: ctx.score,
+ px_cookie_hmac : ctx.hmac,
+ block_action: ctx.blockAction,
+ http_method: ctx.httpMethod,
+ hostname: ctx.hostname,
+ headers: ctx.headers,
+ user_agent: ctx.userAgent,
+ uri: ctx.uri,
+ is_made_s2s_api_call: ctx.hasMadeServerCall || false,
+ sensitive_route: ctx.sensitiveRoute,
+ sensitive_routes_list: config.SENSITIVE_ROUTES,
+ whitelist_routes: config.WHITELIST_ROUTES,
+ decoded_px_cookie: ctx.decodedCookie,
+ cookie_origin: ctx.cookieOrigin,
+ http_version: ctx.httpVersion,
+ s2s_call_reason: ctx.s2sCallReason || 'none',
+ block_reason: ctx.blockReason || 'none',
+ module_mode: config.MODULE_MODE
};
- if (pxCtx.originalUuid) {
- result['original_uuid'] = pxCtx.originalUuid;
+ if (ctx.originalUuid) {
+ result['original_uuid'] = ctx.originalUuid;
}
- if (pxCtx.originalTokenError) {
- result['original_token_error'] = pxCtx.originalTokenError;
+ if (ctx.originalTokenError) {
+ result['original_token_error'] = ctx.originalTokenError;
}
res.json(result);
diff --git a/lib/pxutil.js b/lib/pxutil.js
index 8a68a126..598cf889 100644
--- a/lib/pxutil.js
+++ b/lib/pxutil.js
@@ -1,5 +1,7 @@
'use strict';
+const net = require('net');
+
/**
* PerimeterX (http://www.perimeterx.com) NodeJS-Express SDK
* Version 1.0 Published 12 May 2016
@@ -11,8 +13,7 @@
* @param {Object} headers - request headers in key value format.
* @return {Array} request headers an array format.
*/
-function formatHeaders(headers) {
- const pxConfig = require('./pxconfig').conf;
+function formatHeaders(headers, sensitiveHeaders) {
const retval = [];
try {
if (!headers || typeof headers !== 'object' || Object.keys(headers).length === 0) {
@@ -20,7 +21,7 @@ function formatHeaders(headers) {
}
for (const header in headers) {
- if (header && headers[header] && pxConfig.SENSITIVE_HEADERS.indexOf(header) === -1) {
+ if (header && headers[header] && sensitiveHeaders.indexOf(header) === -1) {
retval.push({name: header, value: headers[header]});
}
}
@@ -55,12 +56,9 @@ function checkForStatic(req, exts) {
*
* @return {object} filtered headers.
*/
-function filterSensitiveHeaders(headers) {
+function filterSensitiveHeaders(headers, sensitiveKeys) {
try {
- const pxConfig = require('./pxconfig').conf;
const retval = {};
-
- const sensitiveKeys = pxConfig.SENSITIVE_HEADERS;
for (const key in headers) {
if (sensitiveKeys.findIndex(item => key.toLowerCase() === item.toLowerCase()) === -1) {
retval[key] = headers[key];
@@ -72,15 +70,14 @@ function filterSensitiveHeaders(headers) {
}
}
-function generateProxyHeaders(headers, ip) {
+function generateProxyHeaders(headers, ip, sensitiveHeaders, forwardedForHeader) {
try {
- const pxConfig = require('./pxconfig').conf;
- const filteredHeaders = filterSensitiveHeaders(headers);
- const xffHeader = Object.keys(filteredHeaders).find(item => item.toLowerCase() === pxConfig.FORWARDED_FOR_HEADER);
+ const filteredHeaders = filterSensitiveHeaders(headers, sensitiveHeaders);
+ const xffHeader = Object.keys(filteredHeaders).find(item => item.toLowerCase() === forwardedForHeader);
if (xffHeader) {
filteredHeaders[xffHeader] += `, ${ip}`;
} else {
- filteredHeaders[pxConfig.FORWARDED_FOR_HEADER] = ip;
+ filteredHeaders[forwardedForHeader] = ip;
}
return filteredHeaders;
} catch(e) {
@@ -165,6 +162,29 @@ function prepareCustomParams(config, dict) {
}
}
+function extractIP(config, req) {
+ let ip;
+ if (Array.isArray(config.IP_HEADERS)) {
+ config.IP_HEADERS.some(ipHeader => {
+ try {
+ const headerValue = req.get(ipHeader);
+ if (headerValue) {
+ ip = headerValue;
+ return true;
+ }
+ } catch (e) {
+ config.logger.debug('Failed to use IP_HEADERS from config.');
+ }
+ });
+ } else {
+ ip = typeof config.GET_USER_IP === 'function' && config.GET_USER_IP(req);
+ }
+ if (ip && net.isIP(ip) > 0) {
+ return ip;
+ }
+ return req.ip;
+}
+
module.exports = {
formatHeaders,
filterSensitiveHeaders,
@@ -174,4 +194,5 @@ module.exports = {
parseAction,
generateProxyHeaders,
prepareCustomParams,
+ extractIP
};
diff --git a/lib/request.js b/lib/request.js
index d0a8a5f6..d33d1449 100644
--- a/lib/request.js
+++ b/lib/request.js
@@ -1,35 +1,21 @@
-const pxLogger = require('./pxlogger');
const p = require('agent-phin').unpromisified;
-const pxConfig = require('./pxconfig');
const https = require('https');
const keepAliveAgent = new https.Agent({ keepAlive: true });
-const request = {
- get: (options, cb) => {
- options.method = 'GET';
- return makeRequest(options, cb);
- },
- post: (options, cb) => {
- options.method = 'POST';
- if (!options.headers['content-type'] && !options.headers['Content-Type']) {
- options.headers['content-type'] = 'application/json';
- }
- return makeRequest(options, cb);
- }
+exports.get = (options, config, cb) => {
+ options.method = 'GET';
+ return makeRequest(options, config, cb);
};
-function makeRequest(options, cb) {
- if (pxConfig.conf.agent) {
- options.agent = pxConfig.conf.agent;
- } else {
- options.agent = keepAliveAgent;
- }
-
- try {
- p(options, cb);
- } catch (e) {
- pxLogger.error(`Error making request: ${e.message}`);
+exports.post = (options, config, cb) => {
+ options.method = 'POST';
+ if (!options.headers['content-type'] && !options.headers['Content-Type']) {
+ options.headers['content-type'] = 'application/json';
}
-}
+ return makeRequest(options, config, cb);
+};
-module.exports = request;
\ No newline at end of file
+function makeRequest(options, config, cb) {
+ options.agent = config.agent || keepAliveAgent;
+ p(options, cb);
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 00000000..23c596c1
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1290 @@
+{
+ "name": "perimeterx-node-core",
+ "version": "1.7.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@babel/code-frame": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
+ "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
+ "dev": true,
+ "requires": {
+ "@babel/highlight": "^7.0.0"
+ }
+ },
+ "@babel/highlight": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz",
+ "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.0",
+ "esutils": "^2.0.2",
+ "js-tokens": "^4.0.0"
+ }
+ },
+ "acorn": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.0.tgz",
+ "integrity": "sha512-MW/FjM+IvU9CgBzjO3UIPCE2pyEwUsoFl+VGdczOPEdxfGFjuKny/gN54mOuX7Qxmb9Rg9MCn2oKiSUeW+pjrw==",
+ "dev": true
+ },
+ "acorn-jsx": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+ "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+ "dev": true
+ },
+ "agent-base": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.2.1.tgz",
+ "integrity": "sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg==",
+ "requires": {
+ "es6-promisify": "^5.0.0"
+ }
+ },
+ "agent-phin": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/agent-phin/-/agent-phin-1.0.3.tgz",
+ "integrity": "sha512-p9aDxReY6SxaVVXYmidCdPFtz+EsRfVodiL/rbIyht9c4kjkUe+RtdMJr5eEuJKCfh3iKKwB6Ntf2UP2E0zL6w==",
+ "requires": {
+ "centra": "^2.2.1"
+ }
+ },
+ "ajv": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
+ "integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
+ "dev": true,
+ "requires": {
+ "fast-deep-equal": "^2.0.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ }
+ },
+ "ansi-escapes": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+ "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+ "dev": true
+ },
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "ansi-styles": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+ "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+ "dev": true,
+ "requires": {
+ "color-convert": "^1.9.0"
+ }
+ },
+ "argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "requires": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "astral-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+ "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+ "dev": true
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
+ "bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg="
+ },
+ "callsites": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+ "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+ "dev": true
+ },
+ "centra": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/centra/-/centra-2.2.2.tgz",
+ "integrity": "sha512-7M9pOa0afioJy+h3+RvaK9BuxtJdo4/cRXoBPoClUcX1YLlaFMIxQlN/Sp4GoDEp2w17qeQ74NDuMdXPaNLToA=="
+ },
+ "chalk": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+ "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ }
+ },
+ "chardet": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+ "dev": true
+ },
+ "circular-json": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+ "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+ "dev": true
+ },
+ "cli-cursor": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+ "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+ "dev": true,
+ "requires": {
+ "restore-cursor": "^2.0.0"
+ }
+ },
+ "cli-width": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+ "dev": true
+ },
+ "color-convert": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+ "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+ "dev": true,
+ "requires": {
+ "color-name": "1.1.3"
+ }
+ },
+ "color-name": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+ "dev": true
+ },
+ "commander": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
+ "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
+ "dev": true
+ },
+ "content-type": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+ "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA=="
+ },
+ "cross-spawn": {
+ "version": "6.0.5",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+ "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
+ "dev": true,
+ "requires": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-is": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+ "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+ "dev": true
+ },
+ "depd": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
+ "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak="
+ },
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true
+ },
+ "doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "requires": {
+ "esutils": "^2.0.2"
+ }
+ },
+ "emoji-regex": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+ "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+ "dev": true
+ },
+ "es6-promise": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.5.tgz",
+ "integrity": "sha512-n6wvpdE43VFtJq+lUDYDBFUwV8TZbuGXLV4D6wKafg13ldznKsyEvatubnmUe31zcvelSzOHF+XbaT+Bl9ObDg=="
+ },
+ "es6-promisify": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
+ "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=",
+ "requires": {
+ "es6-promise": "^4.0.3"
+ }
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
+ "dev": true
+ },
+ "eslint": {
+ "version": "5.13.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.13.0.tgz",
+ "integrity": "sha512-nqD5WQMisciZC5EHZowejLKQjWGuFS5c70fxqSKlnDME+oz9zmE8KTlX+lHSg+/5wsC/kf9Q9eMkC8qS3oM2fg==",
+ "dev": true,
+ "requires": {
+ "@babel/code-frame": "^7.0.0",
+ "ajv": "^6.5.3",
+ "chalk": "^2.1.0",
+ "cross-spawn": "^6.0.5",
+ "debug": "^4.0.1",
+ "doctrine": "^2.1.0",
+ "eslint-scope": "^4.0.0",
+ "eslint-utils": "^1.3.1",
+ "eslint-visitor-keys": "^1.0.0",
+ "espree": "^5.0.0",
+ "esquery": "^1.0.1",
+ "esutils": "^2.0.2",
+ "file-entry-cache": "^2.0.0",
+ "functional-red-black-tree": "^1.0.1",
+ "glob": "^7.1.2",
+ "globals": "^11.7.0",
+ "ignore": "^4.0.6",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "inquirer": "^6.1.0",
+ "js-yaml": "^3.12.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.3.0",
+ "lodash": "^4.17.5",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.1",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.8.2",
+ "path-is-inside": "^1.0.2",
+ "progress": "^2.0.0",
+ "regexpp": "^2.0.1",
+ "semver": "^5.5.1",
+ "strip-ansi": "^4.0.0",
+ "strip-json-comments": "^2.0.1",
+ "table": "^5.0.2",
+ "text-table": "^0.2.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ }
+ }
+ },
+ "eslint-scope": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
+ "integrity": "sha512-1G6UTDi7Jc1ELFwnR58HV4fK9OQK4S6N985f166xqXxpjU6plxFISJa2Ba9KCQuFa8RCnj/lSFJbHo7UFDBnUA==",
+ "dev": true,
+ "requires": {
+ "esrecurse": "^4.1.0",
+ "estraverse": "^4.1.1"
+ }
+ },
+ "eslint-utils": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+ "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+ "dev": true
+ },
+ "eslint-visitor-keys": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+ "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+ "dev": true
+ },
+ "espree": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",
+ "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==",
+ "dev": true,
+ "requires": {
+ "acorn": "^6.0.2",
+ "acorn-jsx": "^5.0.0",
+ "eslint-visitor-keys": "^1.0.0"
+ }
+ },
+ "esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true
+ },
+ "esquery": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+ "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.0.0"
+ }
+ },
+ "esrecurse": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+ "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+ "dev": true,
+ "requires": {
+ "estraverse": "^4.1.0"
+ }
+ },
+ "estraverse": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
+ "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+ "dev": true
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
+ "dev": true
+ },
+ "external-editor": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+ "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+ "dev": true,
+ "requires": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "dependencies": {
+ "iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ }
+ }
+ },
+ "fast-deep-equal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+ "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+ "dev": true
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+ "dev": true
+ },
+ "fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+ "dev": true
+ },
+ "figures": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+ "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+ "dev": true,
+ "requires": {
+ "escape-string-regexp": "^1.0.5"
+ }
+ },
+ "file-entry-cache": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+ "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+ "dev": true,
+ "requires": {
+ "flat-cache": "^1.2.1",
+ "object-assign": "^4.0.1"
+ }
+ },
+ "flat-cache": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
+ "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
+ "dev": true,
+ "requires": {
+ "circular-json": "^0.3.1",
+ "graceful-fs": "^4.1.2",
+ "rimraf": "~2.6.2",
+ "write": "^0.2.1"
+ }
+ },
+ "formatio": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.2.0.tgz",
+ "integrity": "sha1-87IWfZBoxGmKjVH092CjmlTYGOs=",
+ "dev": true,
+ "requires": {
+ "samsam": "1.x"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+ "dev": true
+ },
+ "functional-red-black-tree": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+ "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "globals": {
+ "version": "11.10.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz",
+ "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==",
+ "dev": true
+ },
+ "graceful-fs": {
+ "version": "4.1.15",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+ "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+ "dev": true
+ },
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
+ "has-flag": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+ "dev": true
+ },
+ "he": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
+ "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
+ "dev": true
+ },
+ "http-errors": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
+ "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
+ "requires": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ }
+ },
+ "https-proxy-agent": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.1.tgz",
+ "integrity": "sha512-HPCTS1LW51bcyMYbxUIOO4HEOlQ1/1qRaFWcyxvwaqUS9TY88aoEuHUY33kuAh1YhVVaDQhLZsnPd+XNARWZlQ==",
+ "requires": {
+ "agent-base": "^4.1.0",
+ "debug": "^3.1.0"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.23",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
+ "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+ "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+ "dev": true
+ },
+ "import-fresh": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+ "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+ "dev": true,
+ "requires": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ }
+ },
+ "imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
+ "dev": true
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "dev": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "inquirer": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+ "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
+ "dev": true,
+ "requires": {
+ "ansi-escapes": "^3.2.0",
+ "chalk": "^2.4.2",
+ "cli-cursor": "^2.1.0",
+ "cli-width": "^2.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^2.0.0",
+ "lodash": "^4.17.11",
+ "mute-stream": "0.0.7",
+ "run-async": "^2.2.0",
+ "rxjs": "^6.4.0",
+ "string-width": "^2.1.0",
+ "strip-ansi": "^5.0.0",
+ "through": "^2.3.6"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "strip-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0"
+ }
+ }
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
+ "dev": true
+ },
+ "is-promise": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+ "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+ "dev": true
+ },
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
+ "dev": true
+ },
+ "isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
+ },
+ "js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true
+ },
+ "js-yaml": {
+ "version": "3.12.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+ "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+ "dev": true,
+ "requires": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ }
+ },
+ "json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true
+ },
+ "json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+ "dev": true
+ },
+ "levn": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+ "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2"
+ }
+ },
+ "lodash": {
+ "version": "4.17.11",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+ "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
+ "dev": true
+ },
+ "lolex": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.6.0.tgz",
+ "integrity": "sha1-OpoCg0UqR9dDnnJzG54H1zhuSfY=",
+ "dev": true
+ },
+ "mimic-fn": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz",
+ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
+ "dev": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+ "dev": true
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "mocha": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz",
+ "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==",
+ "dev": true,
+ "requires": {
+ "browser-stdout": "1.3.1",
+ "commander": "2.15.1",
+ "debug": "3.1.0",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "glob": "7.1.2",
+ "growl": "1.10.5",
+ "he": "1.1.1",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "supports-color": "5.4.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
+ "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
+ "dev": true,
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz",
+ "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ }
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg=="
+ },
+ "mu2": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/mu2/-/mu2-0.5.21.tgz",
+ "integrity": "sha1-iIqPD9kOsc/anbgUdvbhmcyeWNM="
+ },
+ "mute-stream": {
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+ "dev": true
+ },
+ "native-promise-only": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
+ "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=",
+ "dev": true
+ },
+ "natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+ "dev": true
+ },
+ "nice-try": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
+ "dev": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "onetime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+ "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+ "dev": true,
+ "requires": {
+ "mimic-fn": "^1.0.0"
+ }
+ },
+ "optionator": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+ "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+ "dev": true,
+ "requires": {
+ "deep-is": "~0.1.3",
+ "fast-levenshtein": "~2.0.4",
+ "levn": "~0.3.0",
+ "prelude-ls": "~1.1.2",
+ "type-check": "~0.3.2",
+ "wordwrap": "~1.0.0"
+ }
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
+ },
+ "parent-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+ "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+ "dev": true,
+ "requires": {
+ "callsites": "^3.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
+ "dev": true
+ },
+ "path-is-inside": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+ "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+ "dev": true
+ },
+ "path-key": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+ "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+ "dev": true
+ },
+ "path-to-regexp": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
+ "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
+ "dev": true,
+ "requires": {
+ "isarray": "0.0.1"
+ }
+ },
+ "prelude-ls": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+ "dev": true
+ },
+ "progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true
+ },
+ "punycode": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+ "dev": true
+ },
+ "raw-body": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
+ "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
+ "requires": {
+ "bytes": "3.0.0",
+ "http-errors": "1.6.3",
+ "iconv-lite": "0.4.23",
+ "unpipe": "1.0.0"
+ }
+ },
+ "regexpp": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+ "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+ "dev": true
+ },
+ "resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true
+ },
+ "restore-cursor": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+ "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+ "dev": true,
+ "requires": {
+ "onetime": "^2.0.0",
+ "signal-exit": "^3.0.2"
+ }
+ },
+ "rewire": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/rewire/-/rewire-2.5.2.tgz",
+ "integrity": "sha1-ZCfee3/u+n02QBUH62SlOFvFjcc=",
+ "dev": true
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz",
+ "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==",
+ "dev": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "run-async": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+ "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+ "dev": true,
+ "requires": {
+ "is-promise": "^2.1.0"
+ }
+ },
+ "rxjs": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+ "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
+ "dev": true,
+ "requires": {
+ "tslib": "^1.9.0"
+ }
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
+ },
+ "samsam": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.3.0.tgz",
+ "integrity": "sha512-1HwIYD/8UlOtFS3QO3w7ey+SdSDFE4HRNLZoZRYVQefrOY3l17epswImeB1ijgJFQJodIaHcwkp3r/myBjFVbg==",
+ "dev": true
+ },
+ "semver": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
+ "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
+ "dev": true
+ },
+ "setprototypeof": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
+ "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ=="
+ },
+ "shebang-command": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+ "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
+ "dev": true,
+ "requires": {
+ "shebang-regex": "^1.0.0"
+ }
+ },
+ "shebang-regex": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+ "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+ "dev": true
+ },
+ "should": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/should/-/should-8.4.0.tgz",
+ "integrity": "sha1-XmCInT5kS73Tl6MM00+tKPz5C8A=",
+ "dev": true,
+ "requires": {
+ "should-equal": "0.8.0",
+ "should-format": "0.3.2",
+ "should-type": "0.2.0"
+ }
+ },
+ "should-equal": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/should-equal/-/should-equal-0.8.0.tgz",
+ "integrity": "sha1-o/BXMv9FusG3ukEvhAiFaBlkEpk=",
+ "dev": true,
+ "requires": {
+ "should-type": "0.2.0"
+ }
+ },
+ "should-format": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/should-format/-/should-format-0.3.2.tgz",
+ "integrity": "sha1-pZgx4Bot3uFJkRvHFIvlyAMZ4f8=",
+ "dev": true,
+ "requires": {
+ "should-type": "0.2.0"
+ }
+ },
+ "should-type": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/should-type/-/should-type-0.2.0.tgz",
+ "integrity": "sha1-ZwfvlVKdmJ3MCY/gdTqx+RNrt/Y=",
+ "dev": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
+ "dev": true
+ },
+ "sinon": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/sinon/-/sinon-2.4.1.tgz",
+ "integrity": "sha512-vFTrO9Wt0ECffDYIPSP/E5bBugt0UjcBQOfQUMh66xzkyPEnhl/vM2LRZi2ajuTdkH07sA6DzrM6KvdvGIH8xw==",
+ "dev": true,
+ "requires": {
+ "diff": "^3.1.0",
+ "formatio": "1.2.0",
+ "lolex": "^1.6.0",
+ "native-promise-only": "^0.8.1",
+ "path-to-regexp": "^1.7.0",
+ "samsam": "^1.1.3",
+ "text-encoding": "0.6.4",
+ "type-detect": "^4.0.0"
+ }
+ },
+ "slice-ansi": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+ "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+ "dev": true,
+ "requires": {
+ "ansi-styles": "^3.2.0",
+ "astral-regex": "^1.0.0",
+ "is-fullwidth-code-point": "^2.0.0"
+ }
+ },
+ "sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=",
+ "dev": true
+ },
+ "statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow="
+ },
+ "string-width": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
+ "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+ "dev": true,
+ "requires": {
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^4.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+ "dev": true
+ },
+ "supports-color": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+ "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "table": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz",
+ "integrity": "sha512-N2RsDAMvDLvYwFcwbPyF3VmVSSkuF+G1e+8inhBLtHpvwXGw4QRPEZhihQNeEN0i1up6/f6ObCJXNdlRG3YVyQ==",
+ "dev": true,
+ "requires": {
+ "ajv": "^6.9.1",
+ "lodash": "^4.17.11",
+ "slice-ansi": "^2.1.0",
+ "string-width": "^3.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+ "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.0.0.tgz",
+ "integrity": "sha512-rr8CUxBbvOZDUvc5lNIJ+OC1nPVpz+Siw9VBtUjB9b6jZehZLFt0JMCZzShFHIsI8cbhm0EsNIfWJMFV3cu3Ew==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.0.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+ "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^4.0.0"
+ }
+ }
+ }
+ },
+ "text-encoding": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz",
+ "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=",
+ "dev": true
+ },
+ "text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+ "dev": true
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+ "dev": true
+ },
+ "tmp": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+ "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
+ "requires": {
+ "os-tmpdir": "~1.0.2"
+ }
+ },
+ "tslib": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
+ "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+ "dev": true
+ },
+ "type-check": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+ "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+ "dev": true,
+ "requires": {
+ "prelude-ls": "~1.1.2"
+ }
+ },
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
+ "unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw="
+ },
+ "uri-js": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
+ "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+ "dev": true,
+ "requires": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "requires": {
+ "isexe": "^2.0.0"
+ }
+ },
+ "wordwrap": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+ "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+ "dev": true
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
+ "dev": true
+ },
+ "write": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+ "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+ "dev": true,
+ "requires": {
+ "mkdirp": "^0.5.1"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index c21260f5..6c10448f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "perimeterx-node-core",
- "version": "1.8.0",
+ "version": "2.0.0",
"description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
"main": "index.js",
"scripts": {
diff --git a/test/pxapi.test.js b/test/pxapi.test.js
index 896482bd..695d8a6e 100644
--- a/test/pxapi.test.js
+++ b/test/pxapi.test.js
@@ -6,11 +6,14 @@ const rewire = require('rewire');
const pxhttpc = require('../lib/pxhttpc');
const pxapi = rewire('../lib/pxapi');
const originalTokenValidator = require('../lib/pxoriginaltoken');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
describe('PX API - pxapi.js', () => {
let params;
let config;
+ let pxConfig;
+ let logger;
let stub;
beforeEach(() => {
@@ -27,10 +30,11 @@ describe('PX API - pxapi.js', () => {
moduleMode: 1,
};
- const pxconfig = require('../lib/pxconfig');
- pxconfig.init(params, new PxClient());
- config = pxconfig.mergeDefaults(params);
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ logger = new PxLogger();
+ pxConfig = new PxConfig(params, logger);
+ logger.init(pxConfig);
+ config = pxConfig.conf;
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback(data);
});
});
@@ -45,8 +49,8 @@ describe('PX API - pxapi.js', () => {
//Using rewire to get callServer function
const pxApiCallServerFunc = pxapi.__get__('callServer');
- // Prepare pxCtx
- const pxCtx = {
+ // Prepare ctx
+ const ctx = {
ip: '1.2.3.4',
fullUrl: 'stub',
vid: 'stub',
@@ -61,88 +65,88 @@ describe('PX API - pxapi.js', () => {
}
};
- pxApiCallServerFunc(pxCtx, data => {
+ pxApiCallServerFunc(ctx, config, data => {
data.additional.px_orig_cookie.should.equal('abc');
done();
});
});
- it('token v3 - should add originalUuid, vid and decodedOriginalToken to pxCtx when original token decryption succeeds', (done) => {
- const pxCtx = {
+ it('token v3 - should add originalUuid, vid and decodedOriginalToken to ctx when original token decryption succeeds', (done) => {
+ const ctx = {
cookies: {
_px3: 'aaaa'
},
originalToken: '68a1bf96ab3af2e0683a377d332b125dda3e195ee56cf3ce4d61b99cd0860dc6:xTMRZvJnzxM=:1000:0pjajaPCjssb2HjG2436zyFXIvIEbE87nFBrHEQPDRT7fqiQ5RA05+njsLUVpOtdJjLvWNNAlSG70DW2wqWM5VmF9UR420/wxPkx6Ebyz/L9q7Mxk5fcdF8p+dGcMc3uD7Qh8y3WiPSN389cXhfKfMttUABQYvRpOxo7rMC+ngpHEVYg+lfBZCliHB1PZKLy'
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
- pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
- JSON.stringify(pxCtx.decodedOriginalToken).should.equal('{"a":"c","s":0,"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+ ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+ JSON.stringify(ctx.decodedOriginalToken).should.equal('{"a":"c","s":0,"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
done();
});
it('token v3 - should set originalTokenError to decryption_failed on original token decryption fail', (done) => {
- const pxCtx = {
+ const ctx = {
cookies: {
_px3: 'aaaa'
},
originalToken: 'aaaaa:bbbbb:cccc:ddddd'
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalTokenError.should.equal('decryption_failed');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalTokenError.should.equal('decryption_failed');
done();
});
it('token v3 - should set originalTokenError to validation_failed on original token validation fail', (done) => {
- const pxCtx = {
+ const ctx = {
cookies: {
_px3: 'aaaa'
},
originalToken: '68a1bf96ab3af2e0683a377d332b125dda3e195ee56cf3ce4d61b99cd0860dc:xTMRZvJnzxM=:1000:0pjajaPCjssb2HjG2436zyFXIvIEbE87nFBrHEQPDRT7fqiQ5RA05+njsLUVpOtdJjLvWNNAlSG70DW2wqWM5VmF9UR420/wxPkx6Ebyz/L9q7Mxk5fcdF8p+dGcMc3uD7Qh8y3WiPSN389cXhfKfMttUABQYvRpOxo7rMC+ngpHEVYg+lfBZCliHB1PZKLy'
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
- pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
- pxCtx.originalTokenError.should.equal('validation_failed');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+ ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+ ctx.originalTokenError.should.equal('validation_failed');
done();
});
- it('token v1 - should add originalUuid, vid and decodedOriginalToken to pxCtx when original token decryption succeeds', (done) => {
- const pxCtx = {
+ it('token v1 - should add originalUuid, vid and decodedOriginalToken to ctx when original token decryption succeeds', (done) => {
+ const ctx = {
cookies: {
_px: 'aaaa'
},
originalToken: 'Gy9z3mQPYNE=:1000:I7A44BXmO5IlgqhXLM5Mmuq4/jESNgse51Zj/l4bpkAaymDQzcrUMHBofVQ8Q9IYfon3bVQn7gHA124xunjlSlPMlj133wuFBzt7r/yJKpcTEex5WBxynCQAXXx8tymeO1gWXLmPchrV93ysxPl/AeV2/ofVN3YzUR/0PQbXB2fzxkPc5bMPdxLMJCrgLtR4msoMGvg9qaiufMFDWWzah1kvUq1Kvrlk3UQm0y6UU1j6GoLHkTSnDBTg3GexETotOoUkM5FYMPZm8TxK0as+mg=='
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
- pxCtx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
- JSON.stringify(pxCtx.decodedOriginalToken).should.equal('{"h":"aa2341380b7c67ee0ed5c2f7d4facf03847d7dcb4540aab021654361d3dcade4","s":{"a":0,"b":0},"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalUuid.should.equal('09ade30a-f08b-11e7-8c3f-9a214cf093ae');
+ ctx.vid.should.equal('0290edec-f08b-11e7-8c3f-9a214cf093ae');
+ JSON.stringify(ctx.decodedOriginalToken).should.equal('{"h":"aa2341380b7c67ee0ed5c2f7d4facf03847d7dcb4540aab021654361d3dcade4","s":{"a":0,"b":0},"u":"09ade30a-f08b-11e7-8c3f-9a214cf093ae","t":1830515445000,"v":"0290edec-f08b-11e7-8c3f-9a214cf093ae"}');
done();
});
it('token v1 - should set originalTokenError to decryption_failed on original token decryption fail', (done) => {
- const pxCtx = {
+ const ctx = {
cookies: {
_px: 'aaaa'
},
originalToken: 'aaaaa:bbbbb:cccc:ddddd'
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalTokenError.should.equal('decryption_failed');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalTokenError.should.equal('decryption_failed');
done();
});
it('should fail with exception and set originalTokenError to decryption_failed', (done) => {
- const pxCtx = {
+ const ctx = {
cookies: {
_px: 'aaaaa'
},
originalToken: ''
};
- originalTokenValidator.evalCookie(pxCtx, config);
- pxCtx.originalTokenError.should.equal('decryption_failed');
+ originalTokenValidator.evalCookie(ctx, config);
+ ctx.originalTokenError.should.equal('decryption_failed');
done();
});
});
\ No newline at end of file
diff --git a/test/pxconfig.test.js b/test/pxconfig.test.js
index 9f46d538..361964e5 100644
--- a/test/pxconfig.test.js
+++ b/test/pxconfig.test.js
@@ -2,11 +2,12 @@
const should = require('should');
const rewire = require('rewire');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
describe('PX Configurations - pxconfig.js', () => {
- let pxconfig;
let params;
+ const logger = new PxLogger();
beforeEach(() => {
params = {
@@ -21,21 +22,20 @@ describe('PX Configurations - pxconfig.js', () => {
customRequestHandler: null
};
- pxconfig = require('../lib/pxconfig');
});
it('should set baseUrl to sapi-.perimeterx.net', (done) => {
params.pxAppId = 'PXJWbMQarF';
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.SERVER_HOST.should.be.exactly(`sapi-${params.pxAppId.toLowerCase()}.perimeterx.net`);
done();
});
it('blocking score should be 80', (done) => {
params.blockingScore = 80;
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.BLOCKING_SCORE.should.be.exactly(80);
done();
});
@@ -45,8 +45,8 @@ describe('PX Configurations - pxconfig.js', () => {
return '1.2.3.4';
};
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.GET_USER_IP().should.be.exactly('1.2.3.4');
done();
});
@@ -56,51 +56,51 @@ describe('PX Configurations - pxconfig.js', () => {
return 'Blocked';
};
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.CUSTOM_REQUEST_HANDLER().should.be.exactly('Blocked');
done();
});
it('should set enableModule to false', () => {
params.enableModule = false;
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.ENABLE_MODULE.should.be.exactly(false);
});
it('should set sendPageActivities to false', () => {
params.sendPageActivities = false;
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.SEND_PAGE_ACTIVITIES.should.be.exactly(false);
});
it('should set debugMode to true', () => {
params.sendPageActivities = true;
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.DEBUG_MODE.should.be.exactly(true);
});
it('customLogo should be overridden', () => {
params.customLogo = 'http://www.google.com/logo.jpg';
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.CUSTOM_LOGO.should.be.exactly('http://www.google.com/logo.jpg');
});
it('jsRef should be overridden', () => {
params.jsRef = ['http://www.google.com/script.js'];
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.JS_REF[0].should.equal('http://www.google.com/script.js');
});
it('cssRef should be overridden', () => {
params.cssRef = ['http://www.google.com/stylesheet.css'];
- pxconfig.init(params, new PxClient());
- const conf = pxconfig.conf;
+ const pxConfig = new PxConfig(params, logger);
+ const conf = pxConfig.conf;
conf.CSS_REF[0].should.equal('http://www.google.com/stylesheet.css');
});
});
\ No newline at end of file
diff --git a/test/pxenforcer.test.js b/test/pxenforcer.test.js
index 0698159f..c0a057e7 100644
--- a/test/pxenforcer.test.js
+++ b/test/pxenforcer.test.js
@@ -9,7 +9,7 @@ const PxClient = rewire('../lib/pxclient');
const PxEnforcer = require('../lib/pxenforcer');
describe('PX Enforcer - pxenforcer.js', () => {
- let params, enforcer, req, stub;
+ let params, enforcer, req, stub, pxClient;
beforeEach(() => {
params = {
@@ -36,6 +36,8 @@ describe('PX Enforcer - pxenforcer.js', () => {
req.get = (key) => {
return req.headers[key] || '';
};
+
+ pxClient = new PxClient();
});
afterEach(() => {
@@ -43,11 +45,11 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('enforces a call in a disabled module', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
params.enableModule = false;
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (response) => {
(response === undefined).should.equal(true);
@@ -56,10 +58,10 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('enforces a call in an enabled module', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (response) => {
(response === undefined).should.equal(true);
@@ -68,14 +70,14 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('uses first party to get client', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- const reqStub = sinon.stub(request, 'get').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'get').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body: 'hello buddy', proxy:''});
});
req.originalUrl = '/_APP_ID/init.js';
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.should.equal('hello buddy');
@@ -86,16 +88,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('uses first party for xhr post request', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
});
req.originalUrl = '/_APP_ID/xhr/something';
req.method = 'POST';
req.body = 'test';
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.should.equal('hello buddy');
@@ -106,16 +108,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('uses first party for xhr get request', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- const reqStub = sinon.stub(request, 'get').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'get').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body: 'hello buddy'});
});
req.originalUrl = '/_APP_ID/xhr/something';
req.method = 'GET';
req.body = 'test';
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.should.equal('hello buddy');
@@ -126,17 +128,17 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('uses first party with pxvid cookie', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
});
req.originalUrl = '/_APP_ID/xhr/something';
req.method = 'POST';
req.cookies['_pxvid'] = 'abab-123';
req.body = 'test';
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.should.equal('hello buddy');
@@ -147,16 +149,16 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('uses first party for xhr and passed trough bodyParser', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
return callback ? callback(null, data) : '';
});
- const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
});
req.originalUrl = '/_APP_ID/xhr/something';
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(params, new PxClient());
+ enforcer = new PxEnforcer(params, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.should.equal('hello buddy');
@@ -167,7 +169,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
it('should not use first party paths if originated from mobile', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
@@ -176,13 +178,13 @@ describe('PX Enforcer - pxenforcer.js', () => {
moduleMode: 1,
firstPartyEnabled: true
}, params);
- const reqStub = sinon.stub(request, 'post').callsFake((data, callback) => {
+ const reqStub = sinon.stub(request, 'post').callsFake((data, config, callback) => {
callback(null, {headers: {'x-px-johnny': '1'}, body:'hello buddy'});
});
req.headers = {'x-px-authorization': '3:some-fake-cookie'};
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(curParams, new PxClient());
+ enforcer = new PxEnforcer(curParams, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(false);
response.body.action.should.equal('block');
@@ -191,7 +193,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
});
it('should bypass monitor mode by header', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
@@ -208,7 +210,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(curParams, new PxClient());
+ enforcer = new PxEnforcer(curParams, 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);
@@ -217,7 +219,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
});
it('should ignore bypass monitor mode by header', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
@@ -234,7 +236,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(curParams, new PxClient());
+ enforcer = new PxEnforcer(curParams, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
@@ -242,7 +244,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
});
it('should ignore bypass monitor header as its not present', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
data.score = 100;
data.action = 'b';
return callback ? callback(null, data) : '';
@@ -256,7 +258,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(curParams, new PxClient());
+ enforcer = new PxEnforcer(curParams, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
@@ -264,7 +266,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
});
it('should ignore bypass monitor header as cookie is valid', (done) => {
- stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, callback) => {
+ stub = sinon.stub(pxhttpc, 'callServer').callsFake((data, headers, uri, callType, config, callback) => {
data.score = 0;
data.action = 'b';
return callback ? callback(null, data) : '';
@@ -281,7 +283,7 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
req.method = 'POST';
req.body = {key: 'value', anotherKey: 'anotherValue'};
- enforcer = new PxEnforcer(curParams, new PxClient());
+ enforcer = new PxEnforcer(curParams, pxClient);
enforcer.enforce(req, null, (error, response) => {
(response === undefined).should.equal(true);
reqStub.restore();
@@ -289,4 +291,3 @@ describe('PX Enforcer - pxenforcer.js', () => {
});
});
});
-
diff --git a/test/pxutils.test.js b/test/pxutils.test.js
index 82205f9a..85b4d7b1 100644
--- a/test/pxutils.test.js
+++ b/test/pxutils.test.js
@@ -3,10 +3,11 @@
const should = require('should');
const rewire = require('rewire');
const pxutil = require('../lib/pxutil');
-const PxClient = rewire('../lib/pxclient');
+const PxConfig = require('../lib/pxconfig');
+const PxLogger = require('../lib/pxlogger');
describe('PX Utils - pxutils.js', () => {
- let pxconfig;
+ let pxConfig;
let params;
beforeEach(() => {
@@ -22,12 +23,12 @@ describe('PX Utils - pxutils.js', () => {
enrichCustomParameters: enrichCustomParameters
};
- pxconfig = require('../lib/pxconfig');
- pxconfig.init(params, new PxClient());
+ const logger = new PxLogger();
+ pxConfig = new PxConfig(params, logger);
});
it('should generate headers array from headers object', (done) => {
- const formattedHeaders = pxutil.formatHeaders({K: 'v'});
+ const formattedHeaders = pxutil.formatHeaders({K: 'v'}, pxConfig.conf.SENSITIVE_HEADERS);
(Object.prototype.toString.call(formattedHeaders)).should.be.exactly('[object Array]');
formattedHeaders[0]['name'].should.be.exactly('K');
formattedHeaders[0]['value'].should.be.exactly('v');
@@ -36,7 +37,7 @@ describe('PX Utils - pxutils.js', () => {
it('should receive custom params function and custom params object and add only 2 of them', (done) => {
const dict = {};
- pxutil.prepareCustomParams(pxconfig.conf, dict);
+ pxutil.prepareCustomParams(pxConfig.conf, dict);
dict['custom_param1'].should.be.exactly('1');
dict['custom_param2'].should.be.exactly('2');
dict['custom_param10'].should.be.exactly('10');