Skip to content

Commit

Permalink
Merge pull request #296 from PerimeterX/release/v3.13.0
Browse files Browse the repository at this point in the history
Release/v3.13.0 to master
  • Loading branch information
chen-zimmer-px authored Dec 21, 2023
2 parents fa3f6b0 + a08d4f4 commit 5efe994
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 29 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ 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/).

## [3.13.0] - 2023-12-21

### Added
- Added `failOnEmptyBody` flag for `callServer` to specify weather or not a request should fail if it has no body.
- Updated the configuration of PX first-party requests to include a connection timeout

### Changed
- Updated the captcha template to handle empty captcha responses

## [3.12.0] - 2023-12-03

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[PerimeterX](http://www.perimeterx.com) Shared base for NodeJS enforcers
=============================================================

> Latest stable version: [v3.12.0](https://www.npmjs.com/package/perimeterx-node-core)
> Latest stable version: [v3.13.0](https://www.npmjs.com/package/perimeterx-node-core)
This is a shared base implementation for PerimeterX Express enforcer and future NodeJS enforcers. For a fully functioning implementation example, see the [Node-Express enforcer](https://github.com/PerimeterX/perimeterx-node-express/) implementation.

Expand Down
10 changes: 9 additions & 1 deletion lib/pxclient.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,15 @@ class PxClient {
}

callServer(data, path, additionalHeaders, config, cb) {
pxHttpc.callServer(data, this.createHeaders(config, additionalHeaders), path, 'activities', config);
pxHttpc.callServer(
data,
this.createHeaders(config, additionalHeaders),
path,
'activities',
config,
null,
false,
);
if (cb) {
cb();
}
Expand Down
9 changes: 6 additions & 3 deletions lib/pxconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ class PxConfig {
['JWT_HEADER_USER_ID_FIELD_NAME', 'px_jwt_header_user_id_field_name'],
['JWT_HEADER_ADDITIONAL_FIELD_NAMES', 'px_jwt_header_additional_field_names'],
['CUSTOM_IS_SENSITIVE_REQUEST', 'px_custom_is_sensitive_request'],
['LOGGER_AUTH_TOKEN', 'px_logger_auth_token']
['LOGGER_AUTH_TOKEN', 'px_logger_auth_token'],
['FIRST_PARTY_TIMEOUT_MS', 'px_first_party_timeout_ms']
];

configKeyMapping.forEach(([targetKey, sourceKey]) => {
Expand Down Expand Up @@ -363,7 +364,8 @@ function pxDefaultConfig() {
JWT_HEADER_USER_ID_FIELD_NAME: '',
JWT_HEADER_ADDITIONAL_FIELD_NAMES: [],
CUSTOM_IS_SENSITIVE_REQUEST: '',
LOGGER_AUTH_TOKEN: ''
LOGGER_AUTH_TOKEN: '',
FIRST_PARTY_TIMEOUT_MS: 4000
};
}

Expand Down Expand Up @@ -437,7 +439,8 @@ const allowedConfigKeys = [
'px_jwt_header_user_id_field_name',
'px_jwt_header_additional_field_names',
'px_custom_is_sensitive_request',
'px_logger_auth_token'
'px_logger_auth_token',
'px_first_party_timeout_ms'
];

module.exports = PxConfig;
40 changes: 30 additions & 10 deletions lib/pxhttpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module.exports = {
* @param {string} callType - indication for a query or activities sending
* @param {Function} callback - callback function.
*/
function callServer(data, headers, uri, callType, config, callback) {
function callServer(data, headers, uri, callType, config, callback, failOnEmptyBody = true) {
callback =
callback ||
((err) => {
Expand All @@ -37,7 +37,12 @@ function callServer(data, headers, uri, callType, config, callback) {
if (err.toString().toLowerCase().includes('timeout')) {
return callback('timeout');
} else {
return handleError(callback, S2SErrorReason.UNKNOWN_ERROR, `encountered error with risk api call: ${err}`, response);
return handleError(
callback,
S2SErrorReason.UNKNOWN_ERROR,
`encountered error with risk api call: ${err}`,
response,
);
}
}

Expand All @@ -46,12 +51,12 @@ function callServer(data, headers, uri, callType, config, callback) {
callback,
S2SErrorReason.UNKNOWN_ERROR,
`call to perimeterx server returned null or empty response: ${response}`,
response
response,
);
}

if (response.statusCode === 200) {
return handleOkResponse(callback, response);
return handleOkResponse(callback, response, failOnEmptyBody);
}

return handleUnexpectedHttpResponse(callback, response);
Expand All @@ -62,23 +67,38 @@ function callServer(data, headers, uri, callType, config, callback) {
}
}

const handleOkResponse = (callback, response) => {
const handleOkResponse = (callback, response, failOnEmptyBody) => {
let data = getDataFromResponse(response);

if (!data) {
return handleError(callback, S2SErrorReason.INVALID_RESPONSE, `unable to get data from response body: ${response.body}`, response);
if (!data && failOnEmptyBody) {
return handleError(
callback,
S2SErrorReason.INVALID_RESPONSE,
`unable to get data from response body: ${response.body}`,
response,
);
}

if (typeof data !== 'object') {
try {
data = JSON.parse(data);
} catch (e) {
return handleError(callback, S2SErrorReason.INVALID_RESPONSE, `error parsing response body - ${data}: ${e}`, response);
return handleError(
callback,
S2SErrorReason.INVALID_RESPONSE,
`error parsing response body - ${data}: ${e}`,
response,
);
}
}

if (data.status && data.status !== 0) {
return handleError((err) => callback(err, data), S2SErrorReason.REQUEST_FAILED_ON_SERVER, data.message, response);
if (data && data.status && data.status !== 0) {
return handleError(
(err) => callback(err, data),
S2SErrorReason.REQUEST_FAILED_ON_SERVER,
data.message,
response,
);
}

return callback(null, data);
Expand Down
8 changes: 4 additions & 4 deletions lib/pxproxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function getCaptcha(req, config, ip, reversePrefix, cb) {
const callData = {
url: `https://${config.CAPTCHA_HOST}${pxRequestUri}`,
headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
timeout: config.API_TIMEOUT_MS
timeout: config.FIRST_PARTY_TIMEOUT_MS
};
callData.headers['host'] = config.CAPTCHA_HOST;
callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
Expand Down Expand Up @@ -75,7 +75,7 @@ function getClient(req, config, ip, cb) {
const callData = {
url: `https://${config.CLIENT_HOST}${clientRequestUri}`,
headers: pxUtil.filterSensitiveHeaders(req.headers, config.SENSITIVE_HEADERS),
timeout: config.API_TIMEOUT_MS
timeout: config.FIRST_PARTY_TIMEOUT_MS
};
callData.headers['host'] = config.CLIENT_HOST;
callData.headers[config.ENFORCER_TRUE_IP_HEADER] = ip;
Expand Down Expand Up @@ -110,7 +110,7 @@ function sendCDXHR(req, config, ip, reversePrefix, cb) {
const callData = {
url: `https://${config.CD_XHR_HOST}${xhrRequestUri}`,
headers: pxUtil.generateProxyHeaders(req.headers, req.ip, config.SENSITIVE_HEADERS, config.FORWARDED_FOR_HEADER),
timeout: config.API_TIMEOUT_MS
timeout: config.FIRST_PARTY_TIMEOUT_MS
};

callData.headers['host'] = config.CD_XHR_HOST;
Expand Down Expand Up @@ -179,7 +179,7 @@ function sendXHR(req, config, ip, reversePrefix, cb) {
const callData = {
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
timeout: config.FIRST_PARTY_TIMEOUT_MS
};

callData.headers['host'] = config.COLLECTOR_HOST;
Expand Down
43 changes: 36 additions & 7 deletions lib/templates/block_template.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,51 @@
window._pxVid = '{{vid}}';
window._pxUuid = '{{uuid}}';
window._pxAppId = '{{appId}}';
window._pxMobile = {{isMobile}};
window._pxHostUrl = '{{{hostUrl}}}';
window._pxCustomLogo = '{{{customLogo}}}';
window._pxJsClientSrc = '{{{jsClientSrc}}}';
window._pxMobile = {{isMobile}};
window._pxFirstPartyEnabled = {{firstPartyEnabled}};
var pxCaptchaSrc = '{{{blockScript}}}';
var pxCaptchaSrc = '{{{blockScript}}}';
var script = document.createElement('script');
script.src = pxCaptchaSrc;
script.onerror = function () {
script.onload = onScriptLoad;
script.onerror = onScriptError;
var onScriptErrorCalled;
document.head.appendChild(script);
var timeoutID = setTimeout(onScriptError, 5000);
function onScriptLoad() {
clearTimeout(timeoutID);
setTimeout(function() {
if (isCaptchaNotLoaded()) {
onScriptError();
}
}, 1000);
}
function onScriptError() {
if (onScriptErrorCalled) {
return;
}
onScriptErrorCalled = true;
script = document.createElement('script');
script.src = '{{altBlockScript}}';
script.src = '{{{altBlockScript}}}';
script.onload = function() {
clearTimeout(timeoutID);
};
script.onerror = window._pxOnError;
document.head.appendChild(script);
};
timeoutID = setTimeout(function() {
if (isCaptchaNotLoaded()) {
window._pxOnError();
}
}, 5000);
}
function isCaptchaNotLoaded() {
return !document.querySelector('div');
}
window._pxOnError = function () {
var style = document.createElement('style');
style.innerText = '@import url(https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap);body{background-color:#fafbfc}.px-captcha-error-container{position:fixed;height:340px;background-color:#fff;font-family:Roboto,sans-serif}.px-captcha-error-header{color:#f0f1f2;font-size:29px;margin:67px 0 33px;font-weight:500;line-height:.83;text-align:center}.px-captcha-error-message{color:#f0f1f2;font-size:18px;margin:0 0 29px;line-height:1.33;text-align:center}.px-captcha-error-button{text-align:center;line-height:48px;width:253px;margin:auto;border-radius:50px;border:solid 1px #f0f1f2;font-size:20px;color:#f0f1f2}.px-captcha-error-wrapper{margin:18px 0 0}div.px-captcha-error{margin:auto;text-align:center;width:400px;height:30px;font-size:12px;background-color:#fcf0f2;color:#ce0e2d}img.px-captcha-error{margin:6px 8px -2px 0}.px-captcha-error-refid{border-top:solid 1px #f0eeee;height:27px;margin:13px 0 0;border-radius:0 0 3px 3px;background-color:#fafbfc;font-size:10px;line-height:2.5;text-align:center;color:#b1b5b8}@media (min-width:620px){.px-captcha-error-container{width:530px;top:50%;left:50%;margin-top:-170px;margin-left:-265px;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (min-width:481px) and (max-width:620px){.px-captcha-error-container{width:85%;top:50%;left:50%;margin-top:-170px;margin-left:-42.5%;border-radius:3px;box-shadow:0 2px 9px -1px rgba(0,0,0,.13)}}@media (max-width:480px){body{background-color:#fff}.px-captcha-error-header{color:#f0f1f2;font-size:29px;margin:55px 0 33px}.px-captcha-error-container{width:530px;top:50%;left:50%;margin-top:-170px;margin-left:-265px}.px-captcha-error-refid{position:fixed;width:100%;left:0;bottom:0;border-radius:0;font-size:14px;line-height:2}}@media (max-width:390px){div.px-captcha-error{font-size:10px}.px-captcha-error-refid{font-size:11px;line-height:2.5}}';
Expand All @@ -44,10 +74,9 @@
}, 5000);
}
};
document.head.appendChild(script);
</script>
{{#jsRef}}
<script src="{{{jsRef}}}"></script>
{{/jsRef}}
</body>
</html>
</html>
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "perimeterx-node-core",
"version": "3.12.0",
"version": "3.13.0",
"description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score",
"main": "index.js",
"scripts": {
Expand Down

0 comments on commit 5efe994

Please sign in to comment.