perimeterx-js-core 0.30.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -775,6 +775,13 @@ var ConfigurationBase = /** @class */ (function () {
775
775
  enumerable: false,
776
776
  configurable: true
777
777
  });
778
+ Object.defineProperty(ConfigurationBase.prototype, "dataEnrichmentHeaderName", {
779
+ get: function () {
780
+ return this.configParams.px_data_enrichment_header_name;
781
+ },
782
+ enumerable: false,
783
+ configurable: true
784
+ });
778
785
  Object.defineProperty(ConfigurationBase.prototype, "enableBlockedUrlOnCaptchaBlockPage", {
779
786
  get: function () {
780
787
  return true;
@@ -132,6 +132,7 @@ var defaultConfigurationParams = function () { return ({
132
132
  px_remote_config_retry_interval_ms: 1000,
133
133
  px_url_decode_reserved_characters: false,
134
134
  px_secured_pxhd_enabled: false,
135
+ px_data_enrichment_header_name: '',
135
136
  px_snippet_injection_enabled: false,
136
137
  px_create_custom_snippet: null,
137
138
  px_custom_is_sensitive_request: null,
@@ -66,9 +66,7 @@ var createEnforcerProducts = function (config, products, base64Utils, hashUtils,
66
66
  var _a;
67
67
  var botDefender = (products === null || products === void 0 ? void 0 : products.bd) || new products_1.BotDefender(config, { base64Utils: base64Utils, ipRangeChecker: ipRangeChecker, urlUtils: urlUtils });
68
68
  var accountDefender = (products === null || products === void 0 ? void 0 : products.ad) || new products_1.AccountDefender(config, { base64Utils: base64Utils });
69
- var credentialIntelligence = config.ciEnabled
70
- ? (products === null || products === void 0 ? void 0 : products.ci) || new products_1.CredentialIntelligence(config, { hashUtils: hashUtils, urlUtils: urlUtils })
71
- : undefined;
69
+ var credentialIntelligence = (products === null || products === void 0 ? void 0 : products.ci) || new products_1.CredentialIntelligence(config, { hashUtils: hashUtils, urlUtils: urlUtils });
72
70
  var hypeSaleChallenge = (products === null || products === void 0 ? void 0 : products.hsc) || new products_1.HypeSaleChallenge(config, { base64Utils: base64Utils });
73
71
  return _a = {},
74
72
  _a[products_1.ProductName.BOT_DEFENDER] = botDefender,
@@ -189,9 +189,17 @@ var BotDefender = /** @class */ (function () {
189
189
  BotDefender.prototype.createBlockResponse = function (context) {
190
190
  return this.blocker.createBlockResponse(context);
191
191
  };
192
- BotDefender.prototype.modifyIncomingRequest = function (_context) {
192
+ BotDefender.prototype.modifyIncomingRequest = function (context) {
193
193
  return __awaiter(this, void 0, void 0, function () {
194
194
  return __generator(this, function (_a) {
195
+ if (this.config.dataEnrichmentHeaderName && context.pxde && context.pxdeVerified) {
196
+ try {
197
+ context.requestData.request.headers.set(this.config.dataEnrichmentHeaderName, JSON.stringify(context.pxde));
198
+ }
199
+ catch (err) {
200
+ context.logger.debug("unable to set data enrichment header: ".concat(err));
201
+ }
202
+ }
195
203
  return [2 /*return*/];
196
204
  });
197
205
  });
@@ -55,7 +55,9 @@ var constants_1 = require("./constants.js");
55
55
  var CredentialIntelligence = /** @class */ (function () {
56
56
  function CredentialIntelligence(config, options) {
57
57
  this.config = config;
58
- this.endpointManager = new endpoint_1.CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(config, options));
58
+ this.options = options;
59
+ this.remoteConfigVersion = config.remoteConfigVersion;
60
+ this.endpointManager = new endpoint_1.CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(this.config, this.options));
59
61
  }
60
62
  CredentialIntelligence.prototype.enrichContextFromRequest = function (context) {
61
63
  return __awaiter(this, void 0, void 0, function () {
@@ -63,6 +65,12 @@ var CredentialIntelligence = /** @class */ (function () {
63
65
  return __generator(this, function (_a) {
64
66
  switch (_a.label) {
65
67
  case 0:
68
+ if (!this.config.ciEnabled) {
69
+ return [2 /*return*/, null];
70
+ }
71
+ if (this.shouldRefreshCredentialIntelligenceEndpoints()) {
72
+ this.refreshCredentialIntelligenceEndpoints();
73
+ }
66
74
  endpointIndex = this.endpointManager.getEndpointIndex(context);
67
75
  if (endpointIndex === -1) {
68
76
  return [2 /*return*/, null];
@@ -159,14 +167,30 @@ var CredentialIntelligence = /** @class */ (function () {
159
167
  request.headers.set(constants_1.ADDITIONAL_S2S_URL_HEADER_NAME, "".concat(this.config.backendCollectorUrl).concat(activities_1.ACTIVITIES_ENDPOINT));
160
168
  };
161
169
  CredentialIntelligence.prototype.initializeCredentialIntelligenceEndpoints = function (config, options) {
162
- return config.ciEndpoints.map(function (endpointConfig) {
170
+ var _this = this;
171
+ return config.ciEndpoints
172
+ .map(function (endpointConfig) {
163
173
  var _a;
164
- var matcher = endpoint_1.CredentialIntelligenceEndpointMatcherFactory.create(endpointConfig);
165
- var extractor = endpoint_1.CredentialExtractorFactory.create(endpointConfig, options.urlUtils);
166
- var protocol = endpoint_1.CredentialIntelligenceHashProtocolFactory.create(((_a = endpointConfig.protocol) !== null && _a !== void 0 ? _a : config.ciDefaultVersion), options.hashUtils);
167
- var loginSuccessfulParser = endpoint_1.LoginSuccessfulParserFactory.create(config, endpointConfig);
168
- return new endpoint_1.CredentialEndpoint(matcher, extractor, protocol, loginSuccessfulParser);
169
- });
174
+ try {
175
+ var matcher = endpoint_1.CredentialIntelligenceEndpointMatcherFactory.create(endpointConfig);
176
+ var extractor = endpoint_1.CredentialExtractorFactory.create(endpointConfig, options.urlUtils);
177
+ var protocol = endpoint_1.CredentialIntelligenceHashProtocolFactory.create(((_a = endpointConfig.protocol) !== null && _a !== void 0 ? _a : config.ciDefaultVersion), options.hashUtils);
178
+ var loginSuccessfulParser = endpoint_1.LoginSuccessfulParserFactory.create(config, endpointConfig);
179
+ return new endpoint_1.CredentialEndpoint(matcher, extractor, protocol, loginSuccessfulParser);
180
+ }
181
+ catch (error) {
182
+ _this.config.logger.error("caught error initializing credential intelligence endpoint: ".concat(error));
183
+ return null;
184
+ }
185
+ })
186
+ .filter(function (endpoint) { return endpoint !== null; });
187
+ };
188
+ CredentialIntelligence.prototype.shouldRefreshCredentialIntelligenceEndpoints = function () {
189
+ return this.remoteConfigVersion !== this.config.remoteConfigVersion;
190
+ };
191
+ CredentialIntelligence.prototype.refreshCredentialIntelligenceEndpoints = function () {
192
+ this.remoteConfigVersion = this.config.remoteConfigVersion;
193
+ this.endpointManager = new endpoint_1.CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(this.config, this.options));
170
194
  };
171
195
  return CredentialIntelligence;
172
196
  }());
@@ -15,4 +15,4 @@ exports.PUSH_DATA_FEATURE_HEADER_NAME = 'x-px-feature';
15
15
  exports.EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
16
16
  exports.URL_REGEX = /^(https?:)\/\/(([^@\s:\/]+):?([^@\s\/]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/;
17
17
  exports.REGEX_STRUCTURE = /^\/(.+?)\/([gimsuyvd]*)$/;
18
- exports.CORE_MODULE_VERSION = 'JS Core 0.30.0';
18
+ exports.CORE_MODULE_VERSION = 'JS Core 0.31.0';
@@ -405,6 +405,9 @@ export class ConfigurationBase {
405
405
  get createCustomSnippet() {
406
406
  return this.configParams.px_create_custom_snippet;
407
407
  }
408
+ get dataEnrichmentHeaderName() {
409
+ return this.configParams.px_data_enrichment_header_name;
410
+ }
408
411
  get enableBlockedUrlOnCaptchaBlockPage() {
409
412
  return true;
410
413
  }
@@ -129,6 +129,7 @@ export const defaultConfigurationParams = () => ({
129
129
  px_remote_config_retry_interval_ms: 1000,
130
130
  px_url_decode_reserved_characters: false,
131
131
  px_secured_pxhd_enabled: false,
132
+ px_data_enrichment_header_name: '',
132
133
  px_snippet_injection_enabled: false,
133
134
  px_create_custom_snippet: null,
134
135
  px_custom_is_sensitive_request: null,
@@ -68,9 +68,7 @@ export const createEnforcerInitializationBlock = (config, options) => {
68
68
  export const createEnforcerProducts = (config, products, base64Utils, hashUtils, urlUtils, ipRangeChecker) => {
69
69
  const botDefender = products?.bd || new BotDefender(config, { base64Utils, ipRangeChecker, urlUtils });
70
70
  const accountDefender = products?.ad || new AccountDefender(config, { base64Utils });
71
- const credentialIntelligence = config.ciEnabled
72
- ? products?.ci || new CredentialIntelligence(config, { hashUtils, urlUtils })
73
- : undefined;
71
+ const credentialIntelligence = products?.ci || new CredentialIntelligence(config, { hashUtils, urlUtils });
74
72
  const hypeSaleChallenge = products?.hsc || new HypeSaleChallenge(config, { base64Utils });
75
73
  return {
76
74
  [ProductName.BOT_DEFENDER]: botDefender,
@@ -101,8 +101,15 @@ export class BotDefender {
101
101
  createBlockResponse(context) {
102
102
  return this.blocker.createBlockResponse(context);
103
103
  }
104
- async modifyIncomingRequest(_context) {
105
- // intentionally left blank
104
+ async modifyIncomingRequest(context) {
105
+ if (this.config.dataEnrichmentHeaderName && context.pxde && context.pxdeVerified) {
106
+ try {
107
+ context.requestData.request.headers.set(this.config.dataEnrichmentHeaderName, JSON.stringify(context.pxde));
108
+ }
109
+ catch (err) {
110
+ context.logger.debug(`unable to set data enrichment header: ${err}`);
111
+ }
112
+ }
106
113
  }
107
114
  async modifyOutgoingResponse(_context) {
108
115
  // intentionally left blank
@@ -4,12 +4,22 @@ import { CredentialEndpoint, CredentialEndpointManager, CredentialExtractorFacto
4
4
  import { ADDITIONAL_S2S_ACTIVITY_HEADER_NAME, ADDITIONAL_S2S_URL_HEADER_NAME, COMPROMISED_CREDENTIALS_HEADER_VALUE, } from './constants.js';
5
5
  export class CredentialIntelligence {
6
6
  config;
7
+ options;
8
+ remoteConfigVersion;
7
9
  endpointManager;
8
10
  constructor(config, options) {
9
11
  this.config = config;
10
- this.endpointManager = new CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(config, options));
12
+ this.options = options;
13
+ this.remoteConfigVersion = config.remoteConfigVersion;
14
+ this.endpointManager = new CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(this.config, this.options));
11
15
  }
12
16
  async enrichContextFromRequest(context) {
17
+ if (!this.config.ciEnabled) {
18
+ return null;
19
+ }
20
+ if (this.shouldRefreshCredentialIntelligenceEndpoints()) {
21
+ this.refreshCredentialIntelligenceEndpoints();
22
+ }
13
23
  const endpointIndex = this.endpointManager.getEndpointIndex(context);
14
24
  if (endpointIndex === -1) {
15
25
  return null;
@@ -66,12 +76,27 @@ export class CredentialIntelligence {
66
76
  request.headers.set(ADDITIONAL_S2S_URL_HEADER_NAME, `${this.config.backendCollectorUrl}${ACTIVITIES_ENDPOINT}`);
67
77
  }
68
78
  initializeCredentialIntelligenceEndpoints(config, options) {
69
- return config.ciEndpoints.map((endpointConfig) => {
70
- const matcher = CredentialIntelligenceEndpointMatcherFactory.create(endpointConfig);
71
- const extractor = CredentialExtractorFactory.create(endpointConfig, options.urlUtils);
72
- const protocol = CredentialIntelligenceHashProtocolFactory.create((endpointConfig.protocol ?? config.ciDefaultVersion), options.hashUtils);
73
- const loginSuccessfulParser = LoginSuccessfulParserFactory.create(config, endpointConfig);
74
- return new CredentialEndpoint(matcher, extractor, protocol, loginSuccessfulParser);
75
- });
79
+ return config.ciEndpoints
80
+ .map((endpointConfig) => {
81
+ try {
82
+ const matcher = CredentialIntelligenceEndpointMatcherFactory.create(endpointConfig);
83
+ const extractor = CredentialExtractorFactory.create(endpointConfig, options.urlUtils);
84
+ const protocol = CredentialIntelligenceHashProtocolFactory.create((endpointConfig.protocol ?? config.ciDefaultVersion), options.hashUtils);
85
+ const loginSuccessfulParser = LoginSuccessfulParserFactory.create(config, endpointConfig);
86
+ return new CredentialEndpoint(matcher, extractor, protocol, loginSuccessfulParser);
87
+ }
88
+ catch (error) {
89
+ this.config.logger.error(`caught error initializing credential intelligence endpoint: ${error}`);
90
+ return null;
91
+ }
92
+ })
93
+ .filter((endpoint) => endpoint !== null);
94
+ }
95
+ shouldRefreshCredentialIntelligenceEndpoints() {
96
+ return this.remoteConfigVersion !== this.config.remoteConfigVersion;
97
+ }
98
+ refreshCredentialIntelligenceEndpoints() {
99
+ this.remoteConfigVersion = this.config.remoteConfigVersion;
100
+ this.endpointManager = new CredentialEndpointManager(this.initializeCredentialIntelligenceEndpoints(this.config, this.options));
76
101
  }
77
102
  }
@@ -12,4 +12,4 @@ export const PUSH_DATA_FEATURE_HEADER_NAME = 'x-px-feature';
12
12
  export const EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
13
13
  export const URL_REGEX = /^(https?:)\/\/(([^@\s:\/]+):?([^@\s\/]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/;
14
14
  export const REGEX_STRUCTURE = /^\/(.+?)\/([gimsuyvd]*)$/;
15
- export const CORE_MODULE_VERSION = 'JS Core 0.30.0';
15
+ export const CORE_MODULE_VERSION = 'JS Core 0.31.0';
@@ -143,6 +143,7 @@ export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends
143
143
  get tokenVersion(): `${TokenVersion}`;
144
144
  get snippetInjectionEnabled(): boolean;
145
145
  get createCustomSnippet(): CustomSnippetFunction<Req, Res, Added, Removed> | null;
146
+ get dataEnrichmentHeaderName(): string;
146
147
  get enableBlockedUrlOnCaptchaBlockPage(): boolean;
147
148
  get awaitAsyncHttpRequests(): boolean;
148
149
  get isPostEnforceEnabled(): boolean;
@@ -404,6 +404,10 @@ export interface IConfiguration<Req, Res, Added, Removed> {
404
404
  * The version of the token the enforcer is able to parse.
405
405
  */
406
406
  readonly tokenVersion: `${TokenVersion}`;
407
+ /**
408
+ * The header name to use when adding the data enrichment value as an incoming request header.
409
+ */
410
+ readonly dataEnrichmentHeaderName: string;
407
411
  /**
408
412
  * Returns an object representation of the current configuration.
409
413
  */
@@ -120,6 +120,7 @@ export type CommonConfigurationParams<Req, Res, Added, Removed> = TokenV3Configu
120
120
  px_remote_config_retry_interval_ms?: number;
121
121
  px_url_decode_reserved_characters?: boolean;
122
122
  px_secured_pxhd_enabled?: boolean;
123
+ px_data_enrichment_header_name?: string;
123
124
  px_additional_activity_handler?: AdditionalActivityHandler<Req, Res, Added, Removed> | null;
124
125
  px_enrich_custom_parameters?: CustomParametersFunction<Req, Res, Added, Removed> | null;
125
126
  px_enrich_response_custom_parameters?: ResponseCustomParametersFunction<Req, Res, Added, Removed> | null;
@@ -46,6 +46,6 @@ export declare class BotDefender<Req, Res, Added, Removed> implements IBotDefend
46
46
  protected getSuccessfulRiskApiAction(riskResponse: DeepReadonly<IRiskResponse>, isMonitored: boolean): ActionData;
47
47
  shouldBlock(context: ReadonlyContext<Req, Res>): boolean;
48
48
  createBlockResponse(context: ReadonlyContext<Req, Res>): IMinimalResponse;
49
- modifyIncomingRequest(_context: ReadonlyContext<Req, Res>): Promise<void>;
49
+ modifyIncomingRequest(context: ReadonlyContext<Req, Res>): Promise<void>;
50
50
  modifyOutgoingResponse(_context: ReadonlyContext<Req, Res>): Promise<void>;
51
51
  }
@@ -11,7 +11,9 @@ export type CredentialIntelligenceOptions = {
11
11
  };
12
12
  export declare class CredentialIntelligence<Req, Res, Added, Removed> implements ICredentialIntelligence<Req, Res> {
13
13
  protected readonly config: IConfiguration<Req, Res, Added, Removed>;
14
- protected readonly endpointManager: CredentialEndpointManager<Req, Res>;
14
+ protected readonly options: CredentialIntelligenceOptions;
15
+ protected remoteConfigVersion: number;
16
+ protected endpointManager: CredentialEndpointManager<Req, Res>;
15
17
  constructor(config: IConfiguration<Req, Res, Added, Removed>, options: CredentialIntelligenceOptions);
16
18
  enrichContextFromRequest(context: ReadonlyContext<Req, Res>): Promise<CredentialIntelligenceData | null>;
17
19
  enrichContextFromRiskApi(context: ReadonlyContext<Req, Res>): Promise<Partial<CredentialIntelligenceData> | null>;
@@ -23,4 +25,6 @@ export declare class CredentialIntelligence<Req, Res, Added, Removed> implements
23
25
  protected shouldSetAdditionalS2SActivityHeaders(): boolean;
24
26
  protected setAdditionalS2SActivityHeaders(request: IIncomingRequest<Req>, context: ReadonlyContext<Req, Res>): void;
25
27
  protected initializeCredentialIntelligenceEndpoints(config: IConfiguration<Req, Res, Added, Removed>, options: CredentialIntelligenceOptions): ICredentialEndpoint<Req, Res>[];
28
+ protected shouldRefreshCredentialIntelligenceEndpoints(): boolean;
29
+ protected refreshCredentialIntelligenceEndpoints(): void;
26
30
  }
@@ -12,4 +12,4 @@ export declare const PUSH_DATA_FEATURE_HEADER_NAME = "x-px-feature";
12
12
  export declare const EMAIL_ADDRESS_REGEX: RegExp;
13
13
  export declare const URL_REGEX: RegExp;
14
14
  export declare const REGEX_STRUCTURE: RegExp;
15
- export declare const CORE_MODULE_VERSION = "JS Core 0.30.0";
15
+ export declare const CORE_MODULE_VERSION = "JS Core 0.31.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perimeterx-js-core",
3
- "version": "0.30.0",
3
+ "version": "0.31.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "typesVersions": {
@@ -38,7 +38,7 @@
38
38
  "lint": "eslint . --ext .ts",
39
39
  "lint:fix": "eslint . --ext .ts --fix",
40
40
  "test": "mocha",
41
- "coverage": "nyc npm run test",
41
+ "coverage": "c8 npm run test",
42
42
  "pre-commit": "./node_modules/.bin/lint-staged",
43
43
  "prepare": "husky"
44
44
  },
@@ -58,8 +58,9 @@
58
58
  "@types/node": "^18.19.50 || ^20.6.0 || ^22.0.0",
59
59
  "@types/sinon": "^17.0.1",
60
60
  "@types/uuid": "^10.0.0",
61
- "@typescript-eslint/eslint-plugin": "^8.26.0",
61
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
62
62
  "@typescript-eslint/parser": "^8.26.0",
63
+ "c8": "^10.1.3",
63
64
  "chai": "^5.2.0",
64
65
  "chai-as-promised": "^8.0.0",
65
66
  "core-js": "^3.19.1",
@@ -69,9 +70,8 @@
69
70
  "husky": "^9.1.7",
70
71
  "lint-staged": "^16.0.0",
71
72
  "mocha": "^11.4.0",
72
- "nyc": "^17.0.0",
73
73
  "prettier": "^3.5.3",
74
- "sinon": "^20.0.0",
74
+ "sinon": "^21.0.0",
75
75
  "ts-loader": "^9.4.1",
76
76
  "ts-node": "^10.9.1",
77
77
  "tsc-alias": "^1.8.11",