From 2fad78c9a4599d52c6d01217720a73a3633a6d79 Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Wed, 17 Jan 2024 09:52:59 +0200 Subject: [PATCH 1/8] Remote config initial commit - fetchConfig, add risk parameter and indicate on configuration version update. --- lib/enums/CIVersion.js | 2 +- lib/pxapi.js | 5 +++++ lib/pxclient.js | 20 ++++++++++++++++++++ lib/pxconfig.js | 27 +++++++++++++++++++++++---- lib/pxenforcer.js | 2 +- lib/request.js | 2 +- lib/utils/constants.js | 4 +++- package.json | 3 ++- 8 files changed, 56 insertions(+), 9 deletions(-) diff --git a/lib/enums/CIVersion.js b/lib/enums/CIVersion.js index 324696ca..9df466a4 100644 --- a/lib/enums/CIVersion.js +++ b/lib/enums/CIVersion.js @@ -6,4 +6,4 @@ const CIVersion = { module.exports = { CIVersion -}; \ No newline at end of file +}; diff --git a/lib/pxapi.js b/lib/pxapi.js index 781d269d..16b7c80c 100644 --- a/lib/pxapi.js +++ b/lib/pxapi.js @@ -66,6 +66,7 @@ function buildRequestData(ctx, config) { cookie_origin: ctx.cookieOrigin, request_cookie_names: ctx.requestCookieNames, request_id: ctx.requestId, + px_remote_config_id: config.REMOTE_CONFIG_ID ? config.REMOTE_CONFIG_ID : '', }, }; @@ -187,6 +188,10 @@ function evalByServerCall(ctx, config, callback) { return callback(ScoreEvaluateAction.UNEXPECTED_RESULT); } ctx.pxhdServer = res.pxhd; + if (res.remote_config && res.remote_config.id === config.REMOTE_CONFIG_ID) { + ctx.isRemoteConfigOutdated = res.remote_config.version > config.REMOTE_CONFIG_VERSION; + } + if (res.data_enrichment) { ctx.pxde = res.data_enrichment; ctx.pxdeVerified = true; diff --git a/lib/pxclient.js b/lib/pxclient.js index 784cfb74..0ef5f323 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -4,6 +4,7 @@ const pxUtil = require('./pxutil'); const pxHttpc = require('./pxhttpc'); const { ActivityType } = require('./enums/ActivityType'); const { CIVersion } = require('./enums/CIVersion'); +const fetch = require('node-fetch'); const { CI_VERSION_FIELD, CI_SSO_STEP_FIELD, @@ -42,6 +43,7 @@ class PxClient { vid: ctx.vid ? ctx.vid : undefined, }; details['request_id'] = ctx.requestId; + details['px_remote_config_version'] = config.remoteConfigVersion; this.addAdditionalFieldsToActivity(details, ctx); if (activityType !== ActivityType.ADDITIONAL_S2S) { @@ -57,6 +59,24 @@ class PxClient { activity.details = details; return activity; } + async fetchRemoteConfig(appId, remoteConfigToken) { + const callData = { + url: `https://sapi-${appId}.perimeterx.net/config/`, + headers: { 'Authorization': `Bearer ${remoteConfigToken}`, 'Accept-Encoding': '' }, + timeout: 20000, + }; + + const response = await fetch(callData.url, { headers: callData.headers, timeout: callData.timeout }); + const remoteConfigObject = await response.json(); + + const remoteConfig = { + px_remote_config_id: remoteConfigObject.id, + px_remote_config_version: remoteConfigObject.version, + ... remoteConfigObject.configValue + }; + + return remoteConfig; + } addAdditionalFieldsToActivity(details, ctx) { if (ctx.additionalFields && ctx.additionalFields.loginCredentials) { diff --git a/lib/pxconfig.js b/lib/pxconfig.js index 8dec4633..a59404cd 100644 --- a/lib/pxconfig.js +++ b/lib/pxconfig.js @@ -7,16 +7,16 @@ const { LoggerSeverity } = require('./enums/LoggerSeverity'); const { DEFAULT_COMPROMISED_CREDENTIALS_HEADER_NAME } = require('./utils/constants'); const { CIVersion } = require('./enums/CIVersion'); const { LoginSuccessfulReportingMethod } = require('./enums/LoginSuccessfulReportingMethod'); - +const { INVALID_VERSION_NUMBER } = require('./utils/constants'); class PxConfig { constructor(params, logger) { this.PX_INTERNAL = pxInternalConfig(); this.PX_DEFAULT = pxDefaultConfig(); this.logger = logger; this.config = this.mergeParams(params); + this._remoteConfigOutdated = false; this.config.FILTER_BY_METHOD = this.config.FILTER_BY_METHOD.map((v) => v.toUpperCase()); this.config.logger = this.logger; - this.config.WHITELIST_EXT = [...this.PX_INTERNAL.STATIC_FILES_EXT, ...this.PX_DEFAULT.WHITELIST_EXT]; if (this.PX_DEFAULT.TESTING_MODE) { @@ -30,6 +30,17 @@ class PxConfig { this.configLoader = null; } + get remoteConfigVersion() { + return this.config.px_remote_config_version || INVALID_VERSION_NUMBER; + } + + get isRemoteConfigOutdated() { + return this._remoteConfigOutdated; + } + set isRemoteConfigOutdated(value) { + this._remoteConfigOutdated = value; + } + mergeParams(params) { params = this.mergeConfigFileParams(params); @@ -104,7 +115,11 @@ class PxConfig { ['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'], - ['FIRST_PARTY_TIMEOUT_MS', 'px_first_party_timeout_ms'] + ['FIRST_PARTY_TIMEOUT_MS', 'px_first_party_timeout_ms'], + ['REMOTE_CONFIG_ENABLED', 'px_remote_config_enabled'], + ['REMOTE_CONFIG_AUTH_TOKEN', 'px_remote_config_auth_token'], + ['REMOTE_CONFIG_ID', 'px_remote_config_id'], + ['REMOTE_CONFIG_VERSION', 'px_remote_config_version'] ]; configKeyMapping.forEach(([targetKey, sourceKey]) => { @@ -365,7 +380,11 @@ function pxDefaultConfig() { JWT_HEADER_ADDITIONAL_FIELD_NAMES: [], CUSTOM_IS_SENSITIVE_REQUEST: '', LOGGER_AUTH_TOKEN: '', - FIRST_PARTY_TIMEOUT_MS: 4000 + FIRST_PARTY_TIMEOUT_MS: 4000, + REMOTE_CONFIG_ENABLED: false, + REMOTE_CONFIG_AUTH_TOKEN: '', + REMOTE_CONFIG_ID: '', + REMOTE_CONFIG_VERSION: INVALID_VERSION_NUMBER }; } diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js index 9e7907bc..051c6656 100644 --- a/lib/pxenforcer.js +++ b/lib/pxenforcer.js @@ -294,7 +294,7 @@ class PxEnforcer { handleVerification(ctx, req, res, cb) { const verified = ctx.score < this._config.BLOCKING_SCORE; - + this.config.isRemoteConfigOutdated = ctx.isRemoteConfigOutdated; if (res) { const setCookie = res.getHeader('Set-Cookie') ? res.getHeader('Set-Cookie') : ''; const secure = this._config.PXHD_SECURE ? '; Secure' : ''; diff --git a/lib/request.js b/lib/request.js index 5b9e34cf..0a770769 100644 --- a/lib/request.js +++ b/lib/request.js @@ -23,4 +23,4 @@ function makeRequest(options, config, cb) { options.agent = new http.Agent(); } p(options, cb); -} \ No newline at end of file +} diff --git a/lib/utils/constants.js b/lib/utils/constants.js index e9b03db4..0077ca2f 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -38,6 +38,7 @@ const COOKIE_SEPARATOR = ';'; const X_PX_ENFORCER_LOG_HEADER = 'x-px-enforcer-log'; const EXTERNAL_LOGGER_SERVICE_PATH = '/enforcer-logs/'; +const INVALID_VERSION_NUMBER = -1; module.exports = { MILLISECONDS_IN_SECOND, @@ -70,5 +71,6 @@ module.exports = { CROSS_TAB_SESSION, COOKIE_SEPARATOR, X_PX_ENFORCER_LOG_HEADER, - EXTERNAL_LOGGER_SERVICE_PATH + EXTERNAL_LOGGER_SERVICE_PATH, + INVALID_VERSION_NUMBER }; diff --git a/package.json b/package.json index 536b2884..e067c9bc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "perimeterx-node-core", - "version": "3.13.0", + "version": "3.14.0", "description": "PerimeterX NodeJS shared core for various applications to monitor and block traffic according to PerimeterX risk score", "main": "index.js", "scripts": { @@ -24,6 +24,7 @@ "https-proxy-agent": "^5.0.0", "ip-range-check": "^0.2.0", "mu2": "^0.5.21", + "node-fetch": "^2.7.0", "raw-body": "^2.3.2", "uuid": "^8.3.2" }, From 639235f2a1f3819328424ab08878bc220cc9154f Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:59:38 +0200 Subject: [PATCH 2/8] Add host name to s2s and async activities --- lib/pxapi.js | 3 ++- lib/pxclient.js | 3 ++- lib/utils/constants.js | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/pxapi.js b/lib/pxapi.js index 8aaf9cd8..a8853430 100644 --- a/lib/pxapi.js +++ b/lib/pxapi.js @@ -1,7 +1,7 @@ 'use strict'; const pxUtil = require('./pxutil'); const pxHttpc = require('./pxhttpc'); - +const os = require('os'); const S2SErrorInfo = require('./models/S2SErrorInfo'); const { ModuleMode } = require('./enums/ModuleMode'); const PassReason = require('./enums/PassReason'); @@ -67,6 +67,7 @@ function buildRequestData(ctx, config) { request_cookie_names: ctx.requestCookieNames, request_id: ctx.requestId, px_remote_config_id: config.REMOTE_CONFIG_ID ? config.REMOTE_CONFIG_ID : '', + hostname: os.hostname() }, }; diff --git a/lib/pxclient.js b/lib/pxclient.js index 0ef5f323..49d732af 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -13,7 +13,7 @@ const { GQL_OPERATIONS_FIELD, APP_USER_ID_FIELD_NAME, JWT_ADDITIONAL_FIELDS_FIELD_NAME, - CROSS_TAB_SESSION, + CROSS_TAB_SESSION, HOST_NAME, } = require('./utils/constants'); class PxClient { @@ -104,6 +104,7 @@ class PxClient { } } + details[HOST_NAME] = os.hostname(); if (ctx.cts) { details[CROSS_TAB_SESSION] = ctx.cts; } diff --git a/lib/utils/constants.js b/lib/utils/constants.js index 0077ca2f..d4f73cad 100644 --- a/lib/utils/constants.js +++ b/lib/utils/constants.js @@ -39,7 +39,7 @@ const COOKIE_SEPARATOR = ';'; const X_PX_ENFORCER_LOG_HEADER = 'x-px-enforcer-log'; const EXTERNAL_LOGGER_SERVICE_PATH = '/enforcer-logs/'; const INVALID_VERSION_NUMBER = -1; - +const HOST_NAME = 'hostname'; module.exports = { MILLISECONDS_IN_SECOND, SECONDS_IN_MINUTE, @@ -72,5 +72,6 @@ module.exports = { COOKIE_SEPARATOR, X_PX_ENFORCER_LOG_HEADER, EXTERNAL_LOGGER_SERVICE_PATH, - INVALID_VERSION_NUMBER + INVALID_VERSION_NUMBER, + HOST_NAME }; From 2cca40b6cbac4853869a4fb9f50ccbbcfab4876d Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Sun, 21 Jan 2024 19:04:33 +0200 Subject: [PATCH 3/8] Remote loggger and using phin instead of node-fetch to avoid new dependency --- index.js | 2 +- lib/enums/ErrorType.js | 4 +++ lib/pxclient.js | 62 +++++++++++++++++++++++++++------------ lib/pxenforcer.js | 11 ------- lib/pxlogparser.js | 24 +++++++++++++++ lib/pxlogserviceclient.js | 41 -------------------------- package.json | 1 - 7 files changed, 73 insertions(+), 72 deletions(-) create mode 100644 lib/enums/ErrorType.js create mode 100644 lib/pxlogparser.js delete mode 100644 lib/pxlogserviceclient.js diff --git a/index.js b/index.js index 53d7b2fc..1e9c98ba 100644 --- a/index.js +++ b/index.js @@ -29,4 +29,4 @@ module.exports = { PxCdEnforcer: require('./lib/pxcdenforcer'), PxCdFirstParty: require('./lib/pxcdfirstparty'), addNonce: require('./lib/nonce') -}; \ No newline at end of file +}; diff --git a/lib/enums/ErrorType.js b/lib/enums/ErrorType.js new file mode 100644 index 00000000..3e583f2c --- /dev/null +++ b/lib/enums/ErrorType.js @@ -0,0 +1,4 @@ +const ErrorType = { + WRITE_REMOTE_CONFIG: 'write_remote_config', +}; +module.exports = { ErrorType }; diff --git a/lib/pxclient.js b/lib/pxclient.js index 49d732af..89f7d52c 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -4,7 +4,11 @@ const pxUtil = require('./pxutil'); const pxHttpc = require('./pxhttpc'); const { ActivityType } = require('./enums/ActivityType'); const { CIVersion } = require('./enums/CIVersion'); -const fetch = require('node-fetch'); +const p = require('agent-phin'); +const { LoggerSeverity } = require('./enums/LoggerSeverity'); +const { PxLogsParser } = require('./pxlogparser'); +const PxLogger = require('./pxlogger'); +const PxConfig = require('./pxconfig'); const { CI_VERSION_FIELD, CI_SSO_STEP_FIELD, @@ -19,6 +23,7 @@ const { class PxClient { constructor() { this.activitiesBuffer = []; + this.logsBuffer = []; // TODO: add buffer to logs and flush it at the End of the Enforcer flow } init() { @@ -59,23 +64,24 @@ class PxClient { activity.details = details; return activity; } - async fetchRemoteConfig(appId, remoteConfigToken) { - const callData = { - url: `https://sapi-${appId}.perimeterx.net/config/`, - headers: { 'Authorization': `Bearer ${remoteConfigToken}`, 'Accept-Encoding': '' }, - timeout: 20000, - }; - - const response = await fetch(callData.url, { headers: callData.headers, timeout: callData.timeout }); - const remoteConfigObject = await response.json(); - - const remoteConfig = { - px_remote_config_id: remoteConfigObject.id, - px_remote_config_version: remoteConfigObject.version, - ... remoteConfigObject.configValue - }; - - return remoteConfig; + async fetchRemoteConfig(config) { + try { + const callData = { + url: `https://sapi-${config.px_app_id}.perimeterx.net/config/`, + headers: { 'Authorization': `Bearer ${config.px_remote_config_secret}`, 'Accept-Encoding': '' }, + timeout: 20000, + }; + const res = await p({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }); + const remoteConfigObject = JSON.parse(res.body); + return { + px_remote_config_id: remoteConfigObject.id, + px_remote_config_version: remoteConfigObject.version, + ... remoteConfigObject.configValue + }; + } catch (e) { + this.sendRemoteLog(`Error fetching remote configurations: ${e.message}`, LoggerSeverity.DEBUG, 'write_remote_config', config); + return undefined; + } } addAdditionalFieldsToActivity(details, ctx) { @@ -203,5 +209,25 @@ class PxClient { cb(); } } + + sendRemoteLog(message, severity, errorType, config) { + const enforcerConfig = new PxConfig(config, new PxLogger(config)); + const reqHeaders = { + 'Authorization': 'Bearer ' + enforcerConfig.config.LOGGER_AUTH_TOKEN, + 'Content-Type': 'application/json', + }; + const logParser = new PxLogsParser(config); + const logs = [{message, severity, errorType}]; + logParser.enrichLogs(logs); + pxHttpc.callServer( + logs, + reqHeaders, + '/enforcer-logs/', + 'remote-log', + enforcerConfig.conf, + null, + false, + ); + } } module.exports = PxClient; diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js index def248c3..f2565ee1 100644 --- a/lib/pxenforcer.js +++ b/lib/pxenforcer.js @@ -30,7 +30,6 @@ const { CI_CREDENTIALS_COMPROMISED_FIELD, } = require('./utils/constants'); const pxCors = require('./pxcors'); -const { LogServiceClient } = require('./pxlogserviceclient'); class PxEnforcer { constructor(params, client) { @@ -48,8 +47,6 @@ class PxEnforcer { this.reversePrefix = this.pxConfig.conf.PX_APP_ID.substring(2); this.initializeCredentialsIntelligence(this.logger, this._config); - - this.logServiceClient = new LogServiceClient(this._config, this.pxClient); } initializeCredentialsIntelligence(logger, config) { @@ -642,14 +639,6 @@ class PxEnforcer { cb(htmlTemplate); }); } - - sendHeaderBasedLogs(pxCtx, config, req) { - const headerValue = pxCtx ? pxCtx.headers[Constants.X_PX_ENFORCER_LOG_HEADER] : req.headers[Constants.X_PX_ENFORCER_LOG_HEADER]; - if (headerValue && headerValue === config.LOGGER_AUTH_TOKEN) { - this.logServiceClient.sendLogs(pxCtx, config.logger.logs, req); - } - config.logger.logs = []; - } } module.exports = PxEnforcer; diff --git a/lib/pxlogparser.js b/lib/pxlogparser.js new file mode 100644 index 00000000..49f2caed --- /dev/null +++ b/lib/pxlogparser.js @@ -0,0 +1,24 @@ +const PxConfig = require('./pxconfig'); + +class PxLogsParser { + constructor(config) { + this.config = new PxConfig(config, null); + + } + enrichLogs(logs) { + return logs.map((log) => this.enrichLogRecord(log)); + } + + enrichLogRecord(log) { + log.message = log.message.substring(0, this.config.px_external_logger_max_message_size || log.message.length); + Object.assign(log, { + messageTimestamp: new Date().toISOString(), + appID: this.config.config.PX_APP_ID, + container: 'enforcer', + configID: this.config.config.REMOTE_CONFIG_ID, + configVersion: this.config.config.REMOTE_CONFIG_VERSION + }); + } +} + +module.exports = { PxLogsParser }; diff --git a/lib/pxlogserviceclient.js b/lib/pxlogserviceclient.js deleted file mode 100644 index d99d6428..00000000 --- a/lib/pxlogserviceclient.js +++ /dev/null @@ -1,41 +0,0 @@ -const { EXTERNAL_LOGGER_SERVICE_PATH } = require('./utils/constants'); - -class LogServiceClient { - constructor(config, pxClient) { - this.config = config; - this.appId = config.PX_APP_ID; - this.pxClient = pxClient; - } - - sendLogs(pxCtx, logs, req) { - try { - const enrichedLogs = logs.map((log) => this.enrichLogRecord(pxCtx, log, req)); - this.postLogs(enrichedLogs); - } catch (e) { - this.config.logger.error(`unable to send logs: + ${e}`); - } - } - - enrichLogRecord(pxCtx, logs, req) { - const logMetadata = { - container: 'enforcer', - appID: this.appId, - method: pxCtx ? pxCtx.httpMethod : req.method || '', - host: pxCtx ? pxCtx.hostname : req.hostname || req.get('host'), - path: pxCtx ? pxCtx.uri : req.originalUrl || '/', - requestId: pxCtx ? pxCtx.requestId : '' - }; - - return { ... logMetadata, ...logs }; - } - - postLogs(enrichLogs) { - const reqHeaders = { - Authorization: 'Bearer ' + this.config.LOGGER_AUTH_TOKEN - }; - - this.pxClient.callServer(enrichLogs, EXTERNAL_LOGGER_SERVICE_PATH, reqHeaders, this.config); - } -} - -module.exports = { LogServiceClient }; \ No newline at end of file diff --git a/package.json b/package.json index e067c9bc..6bdf02d2 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "https-proxy-agent": "^5.0.0", "ip-range-check": "^0.2.0", "mu2": "^0.5.21", - "node-fetch": "^2.7.0", "raw-body": "^2.3.2", "uuid": "^8.3.2" }, From 63eadb974d2d730fb26e093a6b07f29344e8b323 Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Mon, 22 Jan 2024 09:08:07 +0200 Subject: [PATCH 4/8] Small refactor and align with rebase --- lib/pxclient.js | 4 ++-- lib/pxconfig.js | 6 ++++-- lib/pxhttpc.js | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/pxclient.js b/lib/pxclient.js index 89f7d52c..10836129 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -17,7 +17,7 @@ const { GQL_OPERATIONS_FIELD, APP_USER_ID_FIELD_NAME, JWT_ADDITIONAL_FIELDS_FIELD_NAME, - CROSS_TAB_SESSION, HOST_NAME, + CROSS_TAB_SESSION, HOST_NAME, EXTERNAL_LOGGER_SERVICE_PATH, } = require('./utils/constants'); class PxClient { @@ -222,7 +222,7 @@ class PxClient { pxHttpc.callServer( logs, reqHeaders, - '/enforcer-logs/', + EXTERNAL_LOGGER_SERVICE_PATH, 'remote-log', enforcerConfig.conf, null, diff --git a/lib/pxconfig.js b/lib/pxconfig.js index d54eac10..5c207dc6 100644 --- a/lib/pxconfig.js +++ b/lib/pxconfig.js @@ -119,7 +119,8 @@ class PxConfig { ['REMOTE_CONFIG_ENABLED', 'px_remote_config_enabled'], ['REMOTE_CONFIG_AUTH_TOKEN', 'px_remote_config_auth_token'], ['REMOTE_CONFIG_ID', 'px_remote_config_id'], - ['REMOTE_CONFIG_VERSION', 'px_remote_config_version'] + ['REMOTE_CONFIG_VERSION', 'px_remote_config_version'], + ['LOGGER_AUTH_TOKEN', 'px_logger_auth_token'] ]; configKeyMapping.forEach(([targetKey, sourceKey]) => { @@ -384,7 +385,8 @@ function pxDefaultConfig() { REMOTE_CONFIG_ENABLED: false, REMOTE_CONFIG_AUTH_TOKEN: '', REMOTE_CONFIG_ID: '', - REMOTE_CONFIG_VERSION: INVALID_VERSION_NUMBER + REMOTE_CONFIG_VERSION: INVALID_VERSION_NUMBER, + LOGGER_AUTH_TOKEN: '' }; } diff --git a/lib/pxhttpc.js b/lib/pxhttpc.js index 0b4e47f6..9517590c 100644 --- a/lib/pxhttpc.js +++ b/lib/pxhttpc.js @@ -33,6 +33,9 @@ function callServer(data, headers, uri, callType, config, callback, failOnEmptyB try { request.post(callData, config, function (err, response) { + if (callType === 'remote-log') { + return; + } if (err) { if (err.toString().toLowerCase().includes('timeout')) { return callback('timeout'); From f05746fd8817caee3120701d473c24d962fe7990 Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Tue, 23 Jan 2024 16:58:53 +0200 Subject: [PATCH 5/8] PR Fixes #1 --- lib/pxapi.js | 4 +++- lib/pxclient.js | 33 +++++++++++++++++++++++---------- lib/pxconfig.js | 12 ------------ lib/pxenforcer.js | 5 +++-- lib/pxexternallogparser.js | 22 ++++++++++++++++++++++ lib/pxlogparser.js | 24 ------------------------ 6 files changed, 51 insertions(+), 49 deletions(-) create mode 100644 lib/pxexternallogparser.js delete mode 100644 lib/pxlogparser.js diff --git a/lib/pxapi.js b/lib/pxapi.js index a8853430..47ab95dd 100644 --- a/lib/pxapi.js +++ b/lib/pxapi.js @@ -66,11 +66,13 @@ function buildRequestData(ctx, config) { cookie_origin: ctx.cookieOrigin, request_cookie_names: ctx.requestCookieNames, request_id: ctx.requestId, - px_remote_config_id: config.REMOTE_CONFIG_ID ? config.REMOTE_CONFIG_ID : '', hostname: os.hostname() }, }; + if (config.REMOTE_CONFIG_ID) { + data.additional.px_remote_config_id = config.REMOTE_CONFIG_ID; + } if (ctx.graphqlData) { data.additional[GQL_OPERATIONS_FIELD] = ctx.graphqlData; } diff --git a/lib/pxclient.js b/lib/pxclient.js index 10836129..e87962d3 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -6,7 +6,7 @@ const { ActivityType } = require('./enums/ActivityType'); const { CIVersion } = require('./enums/CIVersion'); const p = require('agent-phin'); const { LoggerSeverity } = require('./enums/LoggerSeverity'); -const { PxLogsParser } = require('./pxlogparser'); +const { PxExternalLogsParser } = require('./pxexternallogparser'); const PxLogger = require('./pxlogger'); const PxConfig = require('./pxconfig'); const { @@ -17,19 +17,28 @@ const { GQL_OPERATIONS_FIELD, APP_USER_ID_FIELD_NAME, JWT_ADDITIONAL_FIELDS_FIELD_NAME, - CROSS_TAB_SESSION, HOST_NAME, EXTERNAL_LOGGER_SERVICE_PATH, + CROSS_TAB_SESSION, HOST_NAME, EXTERNAL_LOGGER_SERVICE_PATH, INVALID_VERSION_NUMBER, } = require('./utils/constants'); +const { ErrorType } = require('./enums/ErrorType'); class PxClient { constructor() { this.activitiesBuffer = []; - this.logsBuffer = []; // TODO: add buffer to logs and flush it at the End of the Enforcer flow + this._isRemoteConfigOutdated = false; } init() { //stub for overriding } + set isRemoteConfigOutdated(value) { + this._isRemoteConfigOutdated = value; + } + + get isRemoteConfigOutdated() { + return this._isRemoteConfigOutdated; + } + /** * generateActivity - returns a JSON representing the activity. * @param {string} activityType - name of the activity @@ -48,9 +57,8 @@ class PxClient { vid: ctx.vid ? ctx.vid : undefined, }; details['request_id'] = ctx.requestId; - details['px_remote_config_version'] = config.remoteConfigVersion; - this.addAdditionalFieldsToActivity(details, ctx); + this.addAdditionalFieldsToActivity(details, ctx, config); if (activityType !== ActivityType.ADDITIONAL_S2S) { activity.headers = pxUtil.formatHeaders(ctx.headers, config.SENSITIVE_HEADERS); activity.pxhd = (ctx.pxhdServer ? ctx.pxhdServer : ctx.pxhdClient) || undefined; @@ -76,15 +84,15 @@ class PxClient { return { px_remote_config_id: remoteConfigObject.id, px_remote_config_version: remoteConfigObject.version, - ... remoteConfigObject.configValue + ...remoteConfigObject.configValue }; } catch (e) { - this.sendRemoteLog(`Error fetching remote configurations: ${e.message}`, LoggerSeverity.DEBUG, 'write_remote_config', config); - return undefined; + const message = `Error fetching remote configurations: ${e.message}`; + this.sendRemoteLog(message, LoggerSeverity.DEBUG, ErrorType.WRITE_REMOTE_CONFIG, config); } } - addAdditionalFieldsToActivity(details, ctx) { + addAdditionalFieldsToActivity(details, ctx, config) { if (ctx.additionalFields && ctx.additionalFields.loginCredentials) { const { loginCredentials } = ctx.additionalFields; details[CI_VERSION_FIELD] = loginCredentials.version; @@ -110,7 +118,12 @@ class PxClient { } } + if (config.remoteConfigVersion !== INVALID_VERSION_NUMBER) { + details['px_remote_config_version'] = config.remoteConfigVersion; + } + details[HOST_NAME] = os.hostname(); + if (ctx.cts) { details[CROSS_TAB_SESSION] = ctx.cts; } @@ -216,7 +229,7 @@ class PxClient { 'Authorization': 'Bearer ' + enforcerConfig.config.LOGGER_AUTH_TOKEN, 'Content-Type': 'application/json', }; - const logParser = new PxLogsParser(config); + const logParser = new PxExternalLogsParser({appId: config.PX_APP_ID, remoteConfigId: config.REMOTE_CONFIG_ID, remoteConfigVersion: config.REMOTE_CONFIG_VERSION}); const logs = [{message, severity, errorType}]; logParser.enrichLogs(logs); pxHttpc.callServer( diff --git a/lib/pxconfig.js b/lib/pxconfig.js index 5c207dc6..c3d1fc55 100644 --- a/lib/pxconfig.js +++ b/lib/pxconfig.js @@ -14,7 +14,6 @@ class PxConfig { this.PX_DEFAULT = pxDefaultConfig(); this.logger = logger; this.config = this.mergeParams(params); - this._remoteConfigOutdated = false; this.config.FILTER_BY_METHOD = this.config.FILTER_BY_METHOD.map((v) => v.toUpperCase()); this.config.logger = this.logger; this.config.WHITELIST_EXT = [...this.PX_INTERNAL.STATIC_FILES_EXT, ...this.PX_DEFAULT.WHITELIST_EXT]; @@ -30,17 +29,6 @@ class PxConfig { this.configLoader = null; } - get remoteConfigVersion() { - return this.config.px_remote_config_version || INVALID_VERSION_NUMBER; - } - - get isRemoteConfigOutdated() { - return this._remoteConfigOutdated; - } - set isRemoteConfigOutdated(value) { - this._remoteConfigOutdated = value; - } - mergeParams(params) { params = this.mergeConfigFileParams(params); diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js index a5ee0c69..9e0ed894 100644 --- a/lib/pxenforcer.js +++ b/lib/pxenforcer.js @@ -261,6 +261,8 @@ class PxEnforcer { pxApi.evalByServerCall(ctx, this._config, (action) => { ctx.riskRtt = Date.now() - startRiskRtt; + this.pxClient.isRemoteConfigOutdated = ctx.isRemoteConfigOutdated === true; + if (action === ScoreEvaluateAction.UNEXPECTED_RESULT) { this.logger.debug('perimeterx score evaluation failed. unexpected error. passing traffic'); return callback(ScoreEvaluateAction.S2S_PASS_TRAFFIC); @@ -308,7 +310,6 @@ class PxEnforcer { handleVerification(ctx, req, res, cb) { const verified = ctx.score < this._config.BLOCKING_SCORE; - this.config.isRemoteConfigOutdated = ctx.isRemoteConfigOutdated; if (res) { const setCookie = res.getHeader('Set-Cookie') ? res.getHeader('Set-Cookie') : ''; const secure = this._config.PXHD_SECURE ? '; Secure' : ''; @@ -639,7 +640,7 @@ class PxEnforcer { cb(htmlTemplate); }); } - + sendHeaderBasedLogs(pxCtx, config, req) { // eslint-disable-line // Feature has been removed, function definition for backwards compatibility. } diff --git a/lib/pxexternallogparser.js b/lib/pxexternallogparser.js new file mode 100644 index 00000000..c41a625c --- /dev/null +++ b/lib/pxexternallogparser.js @@ -0,0 +1,22 @@ +class PxExternalLogsParser { + constructor(appId, remoteConfigId, remoteConfigVersion) { + this.appId = appId; + this.remoteConfigId = remoteConfigId; + this.remoteConfigVersion = remoteConfigVersion; + } + enrichLogs(logs) { + return logs.map((log) => this.enrichLogRecord(log)); + } + + enrichLogRecord(log) { + Object.assign(log, { + messageTimestamp: new Date().toISOString(), + appID: this.appId, + container: 'enforcer', + configID: this.remoteConfigId, + configVersion: this.remoteConfigVersion + }); + } +} + +module.exports = { PxExternalLogsParser }; diff --git a/lib/pxlogparser.js b/lib/pxlogparser.js deleted file mode 100644 index 49f2caed..00000000 --- a/lib/pxlogparser.js +++ /dev/null @@ -1,24 +0,0 @@ -const PxConfig = require('./pxconfig'); - -class PxLogsParser { - constructor(config) { - this.config = new PxConfig(config, null); - - } - enrichLogs(logs) { - return logs.map((log) => this.enrichLogRecord(log)); - } - - enrichLogRecord(log) { - log.message = log.message.substring(0, this.config.px_external_logger_max_message_size || log.message.length); - Object.assign(log, { - messageTimestamp: new Date().toISOString(), - appID: this.config.config.PX_APP_ID, - container: 'enforcer', - configID: this.config.config.REMOTE_CONFIG_ID, - configVersion: this.config.config.REMOTE_CONFIG_VERSION - }); - } -} - -module.exports = { PxLogsParser }; From d24c311debe7e5a7032753c3a6eed45dd38b594e Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Tue, 23 Jan 2024 17:58:07 +0200 Subject: [PATCH 6/8] PR Fixes - Validate Config ID and Version --- lib/pxapi.js | 6 +++--- lib/pxclient.js | 10 ++++++++++ lib/pxenforcer.js | 5 ++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/pxapi.js b/lib/pxapi.js index 47ab95dd..ede2ce73 100644 --- a/lib/pxapi.js +++ b/lib/pxapi.js @@ -70,7 +70,7 @@ function buildRequestData(ctx, config) { }, }; - if (config.REMOTE_CONFIG_ID) { + if (config.REMOTE_CONFIG_ENABLED && config.REMOTE_CONFIG_ID) { data.additional.px_remote_config_id = config.REMOTE_CONFIG_ID; } if (ctx.graphqlData) { @@ -194,8 +194,8 @@ function evalByServerCall(ctx, config, callback) { return callback(ScoreEvaluateAction.UNEXPECTED_RESULT); } ctx.pxhdServer = res.pxhd; - if (res.remote_config && res.remote_config.id === config.REMOTE_CONFIG_ID) { - ctx.isRemoteConfigOutdated = res.remote_config.version > config.REMOTE_CONFIG_VERSION; + if (config.REMOTE_CONFIG_ENABLED && res.remote_config && res.remote_config.id === config.REMOTE_CONFIG_ID) { + ctx.remoteConfigLatestVersion = res.remote_config.version; } if (res.data_enrichment) { diff --git a/lib/pxclient.js b/lib/pxclient.js index e87962d3..5c995e67 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -25,6 +25,7 @@ class PxClient { constructor() { this.activitiesBuffer = []; this._isRemoteConfigOutdated = false; + this.remoteConfigLatestVersion = INVALID_VERSION_NUMBER; } init() { @@ -39,6 +40,9 @@ class PxClient { return this._isRemoteConfigOutdated; } + set remoteConfigLatestVersion(value) { + this._remoteConfigLatestVersion = value; + } /** * generateActivity - returns a JSON representing the activity. * @param {string} activityType - name of the activity @@ -81,6 +85,12 @@ class PxClient { }; const res = await p({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }); const remoteConfigObject = JSON.parse(res.body); + if (remoteConfigObject.id !== config.px_remote_config_id) { + throw new Error(`Remote configuration id mismatch. Expected: ${config.px_remote_config_id}, Actual: ${remoteConfigObject.id}`); + } + if (this._remoteConfigLatestVersion !== INVALID_VERSION_NUMBER && remoteConfigObject.version !== this._remoteConfigLatestVersion) { + throw new Error(`Remote configuration version mismatch. Expected: ${this._remoteConfigLatestVersion}, Actual: ${remoteConfigObject.version}`); + } return { px_remote_config_id: remoteConfigObject.id, px_remote_config_version: remoteConfigObject.version, diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js index 9e0ed894..05c177d6 100644 --- a/lib/pxenforcer.js +++ b/lib/pxenforcer.js @@ -261,7 +261,10 @@ class PxEnforcer { pxApi.evalByServerCall(ctx, this._config, (action) => { ctx.riskRtt = Date.now() - startRiskRtt; - this.pxClient.isRemoteConfigOutdated = ctx.isRemoteConfigOutdated === true; + if (this.config.config.REMOTE_CONFIG_ENABLED && ctx.remoteConfigLatestVersion > this._config.REMOTE_CONFIG_VERSION) { + this.pxClient.isRemoteConfigOutdated = true; + this.pxClient.remoteConfigLatestVersion = ctx.remoteConfigLatestVersion; + } if (action === ScoreEvaluateAction.UNEXPECTED_RESULT) { this.logger.debug('perimeterx score evaluation failed. unexpected error. passing traffic'); From 4978994bf1e20903524d3518a70cefd350392d8d Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Tue, 23 Jan 2024 18:13:03 +0200 Subject: [PATCH 7/8] Retry mechanism when fetching the configuration of 5 max retry as in spec --- lib/pxclient.js | 55 ++++++++++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/pxclient.js b/lib/pxclient.js index 5c995e67..beca3aa4 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -76,29 +76,38 @@ class PxClient { activity.details = details; return activity; } + async fetchRemoteConfig(config) { - try { - const callData = { - url: `https://sapi-${config.px_app_id}.perimeterx.net/config/`, - headers: { 'Authorization': `Bearer ${config.px_remote_config_secret}`, 'Accept-Encoding': '' }, - timeout: 20000, - }; - const res = await p({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }); - const remoteConfigObject = JSON.parse(res.body); - if (remoteConfigObject.id !== config.px_remote_config_id) { - throw new Error(`Remote configuration id mismatch. Expected: ${config.px_remote_config_id}, Actual: ${remoteConfigObject.id}`); - } - if (this._remoteConfigLatestVersion !== INVALID_VERSION_NUMBER && remoteConfigObject.version !== this._remoteConfigLatestVersion) { - throw new Error(`Remote configuration version mismatch. Expected: ${this._remoteConfigLatestVersion}, Actual: ${remoteConfigObject.version}`); + const maxRetries = 5; + for (let i = 0; i < maxRetries; i++) { + try { + const callData = { + url: `https://sapi-${config.px_app_id}.perimeterx.net/config/`, + headers: { 'Authorization': `Bearer ${config.px_remote_config_secret}`, 'Accept-Encoding': '' }, + timeout: 20000, + }; + const res = await p({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }); + const remoteConfigObject = JSON.parse(res.body); + if (remoteConfigObject.id !== config.px_remote_config_id) { + throw new Error(`Remote configuration id mismatch. Expected: ${config.px_remote_config_id}, Actual: ${remoteConfigObject.id}`); + } + if (this._remoteConfigLatestVersion !== INVALID_VERSION_NUMBER && remoteConfigObject.version !== this._remoteConfigLatestVersion) { + throw new Error(`Remote configuration version mismatch. Expected: ${this._remoteConfigLatestVersion}, Actual: ${remoteConfigObject.version}`); + } + return { + px_remote_config_id: remoteConfigObject.id, + px_remote_config_version: remoteConfigObject.version, + ...remoteConfigObject.configValue + }; + } catch (e) { + const message = `Error fetching remote configurations: ${e.message}`; + this.sendRemoteLog(message, LoggerSeverity.DEBUG, ErrorType.WRITE_REMOTE_CONFIG, config); + if (i < maxRetries - 1) { // if it's not the last retry + await new Promise(resolve => setTimeout(resolve, 1000)); // wait for 1 second before retrying + } else { + config.logger.error('Failed to fetch remote configuration after 5 attempts'); + } } - return { - px_remote_config_id: remoteConfigObject.id, - px_remote_config_version: remoteConfigObject.version, - ...remoteConfigObject.configValue - }; - } catch (e) { - const message = `Error fetching remote configurations: ${e.message}`; - this.sendRemoteLog(message, LoggerSeverity.DEBUG, ErrorType.WRITE_REMOTE_CONFIG, config); } } @@ -239,8 +248,8 @@ class PxClient { 'Authorization': 'Bearer ' + enforcerConfig.config.LOGGER_AUTH_TOKEN, 'Content-Type': 'application/json', }; - const logParser = new PxExternalLogsParser({appId: config.PX_APP_ID, remoteConfigId: config.REMOTE_CONFIG_ID, remoteConfigVersion: config.REMOTE_CONFIG_VERSION}); - const logs = [{message, severity, errorType}]; + const logParser = new PxExternalLogsParser( { appId: config.PX_APP_ID, remoteConfigId: config.REMOTE_CONFIG_ID, remoteConfigVersion: config.REMOTE_CONFIG_VERSION }); + const logs = [{ message, severity, errorType }]; logParser.enrichLogs(logs); pxHttpc.callServer( logs, From 52b3b361aadcabd52a9551fee15efe0a7559e6b6 Mon Sep 17 00:00:00 2001 From: AsafAklerPX <82310719+AsafAklerPX@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:24:15 +0200 Subject: [PATCH 8/8] PR Fixes --- lib/pxclient.js | 53 +++++++++++++++++++------------------- lib/pxenforcer.js | 3 +-- lib/pxexternallogparser.js | 9 ++++--- lib/request.js | 10 +++++++ 4 files changed, 44 insertions(+), 31 deletions(-) diff --git a/lib/pxclient.js b/lib/pxclient.js index beca3aa4..23445f45 100644 --- a/lib/pxclient.js +++ b/lib/pxclient.js @@ -4,7 +4,7 @@ const pxUtil = require('./pxutil'); const pxHttpc = require('./pxhttpc'); const { ActivityType } = require('./enums/ActivityType'); const { CIVersion } = require('./enums/CIVersion'); -const p = require('agent-phin'); +const { makeAsyncRequest } = require('./request'); const { LoggerSeverity } = require('./enums/LoggerSeverity'); const { PxExternalLogsParser } = require('./pxexternallogparser'); const PxLogger = require('./pxlogger'); @@ -24,20 +24,14 @@ const { ErrorType } = require('./enums/ErrorType'); class PxClient { constructor() { this.activitiesBuffer = []; - this._isRemoteConfigOutdated = false; - this.remoteConfigLatestVersion = INVALID_VERSION_NUMBER; + this._remoteConfigLatestVersion = INVALID_VERSION_NUMBER; } init() { //stub for overriding } - - set isRemoteConfigOutdated(value) { - this._isRemoteConfigOutdated = value; - } - - get isRemoteConfigOutdated() { - return this._isRemoteConfigOutdated; + get remoteConfigLatestVersion() { + return this._remoteConfigLatestVersion; } set remoteConfigLatestVersion(value) { @@ -81,19 +75,8 @@ class PxClient { const maxRetries = 5; for (let i = 0; i < maxRetries; i++) { try { - const callData = { - url: `https://sapi-${config.px_app_id}.perimeterx.net/config/`, - headers: { 'Authorization': `Bearer ${config.px_remote_config_secret}`, 'Accept-Encoding': '' }, - timeout: 20000, - }; - const res = await p({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }); - const remoteConfigObject = JSON.parse(res.body); - if (remoteConfigObject.id !== config.px_remote_config_id) { - throw new Error(`Remote configuration id mismatch. Expected: ${config.px_remote_config_id}, Actual: ${remoteConfigObject.id}`); - } - if (this._remoteConfigLatestVersion !== INVALID_VERSION_NUMBER && remoteConfigObject.version !== this._remoteConfigLatestVersion) { - throw new Error(`Remote configuration version mismatch. Expected: ${this._remoteConfigLatestVersion}, Actual: ${remoteConfigObject.version}`); - } + + const remoteConfigObject = await this.getRemoteConfigObject(config); return { px_remote_config_id: remoteConfigObject.id, px_remote_config_version: remoteConfigObject.version, @@ -111,6 +94,23 @@ class PxClient { } } + async getRemoteConfigObject(config) { + const callData = { + url: `https://sapi-${config.px_app_id}.perimeterx.net/config/`, + headers: { 'Authorization': `Bearer ${config.px_remote_config_secret}`, 'Accept-Encoding': '' }, + timeout: 20000, + }; + const res = await makeAsyncRequest({ url: callData.url, headers: callData.headers, timeout: callData.timeout, method: 'GET' }, config); + const remoteConfigObject = JSON.parse(res.body); + if (remoteConfigObject.id !== config.px_remote_config_id) { + throw new Error(`Remote configuration id mismatch. Expected: ${config.px_remote_config_id}, Actual: ${remoteConfigObject.id}`); + } + if (this._remoteConfigLatestVersion !== INVALID_VERSION_NUMBER && remoteConfigObject.version !== this._remoteConfigLatestVersion) { + throw new Error(`Remote configuration version mismatch. Expected: ${this._remoteConfigLatestVersion}, Actual: ${remoteConfigObject.version}`); + } + return remoteConfigObject; + } + addAdditionalFieldsToActivity(details, ctx, config) { if (ctx.additionalFields && ctx.additionalFields.loginCredentials) { const { loginCredentials } = ctx.additionalFields; @@ -243,16 +243,17 @@ class PxClient { } sendRemoteLog(message, severity, errorType, config) { - const enforcerConfig = new PxConfig(config, new PxLogger(config)); + const pxLogger = config.logger ? config.logger : new PxLogger(config); + const enforcerConfig = new PxConfig(config, pxLogger); const reqHeaders = { 'Authorization': 'Bearer ' + enforcerConfig.config.LOGGER_AUTH_TOKEN, 'Content-Type': 'application/json', }; const logParser = new PxExternalLogsParser( { appId: config.PX_APP_ID, remoteConfigId: config.REMOTE_CONFIG_ID, remoteConfigVersion: config.REMOTE_CONFIG_VERSION }); const logs = [{ message, severity, errorType }]; - logParser.enrichLogs(logs); + const enrichedLogs = logParser.enrichLogs(logs); pxHttpc.callServer( - logs, + enrichedLogs, reqHeaders, EXTERNAL_LOGGER_SERVICE_PATH, 'remote-log', diff --git a/lib/pxenforcer.js b/lib/pxenforcer.js index 05c177d6..a6d7e2e7 100644 --- a/lib/pxenforcer.js +++ b/lib/pxenforcer.js @@ -261,8 +261,7 @@ class PxEnforcer { pxApi.evalByServerCall(ctx, this._config, (action) => { ctx.riskRtt = Date.now() - startRiskRtt; - if (this.config.config.REMOTE_CONFIG_ENABLED && ctx.remoteConfigLatestVersion > this._config.REMOTE_CONFIG_VERSION) { - this.pxClient.isRemoteConfigOutdated = true; + if (this.config.config.REMOTE_CONFIG_ENABLED) { this.pxClient.remoteConfigLatestVersion = ctx.remoteConfigLatestVersion; } diff --git a/lib/pxexternallogparser.js b/lib/pxexternallogparser.js index c41a625c..a3715ebd 100644 --- a/lib/pxexternallogparser.js +++ b/lib/pxexternallogparser.js @@ -5,17 +5,20 @@ class PxExternalLogsParser { this.remoteConfigVersion = remoteConfigVersion; } enrichLogs(logs) { - return logs.map((log) => this.enrichLogRecord(log)); + const enrichedLogs = logs.map((log) => { + return this.enrichLogRecord(log); + }); + return enrichedLogs; } enrichLogRecord(log) { - Object.assign(log, { + return {...log, ...{ messageTimestamp: new Date().toISOString(), appID: this.appId, container: 'enforcer', configID: this.remoteConfigId, configVersion: this.remoteConfigVersion - }); + }}; } } diff --git a/lib/request.js b/lib/request.js index 0a770769..c537e2ae 100644 --- a/lib/request.js +++ b/lib/request.js @@ -16,6 +16,16 @@ exports.post = (options, config, cb) => { return makeRequest(options, config, cb); }; +exports.makeAsyncRequest = (options, config) => { + return new Promise((resolve, reject) => { + makeRequest(options, config, (err, res) => { + if (err) { + return reject(err); + } + return resolve(res); + }); + }); +}; function makeRequest(options, config, cb) { if (options.url && options.url.startsWith('https://')) { options.agent = config.agent || httpsKeepAliveAgent;