perimeterx-js-core 0.37.2 → 1.0.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.
Files changed (33) hide show
  1. package/README.md +9 -9
  2. package/lib/cjs/activities/utils.js +1 -1
  3. package/lib/cjs/impl/cipher/SubtleCryptoCipherUtils.js +3 -2
  4. package/lib/cjs/impl/http/{phin/PhinHttpClient.js → undici/UndiciHttpClient.js} +50 -39
  5. package/lib/cjs/impl/http/{phin/PhinHttpProxyClient.js → undici/UndiciHttpProxyClient.js} +12 -13
  6. package/lib/cjs/impl/http/{phin/PhinIncomingResponse.js → undici/UndiciIncomingResponse.js} +11 -14
  7. package/lib/cjs/impl/http/{phin → undici}/index.js +3 -3
  8. package/lib/cjs/products/account_defender/AccountDefender.js +12 -7
  9. package/lib/cjs/risk_api/client/abstract/RiskApiClientBase.js +12 -8
  10. package/lib/cjs/utils/constants.js +1 -1
  11. package/lib/esm/activities/utils.js +1 -1
  12. package/lib/esm/impl/cipher/SubtleCryptoCipherUtils.js +3 -2
  13. package/lib/esm/impl/http/undici/UndiciHttpClient.js +41 -0
  14. package/lib/esm/impl/http/undici/UndiciHttpProxyClient.js +9 -0
  15. package/lib/esm/impl/http/{phin/PhinIncomingResponse.js → undici/UndiciIncomingResponse.js} +2 -3
  16. package/lib/esm/impl/http/undici/index.js +3 -0
  17. package/lib/esm/products/account_defender/AccountDefender.js +11 -7
  18. package/lib/esm/risk_api/client/abstract/RiskApiClientBase.js +9 -5
  19. package/lib/esm/utils/constants.js +1 -1
  20. package/lib/types/impl/http/undici/UndiciHttpClient.d.ts +13 -0
  21. package/lib/types/impl/http/undici/UndiciHttpProxyClient.d.ts +4 -0
  22. package/lib/types/impl/http/{phin/PhinIncomingResponse.d.ts → undici/UndiciIncomingResponse.d.ts} +8 -3
  23. package/lib/types/impl/http/undici/index.d.ts +3 -0
  24. package/lib/types/products/account_defender/AccountDefender.d.ts +4 -0
  25. package/lib/types/risk_api/client/abstract/RiskApiClientBase.d.ts +2 -2
  26. package/lib/types/utils/constants.d.ts +1 -1
  27. package/package.json +11 -13
  28. package/lib/esm/impl/http/phin/PhinHttpClient.js +0 -42
  29. package/lib/esm/impl/http/phin/PhinHttpProxyClient.js +0 -10
  30. package/lib/esm/impl/http/phin/index.js +0 -3
  31. package/lib/types/impl/http/phin/PhinHttpClient.d.ts +0 -14
  32. package/lib/types/impl/http/phin/PhinHttpProxyClient.d.ts +0 -4
  33. package/lib/types/impl/http/phin/index.d.ts +0 -3
package/README.md CHANGED
@@ -96,8 +96,8 @@ When developing your JavaScript/TypeScript enforcer with this library, you will
96
96
  objects such that they conform to these interfaces. Of course, you are free to extend these interfaces as needed.
97
97
 
98
98
  You will also need to implement an HTTP client. You can do this however you see fit: use the platform's built-in API
99
- or use an external library if needed. This library includes implementations using `phin` (`PhinHttpClient` and `PhinIncomingResponse`)
100
- for Node.js-based enforcers.
99
+ or use an external library if needed. This library includes implementations using `undici` (`UndiciHttpClient`,
100
+ `UndiciHttpProxyClient`, and `UndiciIncomingResponse`) for Node.js-based enforcers.
101
101
 
102
102
  ### Crypto
103
103
 
@@ -131,13 +131,13 @@ are provided:
131
131
  All dependencies in this library have been encapsulated into specific implementations with a defined interface and therefore
132
132
  can be swapped with other implementations. These classes are:
133
133
 
134
- | **Library** | **Implementation** | **Used For** |
135
- | :--- |:-----------------------------------------|:-----------------------------------|
136
- | `crypto-js` | `CryptoJSHmacUtils` | PXDE, telemetry, token V2/V3, etc. |
137
- | `js-base64` | `JSBase64Base64Utils` | PXDE, telemetry, token V2, etc. |
138
- | `uuid` | `UuidRequestIdGenerator` | `DefaultContext` |
139
- | `ip-range-check` | `DefaultIpRangeChecker` | `DefaultBotDefenderFilter` |
140
- | `phin` | `PhinHttpClient`, `PhinIncomingResponse` | First party, Risk API, Activities |
134
+ | **Library** | **Implementation** | **Used For** |
135
+ | :--- |:----------------------------------------------------------------------|:-----------------------------------|
136
+ | `crypto-js` | `CryptoJSHmacUtils` | PXDE, telemetry, token V2/V3, etc. |
137
+ | `js-base64` | `JSBase64Base64Utils` | PXDE, telemetry, token V2, etc. |
138
+ | `uuid` | `UuidRequestIdGenerator` | `DefaultContext` |
139
+ | `ip-range-check` | `DefaultIpRangeChecker` | `DefaultBotDefenderFilter` |
140
+ | `undici` | `UndiciHttpClient`, `UndiciHttpProxyClient`, `UndiciIncomingResponse` | First party, Risk API, Activities |
141
141
 
142
142
  ## For Contributors
143
143
 
@@ -260,7 +260,7 @@ var addTokenDataToDetails = function (details, _a) {
260
260
  exports.addTokenDataToDetails = addTokenDataToDetails;
261
261
  var addRiskApiDataToAsyncActivityCommonDetails = function (details, context) {
262
262
  var _a;
263
- if (context.riskApiData.riskRtt) {
263
+ if (context.riskApiData.riskRtt !== undefined) {
264
264
  details.risk_rtt = context.riskApiData.riskRtt;
265
265
  }
266
266
  if (context.riskApiData.s2sCallReason) {
@@ -83,11 +83,12 @@ var SubtleCryptoCipherUtils = /** @class */ (function () {
83
83
  SubtleCryptoCipherUtils.prototype.base64ToArrayBuffer = function (base64String) {
84
84
  var binaryString = this.base64Utils.base64Decode(base64String);
85
85
  var length = binaryString.length;
86
- var bytes = new Uint8Array(length);
86
+ var buffer = new ArrayBuffer(length);
87
+ var bytes = new Uint8Array(buffer);
87
88
  binaryString.split('').forEach(function (char, index) {
88
89
  bytes[index] = char.charCodeAt(0);
89
90
  });
90
- return bytes.buffer;
91
+ return buffer;
91
92
  };
92
93
  return SubtleCryptoCipherUtils;
93
94
  }());
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
2
13
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
14
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
15
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -35,61 +46,61 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
46
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
47
  }
37
48
  };
38
- var __importDefault = (this && this.__importDefault) || function (mod) {
39
- return (mod && mod.__esModule) ? mod : { "default": mod };
40
- };
41
49
  Object.defineProperty(exports, "__esModule", { value: true });
42
- exports.PhinHttpClient = void 0;
43
- var http_1 = require("http");
44
- var https_1 = require("https");
45
- var phin_1 = __importDefault(require("phin"));
50
+ exports.UndiciHttpClient = void 0;
51
+ var undici_1 = require("undici");
52
+ var http_1 = require("../../../http/index.js");
46
53
  var utils_1 = require("../../../utils/index.js");
47
- var http_2 = require("../../../http/index.js");
48
- var PhinIncomingResponse_1 = require("./PhinIncomingResponse.js");
49
- var PhinHttpClient = /** @class */ (function () {
50
- function PhinHttpClient(options) {
54
+ var UndiciIncomingResponse_1 = require("./UndiciIncomingResponse.js");
55
+ var UndiciHttpClient = /** @class */ (function () {
56
+ function UndiciHttpClient(options) {
51
57
  var _a, _b;
52
- this.httpAgent = (_a = options === null || options === void 0 ? void 0 : options.httpAgent) !== null && _a !== void 0 ? _a : null;
53
- this.httpsAgent = (_b = options === null || options === void 0 ? void 0 : options.httpsAgent) !== null && _b !== void 0 ? _b : new https_1.Agent({ keepAlive: true });
58
+ this.httpDispatcher = (_a = options === null || options === void 0 ? void 0 : options.httpDispatcher) !== null && _a !== void 0 ? _a : null;
59
+ this.httpsDispatcher = (_b = options === null || options === void 0 ? void 0 : options.httpsDispatcher) !== null && _b !== void 0 ? _b : new undici_1.Agent();
54
60
  }
55
- PhinHttpClient.prototype.send = function (request, options) {
61
+ UndiciHttpClient.prototype.send = function (request, options) {
56
62
  return __awaiter(this, void 0, void 0, function () {
57
- var res, e_1, isTimeout;
58
- return __generator(this, function (_a) {
59
- switch (_a.label) {
63
+ var response, _a, _b, _c, _d, e_1, isTimeout;
64
+ var _e;
65
+ var _f, _g, _h;
66
+ return __generator(this, function (_j) {
67
+ switch (_j.label) {
60
68
  case 0:
61
- _a.trys.push([0, 2, , 3]);
62
- return [4 /*yield*/, phin_1.default.promisified({
63
- url: request.url,
69
+ _j.trys.push([0, 3, , 4]);
70
+ return [4 /*yield*/, (0, undici_1.request)(request.url, {
64
71
  method: request.method,
65
- headers: (0, http_2.joinHeaderValues)(request.headers),
66
- data: request.body,
67
- timeout: options === null || options === void 0 ? void 0 : options.timeoutMs,
68
- core: {
69
- agent: this.getAgent(request),
70
- },
72
+ headers: (0, http_1.joinHeaderValues)(request.headers),
73
+ body: request.body,
74
+ headersTimeout: options === null || options === void 0 ? void 0 : options.timeoutMs,
75
+ bodyTimeout: options === null || options === void 0 ? void 0 : options.timeoutMs,
76
+ dispatcher: this.getDispatcher(request),
71
77
  })];
72
78
  case 1:
73
- res = _a.sent();
74
- return [2 /*return*/, new PhinIncomingResponse_1.PhinIncomingResponse(res)];
75
- case 2:
76
- e_1 = _a.sent();
77
- isTimeout = e_1 === null || e_1 === void 0 ? void 0 : e_1.toString().toLowerCase().includes('timeout');
79
+ response = _j.sent();
80
+ _a = UndiciIncomingResponse_1.UndiciIncomingResponse.bind;
81
+ _b = [__assign({}, response)];
82
+ _e = {};
83
+ _d = (_c = Buffer).from;
84
+ return [4 /*yield*/, response.body.arrayBuffer()];
85
+ case 2: return [2 /*return*/, new (_a.apply(UndiciIncomingResponse_1.UndiciIncomingResponse, [void 0, __assign.apply(void 0, _b.concat([(_e.body = _d.apply(_c, [_j.sent()]), _e)]))]))()];
86
+ case 3:
87
+ e_1 = _j.sent();
88
+ isTimeout = (_h = (_g = (_f = e_1 === null || e_1 === void 0 ? void 0 : e_1.toString()) === null || _f === void 0 ? void 0 : _f.toLowerCase()) === null || _g === void 0 ? void 0 : _g.includes('timeout')) !== null && _h !== void 0 ? _h : false;
78
89
  throw isTimeout ? new utils_1.EnforcerTimeoutError(options === null || options === void 0 ? void 0 : options.timeoutMs) : e_1;
79
- case 3: return [2 /*return*/];
90
+ case 4: return [2 /*return*/];
80
91
  }
81
92
  });
82
93
  });
83
94
  };
84
- PhinHttpClient.prototype.getAgent = function (request) {
95
+ UndiciHttpClient.prototype.getDispatcher = function (request) {
85
96
  if (request.url.startsWith('https://')) {
86
- return this.httpsAgent;
97
+ return this.httpsDispatcher;
87
98
  }
88
- if (this.httpAgent) {
89
- return this.httpAgent;
99
+ if (this.httpDispatcher) {
100
+ return this.httpDispatcher;
90
101
  }
91
- return new http_1.Agent();
102
+ return new undici_1.Agent();
92
103
  };
93
- return PhinHttpClient;
104
+ return UndiciHttpClient;
94
105
  }());
95
- exports.PhinHttpClient = PhinHttpClient;
106
+ exports.UndiciHttpClient = UndiciHttpClient;
@@ -15,17 +15,16 @@ var __extends = (this && this.__extends) || (function () {
15
15
  };
16
16
  })();
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.PhinHttpProxyClient = void 0;
19
- var PhinHttpClient_1 = require("./PhinHttpClient.js");
20
- var http_proxy_agent_1 = require("http-proxy-agent");
21
- var https_proxy_agent_1 = require("https-proxy-agent");
22
- var PhinHttpProxyClient = /** @class */ (function (_super) {
23
- __extends(PhinHttpProxyClient, _super);
24
- function PhinHttpProxyClient(proxyUrl) {
25
- var httpAgent = new http_proxy_agent_1.HttpProxyAgent(proxyUrl, { keepAlive: true });
26
- var httpsAgent = new https_proxy_agent_1.HttpsProxyAgent(proxyUrl, { keepAlive: true });
27
- return _super.call(this, { httpsAgent: httpsAgent, httpAgent: httpAgent }) || this;
18
+ exports.UndiciHttpProxyClient = void 0;
19
+ var undici_1 = require("undici");
20
+ var UndiciHttpClient_1 = require("./UndiciHttpClient.js");
21
+ var UndiciHttpProxyClient = /** @class */ (function (_super) {
22
+ __extends(UndiciHttpProxyClient, _super);
23
+ function UndiciHttpProxyClient(proxyUrl) {
24
+ var httpDispatcher = new undici_1.ProxyAgent(proxyUrl);
25
+ var httpsDispatcher = new undici_1.ProxyAgent(proxyUrl);
26
+ return _super.call(this, { httpDispatcher: httpDispatcher, httpsDispatcher: httpsDispatcher }) || this;
28
27
  }
29
- return PhinHttpProxyClient;
30
- }(PhinHttpClient_1.PhinHttpClient));
31
- exports.PhinHttpProxyClient = PhinHttpProxyClient;
28
+ return UndiciHttpProxyClient;
29
+ }(UndiciHttpClient_1.UndiciHttpClient));
30
+ exports.UndiciHttpProxyClient = UndiciHttpProxyClient;
@@ -36,14 +36,13 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
36
36
  }
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.PhinIncomingResponse = void 0;
39
+ exports.UndiciIncomingResponse = void 0;
40
40
  var http_1 = require("../../../http/index.js");
41
41
  var utils_1 = require("../../../utils/index.js");
42
42
  var DefaultUrlUtils_1 = require("../../url/DefaultUrlUtils.js");
43
- var PhinIncomingResponse = /** @class */ (function () {
44
- function PhinIncomingResponse(response) {
45
- var _a;
46
- this.status = (_a = response.statusCode) !== null && _a !== void 0 ? _a : 0;
43
+ var UndiciIncomingResponse = /** @class */ (function () {
44
+ function UndiciIncomingResponse(response) {
45
+ this.status = response.statusCode;
47
46
  this.body = response.body;
48
47
  this.headers = Object.fromEntries(Object.entries(response.headers)
49
48
  .map(function (_a) {
@@ -55,7 +54,7 @@ var PhinIncomingResponse = /** @class */ (function () {
55
54
  return !(0, utils_1.isNullOrUndefined)(value);
56
55
  }));
57
56
  }
58
- PhinIncomingResponse.prototype.formData = function () {
57
+ UndiciIncomingResponse.prototype.formData = function () {
59
58
  return __awaiter(this, void 0, void 0, function () {
60
59
  var contentType, formData, _a, _b;
61
60
  var _c;
@@ -78,7 +77,7 @@ var PhinIncomingResponse = /** @class */ (function () {
78
77
  });
79
78
  });
80
79
  };
81
- PhinIncomingResponse.prototype.formUrlEncoded = function () {
80
+ UndiciIncomingResponse.prototype.formUrlEncoded = function () {
82
81
  return __awaiter(this, void 0, void 0, function () {
83
82
  var _a, _b;
84
83
  return __generator(this, function (_c) {
@@ -86,14 +85,12 @@ var PhinIncomingResponse = /** @class */ (function () {
86
85
  case 0:
87
86
  _b = (_a = new DefaultUrlUtils_1.DefaultUrlUtils()).createUrlSearchParams;
88
87
  return [4 /*yield*/, this.text()];
89
- case 1:
90
- // Using DefaultUrlUtils since Phin safely assumes Node.js environment
91
- return [2 /*return*/, _b.apply(_a, [_c.sent()])];
88
+ case 1: return [2 /*return*/, _b.apply(_a, [_c.sent()])];
92
89
  }
93
90
  });
94
91
  });
95
92
  };
96
- PhinIncomingResponse.prototype.json = function () {
93
+ UndiciIncomingResponse.prototype.json = function () {
97
94
  return __awaiter(this, void 0, void 0, function () {
98
95
  var _a, _b;
99
96
  return __generator(this, function (_c) {
@@ -106,13 +103,13 @@ var PhinIncomingResponse = /** @class */ (function () {
106
103
  });
107
104
  });
108
105
  };
109
- PhinIncomingResponse.prototype.text = function () {
106
+ UndiciIncomingResponse.prototype.text = function () {
110
107
  return __awaiter(this, void 0, void 0, function () {
111
108
  return __generator(this, function (_a) {
112
109
  return [2 /*return*/, this.body.toString()];
113
110
  });
114
111
  });
115
112
  };
116
- return PhinIncomingResponse;
113
+ return UndiciIncomingResponse;
117
114
  }());
118
- exports.PhinIncomingResponse = PhinIncomingResponse;
115
+ exports.UndiciIncomingResponse = UndiciIncomingResponse;
@@ -14,6 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- __exportStar(require("./PhinIncomingResponse.js"), exports);
18
- __exportStar(require("./PhinHttpClient.js"), exports);
19
- __exportStar(require("./PhinHttpProxyClient.js"), exports);
17
+ __exportStar(require("./UndiciIncomingResponse.js"), exports);
18
+ __exportStar(require("./UndiciHttpClient.js"), exports);
19
+ __exportStar(require("./UndiciHttpProxyClient.js"), exports);
@@ -127,18 +127,15 @@ var AccountDefender = /** @class */ (function () {
127
127
  : null;
128
128
  };
129
129
  AccountDefender.prototype.extractJwtData = function (jwt, userIdFieldName, additionalFieldNames, context) {
130
+ var _this = this;
130
131
  try {
131
132
  var decodedJwt_1 = this.getDecodedJwt(jwt, context);
132
133
  if (decodedJwt_1) {
133
- var appUserId = utils_1.getPropertyFromObject.apply(void 0, __spreadArray([decodedJwt_1], userIdFieldName.split('.'), false));
134
+ var appUserId = this.lookupJwtField(decodedJwt_1, userIdFieldName).value;
134
135
  var additionalFields = additionalFieldNames.reduce(function (matchedFields, fieldName) {
135
- var fieldNameParts = fieldName.split('.');
136
- var value = utils_1.getPropertyFromObject.apply(void 0, __spreadArray([decodedJwt_1], fieldNameParts, false));
136
+ var _a = _this.lookupJwtField(decodedJwt_1, fieldName), value = _a.value, key = _a.key;
137
137
  if (value) {
138
- var key = fieldNameParts[fieldNameParts.length - 1];
139
- if (key !== undefined) {
140
- matchedFields[key] = value;
141
- }
138
+ matchedFields[key] = value;
142
139
  }
143
140
  return matchedFields;
144
141
  }, {});
@@ -150,6 +147,14 @@ var AccountDefender = /** @class */ (function () {
150
147
  }
151
148
  return null;
152
149
  };
150
+ AccountDefender.prototype.lookupJwtField = function (decodedJwt, fieldName) {
151
+ if (Object.prototype.hasOwnProperty.call(decodedJwt, fieldName)) {
152
+ return { value: decodedJwt[fieldName], key: fieldName };
153
+ }
154
+ var parts = fieldName.split('.');
155
+ var value = utils_1.getPropertyFromObject.apply(void 0, __spreadArray([decodedJwt], parts, false));
156
+ return { value: value, key: fieldName };
157
+ };
153
158
  AccountDefender.prototype.getDecodedJwt = function (jwt, context) {
154
159
  var _a, _b;
155
160
  try {
@@ -54,7 +54,7 @@ var RiskApiClientBase = /** @class */ (function () {
54
54
  */
55
55
  RiskApiClientBase.prototype.executeRiskApi = function (context) {
56
56
  return __awaiter(this, void 0, void 0, function () {
57
- var response, riskResponse, riskApiData, riskRequest, err_1;
57
+ var response, riskResponse, riskApiData, riskRequestStartTime, riskRequest, err_1;
58
58
  return __generator(this, function (_a) {
59
59
  switch (_a.label) {
60
60
  case 0:
@@ -63,7 +63,8 @@ var RiskApiClientBase = /** @class */ (function () {
63
63
  case 1:
64
64
  _a.trys.push([1, 8, , 10]);
65
65
  riskRequest = this.createRiskRequest(context);
66
- return [4 /*yield*/, this.sendRiskActivity(riskRequest, riskApiData, context)];
66
+ riskRequestStartTime = Date.now();
67
+ return [4 /*yield*/, this.sendRiskActivity(riskRequest, riskApiData, context, riskRequestStartTime)];
67
68
  case 2:
68
69
  response = _a.sent();
69
70
  if (!((response === null || response === void 0 ? void 0 : response.status) !== 200)) return [3 /*break*/, 4];
@@ -83,7 +84,7 @@ var RiskApiClientBase = /** @class */ (function () {
83
84
  case 8:
84
85
  err_1 = _a.sent();
85
86
  if ((0, exports.isEnforcerTimeoutError)(err_1)) {
86
- return [2 /*return*/, this.handleS2STimeout(riskApiData)];
87
+ return [2 /*return*/, this.handleS2STimeout(riskApiData, riskRequestStartTime)];
87
88
  }
88
89
  context.logger.error("caught error in risk api: ".concat(err_1, " - ").concat(JSON.stringify(context.requestData.url)));
89
90
  return [4 /*yield*/, this.handleS2SError(riskApiData, context, response, err_1)];
@@ -97,14 +98,14 @@ var RiskApiClientBase = /** @class */ (function () {
97
98
  var headersWithoutSensitive = (0, utils_1.removeSensitiveHeaders)(headers, this.config.sensitiveHeaders);
98
99
  return (0, activities_1.toHeaderEntryArray)(headersWithoutSensitive);
99
100
  };
100
- RiskApiClientBase.prototype.sendRiskActivity = function (riskRequest, riskApiData, context) {
101
- return __awaiter(this, void 0, void 0, function () {
102
- var startTime, response, endTime;
101
+ RiskApiClientBase.prototype.sendRiskActivity = function (riskRequest_1, riskApiData_1, context_1) {
102
+ return __awaiter(this, arguments, void 0, function (riskRequest, riskApiData, context, startTime) {
103
+ var response, endTime;
104
+ if (startTime === void 0) { startTime = Date.now(); }
103
105
  return __generator(this, function (_a) {
104
106
  switch (_a.label) {
105
107
  case 0:
106
108
  context.logger.debug("sending risk api to ".concat(riskRequest.url));
107
- startTime = Date.now();
108
109
  return [4 /*yield*/, this.httpClient.send(riskRequest, { timeoutMs: this.config.s2sTimeout })];
109
110
  case 1:
110
111
  response = _a.sent();
@@ -125,8 +126,11 @@ var RiskApiClientBase = /** @class */ (function () {
125
126
  _a[http_1.AUTHORIZATION_HEADER_NAME] = [(0, utils_1.getAuthorizationHeader)(this.config.authToken)],
126
127
  _a;
127
128
  };
128
- RiskApiClientBase.prototype.handleS2STimeout = function (riskApiData) {
129
+ RiskApiClientBase.prototype.handleS2STimeout = function (riskApiData, startTime) {
129
130
  riskApiData.riskApiCallResult = model_1.RiskApiCallResult.TIMEOUT;
131
+ if (startTime !== undefined) {
132
+ riskApiData.riskRtt = Date.now() - startTime;
133
+ }
130
134
  return riskApiData;
131
135
  };
132
136
  RiskApiClientBase.prototype.handleS2SError = function (riskApiData, context, response, error) {
@@ -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.37.2';
18
+ exports.CORE_MODULE_VERSION = 'JS Core 1.0.0';
@@ -240,7 +240,7 @@ export const addTokenDataToDetails = (details, { token, mobileData }) => {
240
240
  }
241
241
  };
242
242
  export const addRiskApiDataToAsyncActivityCommonDetails = (details, context) => {
243
- if (context.riskApiData.riskRtt) {
243
+ if (context.riskApiData.riskRtt !== undefined) {
244
244
  details.risk_rtt = context.riskApiData.riskRtt;
245
245
  }
246
246
  if (context.riskApiData.s2sCallReason) {
@@ -29,10 +29,11 @@ export class SubtleCryptoCipherUtils {
29
29
  base64ToArrayBuffer(base64String) {
30
30
  const binaryString = this.base64Utils.base64Decode(base64String);
31
31
  const length = binaryString.length;
32
- const bytes = new Uint8Array(length);
32
+ const buffer = new ArrayBuffer(length);
33
+ const bytes = new Uint8Array(buffer);
33
34
  binaryString.split('').forEach((char, index) => {
34
35
  bytes[index] = char.charCodeAt(0);
35
36
  });
36
- return bytes.buffer;
37
+ return buffer;
37
38
  }
38
39
  }
@@ -0,0 +1,41 @@
1
+ import { Agent, request as undiciRequest } from 'undici';
2
+ import { joinHeaderValues } from '../../../http/index.js';
3
+ import { EnforcerTimeoutError } from '../../../utils/index.js';
4
+ import { UndiciIncomingResponse } from './UndiciIncomingResponse.js';
5
+ export class UndiciHttpClient {
6
+ httpDispatcher;
7
+ httpsDispatcher;
8
+ constructor(options) {
9
+ this.httpDispatcher = options?.httpDispatcher ?? null;
10
+ this.httpsDispatcher = options?.httpsDispatcher ?? new Agent();
11
+ }
12
+ async send(request, options) {
13
+ try {
14
+ const response = await undiciRequest(request.url, {
15
+ method: request.method,
16
+ headers: joinHeaderValues(request.headers),
17
+ body: request.body,
18
+ headersTimeout: options?.timeoutMs,
19
+ bodyTimeout: options?.timeoutMs,
20
+ dispatcher: this.getDispatcher(request),
21
+ });
22
+ return new UndiciIncomingResponse({
23
+ ...response,
24
+ body: Buffer.from(await response.body.arrayBuffer()),
25
+ });
26
+ }
27
+ catch (e) {
28
+ const isTimeout = e?.toString()?.toLowerCase()?.includes('timeout') ?? false;
29
+ throw isTimeout ? new EnforcerTimeoutError(options?.timeoutMs) : e;
30
+ }
31
+ }
32
+ getDispatcher(request) {
33
+ if (request.url.startsWith('https://')) {
34
+ return this.httpsDispatcher;
35
+ }
36
+ if (this.httpDispatcher) {
37
+ return this.httpDispatcher;
38
+ }
39
+ return new Agent();
40
+ }
41
+ }
@@ -0,0 +1,9 @@
1
+ import { ProxyAgent } from 'undici';
2
+ import { UndiciHttpClient } from './UndiciHttpClient.js';
3
+ export class UndiciHttpProxyClient extends UndiciHttpClient {
4
+ constructor(proxyUrl) {
5
+ const httpDispatcher = new ProxyAgent(proxyUrl);
6
+ const httpsDispatcher = new ProxyAgent(proxyUrl);
7
+ super({ httpDispatcher, httpsDispatcher });
8
+ }
9
+ }
@@ -1,12 +1,12 @@
1
1
  import { CONTENT_TYPE_HEADER_NAME, MultipartFormDataUtils, } from '../../../http/index.js';
2
2
  import { EnforcerError, isNullOrUndefined } from '../../../utils/index.js';
3
3
  import { DefaultUrlUtils } from '../../url/DefaultUrlUtils.js';
4
- export class PhinIncomingResponse {
4
+ export class UndiciIncomingResponse {
5
5
  status;
6
6
  headers;
7
7
  body;
8
8
  constructor(response) {
9
- this.status = response.statusCode ?? 0;
9
+ this.status = response.statusCode;
10
10
  this.body = response.body;
11
11
  this.headers = Object.fromEntries(Object.entries(response.headers)
12
12
  .map(([key, val]) => [key, typeof val === 'string' ? [val] : val])
@@ -24,7 +24,6 @@ export class PhinIncomingResponse {
24
24
  return formData;
25
25
  }
26
26
  async formUrlEncoded() {
27
- // Using DefaultUrlUtils since Phin safely assumes Node.js environment
28
27
  return new DefaultUrlUtils().createUrlSearchParams(await this.text());
29
28
  }
30
29
  async json() {
@@ -0,0 +1,3 @@
1
+ export * from './UndiciIncomingResponse.js';
2
+ export * from './UndiciHttpClient.js';
3
+ export * from './UndiciHttpProxyClient.js';
@@ -51,15 +51,11 @@ export class AccountDefender {
51
51
  try {
52
52
  const decodedJwt = this.getDecodedJwt(jwt, context);
53
53
  if (decodedJwt) {
54
- const appUserId = getPropertyFromObject(decodedJwt, ...userIdFieldName.split('.'));
54
+ const { value: appUserId } = this.lookupJwtField(decodedJwt, userIdFieldName);
55
55
  const additionalFields = additionalFieldNames.reduce((matchedFields, fieldName) => {
56
- const fieldNameParts = fieldName.split('.');
57
- const value = getPropertyFromObject(decodedJwt, ...fieldNameParts);
56
+ const { value, key } = this.lookupJwtField(decodedJwt, fieldName);
58
57
  if (value) {
59
- const key = fieldNameParts[fieldNameParts.length - 1];
60
- if (key !== undefined) {
61
- matchedFields[key] = value;
62
- }
58
+ matchedFields[key] = value;
63
59
  }
64
60
  return matchedFields;
65
61
  }, {});
@@ -71,6 +67,14 @@ export class AccountDefender {
71
67
  }
72
68
  return null;
73
69
  }
70
+ lookupJwtField(decodedJwt, fieldName) {
71
+ if (Object.prototype.hasOwnProperty.call(decodedJwt, fieldName)) {
72
+ return { value: decodedJwt[fieldName], key: fieldName };
73
+ }
74
+ const parts = fieldName.split('.');
75
+ const value = getPropertyFromObject(decodedJwt, ...parts);
76
+ return { value, key: fieldName };
77
+ }
74
78
  getDecodedJwt(jwt, context) {
75
79
  try {
76
80
  const encodedPayload = jwt.split('.')?.[1];
@@ -19,9 +19,11 @@ export class RiskApiClientBase {
19
19
  let response;
20
20
  let riskResponse;
21
21
  const riskApiData = { riskApiCallResult: RiskApiCallResult.NONE };
22
+ let riskRequestStartTime;
22
23
  try {
23
24
  const riskRequest = this.createRiskRequest(context);
24
- response = await this.sendRiskActivity(riskRequest, riskApiData, context);
25
+ riskRequestStartTime = Date.now();
26
+ response = await this.sendRiskActivity(riskRequest, riskApiData, context, riskRequestStartTime);
25
27
  if (response?.status !== 200) {
26
28
  return await this.handleS2SError(riskApiData, context, response);
27
29
  }
@@ -35,7 +37,7 @@ export class RiskApiClientBase {
35
37
  }
36
38
  catch (err) {
37
39
  if (isEnforcerTimeoutError(err)) {
38
- return this.handleS2STimeout(riskApiData);
40
+ return this.handleS2STimeout(riskApiData, riskRequestStartTime);
39
41
  }
40
42
  context.logger.error(`caught error in risk api: ${err} - ${JSON.stringify(context.requestData.url)}`);
41
43
  return await this.handleS2SError(riskApiData, context, response, err);
@@ -45,9 +47,8 @@ export class RiskApiClientBase {
45
47
  const headersWithoutSensitive = removeSensitiveHeaders(headers, this.config.sensitiveHeaders);
46
48
  return toHeaderEntryArray(headersWithoutSensitive);
47
49
  }
48
- async sendRiskActivity(riskRequest, riskApiData, context) {
50
+ async sendRiskActivity(riskRequest, riskApiData, context, startTime = Date.now()) {
49
51
  context.logger.debug(`sending risk api to ${riskRequest.url}`);
50
- const startTime = Date.now();
51
52
  const response = await this.httpClient.send(riskRequest, { timeoutMs: this.config.s2sTimeout });
52
53
  const endTime = Date.now();
53
54
  riskApiData.riskApiCallResult = RiskApiCallResult.SUCCESSFUL;
@@ -62,8 +63,11 @@ export class RiskApiClientBase {
62
63
  [AUTHORIZATION_HEADER_NAME]: [getAuthorizationHeader(this.config.authToken)],
63
64
  };
64
65
  }
65
- handleS2STimeout(riskApiData) {
66
+ handleS2STimeout(riskApiData, startTime) {
66
67
  riskApiData.riskApiCallResult = RiskApiCallResult.TIMEOUT;
68
+ if (startTime !== undefined) {
69
+ riskApiData.riskRtt = Date.now() - startTime;
70
+ }
67
71
  return riskApiData;
68
72
  }
69
73
  async handleS2SError(riskApiData, context, response, error) {
@@ -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.37.2';
15
+ export const CORE_MODULE_VERSION = 'JS Core 1.0.0';
@@ -0,0 +1,13 @@
1
+ import { Dispatcher } from 'undici';
2
+ import { HttpSendOptions, IHttpClient, IIncomingResponse, IOutgoingRequest } from '../../../http';
3
+ export type UndiciHttpClientOptions = {
4
+ httpDispatcher?: Dispatcher;
5
+ httpsDispatcher?: Dispatcher;
6
+ };
7
+ export declare class UndiciHttpClient implements IHttpClient {
8
+ private readonly httpDispatcher;
9
+ private readonly httpsDispatcher;
10
+ constructor(options?: UndiciHttpClientOptions);
11
+ send(request: IOutgoingRequest, options?: HttpSendOptions): Promise<IIncomingResponse>;
12
+ protected getDispatcher(request: IOutgoingRequest): Dispatcher;
13
+ }
@@ -0,0 +1,4 @@
1
+ import { UndiciHttpClient } from './UndiciHttpClient';
2
+ export declare class UndiciHttpProxyClient extends UndiciHttpClient {
3
+ constructor(proxyUrl: string);
4
+ }
@@ -1,11 +1,16 @@
1
- import { IResponse } from 'phin';
1
+ import { IncomingHttpHeaders } from 'undici/types/header';
2
2
  import { IFormData, IIncomingResponse, ReadonlyHeaders } from '../../../http';
3
3
  import { IUrlSearchParams } from '../../../utils';
4
- export declare class PhinIncomingResponse implements IIncomingResponse {
4
+ export type UndiciIncomingResponseData = {
5
+ statusCode: number;
6
+ headers: IncomingHttpHeaders;
7
+ body: Buffer;
8
+ };
9
+ export declare class UndiciIncomingResponse implements IIncomingResponse {
5
10
  readonly status: number;
6
11
  readonly headers: ReadonlyHeaders;
7
12
  readonly body: Buffer;
8
- constructor(response: IResponse);
13
+ constructor(response: UndiciIncomingResponseData);
9
14
  formData(): Promise<IFormData>;
10
15
  formUrlEncoded(): Promise<IUrlSearchParams>;
11
16
  json(): Promise<any>;
@@ -0,0 +1,3 @@
1
+ export * from './UndiciIncomingResponse';
2
+ export * from './UndiciHttpClient';
3
+ export * from './UndiciHttpProxyClient';
@@ -18,5 +18,9 @@ export declare class AccountDefender<Req, Res, Supported extends string, Added>
18
18
  modifyOutgoingResponse(_context: ReadonlyContext<Req, Res>): Promise<void>;
19
19
  protected getJwtData(context: ReadonlyContext<Req, Res>): JwtData | null;
20
20
  protected extractJwtData(jwt: string, userIdFieldName: string, additionalFieldNames: string[], context: ReadonlyContext<Req, Res>): JwtData | null;
21
+ protected lookupJwtField(decodedJwt: any, fieldName: string): {
22
+ value: any;
23
+ key: string;
24
+ };
21
25
  protected getDecodedJwt(jwt: string, context: ReadonlyContext<Req, Res>): any | null;
22
26
  }
@@ -17,10 +17,10 @@ export declare abstract class RiskApiClientBase<Req, Res, Supported extends stri
17
17
  */
18
18
  executeRiskApi(context: ReadonlyContext<Req, Res>): Promise<RiskApiData>;
19
19
  protected formatRiskHeadersField(headers: ReadonlyHeaders): HeaderEntry[];
20
- protected sendRiskActivity(riskRequest: OutgoingRequestImpl, riskApiData: RiskApiData, context: ReadonlyContext<Req, Res>): Promise<IIncomingResponse>;
20
+ protected sendRiskActivity(riskRequest: OutgoingRequestImpl, riskApiData: RiskApiData, context: ReadonlyContext<Req, Res>, startTime?: number): Promise<IIncomingResponse>;
21
21
  protected getRiskUrl(): string;
22
22
  protected getRiskHeaders(): ReadonlyHeaders;
23
- protected handleS2STimeout(riskApiData: RiskApiData): RiskApiData;
23
+ protected handleS2STimeout(riskApiData: RiskApiData, startTime?: number): RiskApiData;
24
24
  protected handleS2SError(riskApiData: RiskApiData, context: ReadonlyContext<Req, Res>, response?: IIncomingResponse, error?: unknown): Promise<RiskApiData>;
25
25
  }
26
26
  export declare const isEnforcerTimeoutError: (err: unknown) => boolean;
@@ -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.37.2";
15
+ export declare const CORE_MODULE_VERSION = "JS Core 1.0.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perimeterx-js-core",
3
- "version": "0.37.2",
3
+ "version": "1.0.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "typesVersions": {
@@ -44,25 +44,23 @@
44
44
  },
45
45
  "dependencies": {
46
46
  "crypto-js": "^4.1.1",
47
- "http-proxy-agent": "^7.0.2",
48
- "https-proxy-agent": "^7.0.6",
49
47
  "ip-range-check": "^0.2.0",
50
48
  "js-base64": "^3.7.2",
51
- "phin": "^3.7.0",
52
49
  "ts-essentials": "^10.0.0",
53
- "uuid": "^11.1.0"
50
+ "undici": "^6.26.0",
51
+ "uuid": "^11.1.1"
54
52
  },
55
53
  "devDependencies": {
56
54
  "@types/chai": "^5.2.1",
57
55
  "@types/chai-as-promised": "^8.0.2",
58
56
  "@types/crypto-js": "^4.1.1",
59
- "@types/mocha": "^10.0.0",
57
+ "@types/mocha": "^10.0.10",
60
58
  "@types/node": "^18.19.50 || ^20.6.0 || ^22.0.0",
61
- "@types/sinon": "^17.0.1",
59
+ "@types/sinon": "^21.0.1",
62
60
  "@types/uuid": "^10.0.0",
63
- "@typescript-eslint/eslint-plugin": "^8.38.0",
64
- "@typescript-eslint/parser": "^8.26.0",
65
- "c8": "^10.1.3",
61
+ "@typescript-eslint/eslint-plugin": "^8.61.0",
62
+ "@typescript-eslint/parser": "^8.61.0",
63
+ "c8": "^11.0.0",
66
64
  "chai": "^6.2.0",
67
65
  "chai-as-promised": "^8.0.0",
68
66
  "core-js": "^3.19.1",
@@ -71,10 +69,10 @@
71
69
  "eslint-plugin-prettier": "^5.2.3",
72
70
  "husky": "^9.1.7",
73
71
  "lint-staged": "^16.0.0",
74
- "mocha": "^11.4.0",
72
+ "mocha": "^11.7.6",
75
73
  "prettier": "^3.5.3",
76
- "sinon": "^21.0.0",
77
- "ts-loader": "^9.4.1",
74
+ "sinon": "^22.0.0",
75
+ "ts-loader": "^9.6.0",
78
76
  "ts-node": "^10.9.1",
79
77
  "tsc-alias": "^1.8.11",
80
78
  "typescript": "^5.1.6"
@@ -1,42 +0,0 @@
1
- import { Agent as HttpAgent } from 'http';
2
- import { Agent as HttpsAgent } from 'https';
3
- import phin from 'phin';
4
- import { EnforcerTimeoutError } from '../../../utils/index.js';
5
- import { joinHeaderValues } from '../../../http/index.js';
6
- import { PhinIncomingResponse } from './PhinIncomingResponse.js';
7
- export class PhinHttpClient {
8
- httpAgent;
9
- httpsAgent;
10
- constructor(options) {
11
- this.httpAgent = options?.httpAgent ?? null;
12
- this.httpsAgent = options?.httpsAgent ?? new HttpsAgent({ keepAlive: true });
13
- }
14
- async send(request, options) {
15
- try {
16
- const res = await phin.promisified({
17
- url: request.url,
18
- method: request.method,
19
- headers: joinHeaderValues(request.headers),
20
- data: request.body,
21
- timeout: options?.timeoutMs,
22
- core: {
23
- agent: this.getAgent(request),
24
- },
25
- });
26
- return new PhinIncomingResponse(res);
27
- }
28
- catch (e) {
29
- const isTimeout = e?.toString().toLowerCase().includes('timeout');
30
- throw isTimeout ? new EnforcerTimeoutError(options?.timeoutMs) : e;
31
- }
32
- }
33
- getAgent(request) {
34
- if (request.url.startsWith('https://')) {
35
- return this.httpsAgent;
36
- }
37
- if (this.httpAgent) {
38
- return this.httpAgent;
39
- }
40
- return new HttpAgent();
41
- }
42
- }
@@ -1,10 +0,0 @@
1
- import { PhinHttpClient } from './PhinHttpClient.js';
2
- import { HttpProxyAgent } from 'http-proxy-agent';
3
- import { HttpsProxyAgent } from 'https-proxy-agent';
4
- export class PhinHttpProxyClient extends PhinHttpClient {
5
- constructor(proxyUrl) {
6
- const httpAgent = new HttpProxyAgent(proxyUrl, { keepAlive: true });
7
- const httpsAgent = new HttpsProxyAgent(proxyUrl, { keepAlive: true });
8
- super({ httpsAgent, httpAgent });
9
- }
10
- }
@@ -1,3 +0,0 @@
1
- export * from './PhinIncomingResponse.js';
2
- export * from './PhinHttpClient.js';
3
- export * from './PhinHttpProxyClient.js';
@@ -1,14 +0,0 @@
1
- import { Agent as HttpAgent } from 'http';
2
- import { Agent as HttpsAgent } from 'https';
3
- import { HttpSendOptions, IHttpClient, IIncomingResponse, IOutgoingRequest } from '../../../http';
4
- export type PhinHttpClientOptions = {
5
- httpAgent?: HttpAgent;
6
- httpsAgent?: HttpsAgent;
7
- };
8
- export declare class PhinHttpClient implements IHttpClient {
9
- private readonly httpAgent;
10
- private readonly httpsAgent;
11
- constructor(options?: PhinHttpClientOptions);
12
- send(request: IOutgoingRequest, options?: HttpSendOptions): Promise<IIncomingResponse>;
13
- protected getAgent(request: IOutgoingRequest): HttpAgent;
14
- }
@@ -1,4 +0,0 @@
1
- import { PhinHttpClient } from './PhinHttpClient';
2
- export declare class PhinHttpProxyClient extends PhinHttpClient {
3
- constructor(proxyUrl: string);
4
- }
@@ -1,3 +0,0 @@
1
- export * from './PhinIncomingResponse';
2
- export * from './PhinHttpClient';
3
- export * from './PhinHttpProxyClient';