perimeterx-js-core 0.23.2 → 0.24.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.
- package/lib/cjs/activities/utils.js +4 -3
- package/lib/cjs/config/ConfigurationBase.js +14 -0
- package/lib/cjs/config/defaults/DefaultConfigurationParams.js +2 -0
- package/lib/cjs/enforcer/utils.js +6 -1
- package/lib/cjs/graphql/DefaultGraphQLParser.js +1 -1
- package/lib/cjs/index.js +1 -0
- package/lib/cjs/monitored_request/MonitoredRequestUtils.js +2 -2
- package/lib/cjs/phase/flow/PostEnforceFlow.js +2 -2
- package/lib/cjs/phase/impl/ModifyOutgoingResponsePhase.js +32 -1
- package/lib/cjs/products/bot_defender/filter/DefaultBotDefenderFilter.js +2 -2
- package/lib/cjs/sensitive_request/SensitiveRequestUtils.js +1 -1
- package/lib/cjs/snippet_injection/CustomSnippetFunction.js +2 -0
- package/lib/cjs/snippet_injection/index.js +20 -0
- package/lib/cjs/snippet_injection/snippet_injector/ISnippetInjector.js +2 -0
- package/lib/cjs/snippet_injection/snippet_injector/index.js +17 -0
- package/lib/cjs/snippet_injection/snippet_retriever/DefaultSnippetRetriever.js +95 -0
- package/lib/cjs/snippet_injection/snippet_retriever/ISnippetRetriever.js +2 -0
- package/lib/cjs/snippet_injection/snippet_retriever/index.js +18 -0
- package/lib/cjs/snippet_injection/utils.js +5 -0
- package/lib/cjs/utils/constants.js +2 -2
- package/lib/cjs/utils/utils.js +30 -14
- package/lib/esm/activities/utils.js +4 -3
- package/lib/esm/config/ConfigurationBase.js +6 -0
- package/lib/esm/config/defaults/DefaultConfigurationParams.js +2 -0
- package/lib/esm/enforcer/utils.js +5 -0
- package/lib/esm/graphql/DefaultGraphQLParser.js +2 -2
- package/lib/esm/index.js +1 -0
- package/lib/esm/monitored_request/MonitoredRequestUtils.js +3 -3
- package/lib/esm/phase/flow/PostEnforceFlow.js +2 -2
- package/lib/esm/phase/impl/ModifyOutgoingResponsePhase.js +17 -1
- package/lib/esm/products/bot_defender/filter/DefaultBotDefenderFilter.js +3 -3
- package/lib/esm/sensitive_request/SensitiveRequestUtils.js +2 -2
- package/lib/esm/snippet_injection/CustomSnippetFunction.js +1 -0
- package/lib/esm/snippet_injection/index.js +4 -0
- package/lib/esm/snippet_injection/snippet_injector/ISnippetInjector.js +1 -0
- package/lib/esm/snippet_injection/snippet_injector/index.js +1 -0
- package/lib/esm/snippet_injection/snippet_retriever/DefaultSnippetRetriever.js +36 -0
- package/lib/esm/snippet_injection/snippet_retriever/ISnippetRetriever.js +1 -0
- package/lib/esm/snippet_injection/snippet_retriever/index.js +2 -0
- package/lib/esm/snippet_injection/utils.js +9 -0
- package/lib/esm/utils/constants.js +2 -2
- package/lib/esm/utils/utils.js +27 -11
- package/lib/types/activities/model/CommonActivityDetails.d.ts +1 -0
- package/lib/types/config/ConfigurationBase.d.ts +4 -1
- package/lib/types/config/IConfiguration.d.ts +10 -1
- package/lib/types/config/params/CoreConfigurationParams.d.ts +7 -2
- package/lib/types/enforcer/EnforcerOptions.d.ts +3 -0
- package/lib/types/index.d.ts +1 -0
- package/lib/types/phase/flow/PostEnforceFlow.d.ts +1 -1
- package/lib/types/phase/impl/ModifyOutgoingResponsePhase.d.ts +5 -1
- package/lib/types/snippet_injection/CustomSnippetFunction.d.ts +2 -0
- package/lib/types/snippet_injection/index.d.ts +4 -0
- package/lib/types/snippet_injection/snippet_injector/ISnippetInjector.d.ts +5 -0
- package/lib/types/snippet_injection/snippet_injector/index.d.ts +1 -0
- package/lib/types/snippet_injection/snippet_retriever/DefaultSnippetRetriever.d.ts +10 -0
- package/lib/types/snippet_injection/snippet_retriever/ISnippetRetriever.d.ts +5 -0
- package/lib/types/snippet_injection/snippet_retriever/index.d.ts +2 -0
- package/lib/types/snippet_injection/utils.d.ts +1 -0
- package/lib/types/utils/constants.d.ts +1 -1
- package/lib/types/utils/utils.d.ts +2 -2
- package/package.json +1 -1
|
@@ -86,9 +86,10 @@ function redactCookieSecret(secret) {
|
|
|
86
86
|
return '***'.concat(secret.substring(secret.length - 3, secret.length));
|
|
87
87
|
}
|
|
88
88
|
var addConfigDataToDetails = function (details, config) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
(0, utils_1.transferExistingProperties)(config, details, {
|
|
90
|
+
remoteConfigId: 'remote_config_id',
|
|
91
|
+
remoteConfigVersion: 'remote_config_version',
|
|
92
|
+
});
|
|
92
93
|
};
|
|
93
94
|
exports.addConfigDataToDetails = addConfigDataToDetails;
|
|
94
95
|
var addCustomParametersToDetails = function (details, customParameters) {
|
|
@@ -717,6 +717,20 @@ var ConfigurationBase = /** @class */ (function () {
|
|
|
717
717
|
enumerable: false,
|
|
718
718
|
configurable: true
|
|
719
719
|
});
|
|
720
|
+
Object.defineProperty(ConfigurationBase.prototype, "snippetInjectionEnabled", {
|
|
721
|
+
get: function () {
|
|
722
|
+
return this.configParams.px_snippet_injection_enabled;
|
|
723
|
+
},
|
|
724
|
+
enumerable: false,
|
|
725
|
+
configurable: true
|
|
726
|
+
});
|
|
727
|
+
Object.defineProperty(ConfigurationBase.prototype, "createCustomSnippet", {
|
|
728
|
+
get: function () {
|
|
729
|
+
return this.configParams.px_create_custom_snippet;
|
|
730
|
+
},
|
|
731
|
+
enumerable: false,
|
|
732
|
+
configurable: true
|
|
733
|
+
});
|
|
720
734
|
Object.defineProperty(ConfigurationBase.prototype, "enableBlockedUrlOnCaptchaBlockPage", {
|
|
721
735
|
get: function () {
|
|
722
736
|
return true;
|
|
@@ -130,6 +130,8 @@ var defaultConfigurationParams = function () { return ({
|
|
|
130
130
|
px_remote_config_retry_interval_ms: 1000,
|
|
131
131
|
px_url_decode_reserved_characters: false,
|
|
132
132
|
px_secured_pxhd_enabled: false,
|
|
133
|
+
px_snippet_injection_enabled: false,
|
|
134
|
+
px_create_custom_snippet: null,
|
|
133
135
|
px_custom_is_sensitive_request: null,
|
|
134
136
|
px_custom_is_monitored_request: null,
|
|
135
137
|
px_custom_is_enforced_request: null,
|
|
@@ -23,8 +23,9 @@ var activities_1 = require("../activities");
|
|
|
23
23
|
var logger_1 = require("../logger");
|
|
24
24
|
var products_1 = require("../products");
|
|
25
25
|
var utils_1 = require("../utils");
|
|
26
|
+
var snippet_injection_1 = require("../snippet_injection");
|
|
26
27
|
var createEnforcerInitializationBlock = function (config, options) {
|
|
27
|
-
var _a, _b;
|
|
28
|
+
var _a, _b, _c;
|
|
28
29
|
var tokenVersion = config.tokenVersion;
|
|
29
30
|
if (!(0, utils_1.isValidTokenVersion)(tokenVersion)) {
|
|
30
31
|
throw new utils_1.EnforcerError("error initializing enforcer: token version ".concat(tokenVersion, " is invalid (must be one of ").concat(Object.values(risk_token_1.TokenVersion)
|
|
@@ -65,6 +66,8 @@ var createEnforcerInitializationBlock = function (config, options) {
|
|
|
65
66
|
hmacUtils: hmacUtils,
|
|
66
67
|
})
|
|
67
68
|
: null);
|
|
69
|
+
var snippetInjector = (_c = options.snippetInjector) !== null && _c !== void 0 ? _c : null;
|
|
70
|
+
var snippetRetriever = options.snippetRetriever || (snippetInjector ? new snippet_injection_1.DefaultSnippetRetriever(config) : null);
|
|
68
71
|
var allOptions = {
|
|
69
72
|
httpClient: httpClient,
|
|
70
73
|
base64Utils: base64Utils,
|
|
@@ -84,6 +87,8 @@ var createEnforcerInitializationBlock = function (config, options) {
|
|
|
84
87
|
remoteConfigStorageClient: remoteConfigStorageClient,
|
|
85
88
|
remoteConfigServiceClient: remoteConfigServiceClient,
|
|
86
89
|
remoteConfigUpdater: remoteConfigUpdater,
|
|
90
|
+
snippetInjector: snippetInjector,
|
|
91
|
+
snippetRetriever: snippetRetriever,
|
|
87
92
|
};
|
|
88
93
|
var products = (0, exports.createEnforcerProducts)(config, options.products, base64Utils, hashUtils, urlUtils, ipRangeChecker);
|
|
89
94
|
return __assign({ products: products }, allOptions);
|
|
@@ -49,7 +49,7 @@ var DefaultGraphQLParser = /** @class */ (function () {
|
|
|
49
49
|
DefaultGraphQLParser.prototype.isGraphQLRequest = function (_a) {
|
|
50
50
|
var requestData = _a.requestData;
|
|
51
51
|
return (requestData.method === http_1.HttpMethod.POST &&
|
|
52
|
-
(0, utils_1.
|
|
52
|
+
(0, utils_1.isStringInPatterns)(requestData.url.pathname, this.config.graphqlRoutes));
|
|
53
53
|
};
|
|
54
54
|
DefaultGraphQLParser.prototype.parseGraphQLRequest = function (context) {
|
|
55
55
|
return __awaiter(this, void 0, void 0, function () {
|
package/lib/cjs/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./context"), exports);
|
|
|
23
23
|
__exportStar(require("./custom_parameters"), exports);
|
|
24
24
|
__exportStar(require("./sensitive_request"), exports);
|
|
25
25
|
__exportStar(require("./monitored_request"), exports);
|
|
26
|
+
__exportStar(require("./snippet_injection"), exports);
|
|
26
27
|
__exportStar(require("./cors"), exports);
|
|
27
28
|
__exportStar(require("./enforcer"), exports);
|
|
28
29
|
__exportStar(require("./filter"), exports);
|
|
@@ -74,10 +74,10 @@ var MonitoredRequestUtils;
|
|
|
74
74
|
});
|
|
75
75
|
}); };
|
|
76
76
|
MonitoredRequestUtils.isMonitoredRoute = function (url, isMonitoredRoute) {
|
|
77
|
-
return (0, utils_1.
|
|
77
|
+
return (0, utils_1.isStringInPatterns)(url.pathname, isMonitoredRoute);
|
|
78
78
|
};
|
|
79
79
|
MonitoredRequestUtils.isEnforcedRoute = function (url, enforcedRoutes) {
|
|
80
|
-
return (0, utils_1.
|
|
80
|
+
return (0, utils_1.isStringInPatterns)(url.pathname, enforcedRoutes);
|
|
81
81
|
};
|
|
82
82
|
MonitoredRequestUtils.invokeCustomIsEnforcedRequest = function (config, context) { return __awaiter(_this, void 0, void 0, function () {
|
|
83
83
|
var err_1;
|
|
@@ -20,10 +20,10 @@ var impl_1 = require("../impl");
|
|
|
20
20
|
var PostEnforceFlow = /** @class */ (function (_super) {
|
|
21
21
|
__extends(PostEnforceFlow, _super);
|
|
22
22
|
function PostEnforceFlow(config, _a) {
|
|
23
|
-
var products = _a.products, activityClient = _a.activityClient;
|
|
23
|
+
var products = _a.products, activityClient = _a.activityClient, snippetRetriever = _a.snippetRetriever, snippetInjector = _a.snippetInjector;
|
|
24
24
|
return _super.call(this, [
|
|
25
25
|
new impl_1.EnrichContextFromResponsePhase(config, products),
|
|
26
|
-
new impl_1.ModifyOutgoingResponsePhase(config, Object.values(products)),
|
|
26
|
+
new impl_1.ModifyOutgoingResponsePhase(config, Object.values(products), snippetRetriever, snippetInjector),
|
|
27
27
|
new impl_1.SendAsyncActivitiesOnResponsePhase(activityClient),
|
|
28
28
|
]) || this;
|
|
29
29
|
}
|
|
@@ -38,10 +38,13 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.ModifyOutgoingResponsePhase = void 0;
|
|
40
40
|
var pxhd_1 = require("../../pxhd");
|
|
41
|
+
var http_1 = require("../../http");
|
|
41
42
|
var ModifyOutgoingResponsePhase = /** @class */ (function () {
|
|
42
|
-
function ModifyOutgoingResponsePhase(config, products) {
|
|
43
|
+
function ModifyOutgoingResponsePhase(config, products, snippetRetriever, snippetInjector) {
|
|
43
44
|
this.config = config;
|
|
44
45
|
this.products = products;
|
|
46
|
+
this.snippetRetriever = snippetRetriever;
|
|
47
|
+
this.snippetInjector = snippetInjector;
|
|
45
48
|
}
|
|
46
49
|
ModifyOutgoingResponsePhase.prototype.execute = function (context) {
|
|
47
50
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -54,11 +57,39 @@ var ModifyOutgoingResponsePhase = /** @class */ (function () {
|
|
|
54
57
|
if (((_a = context.pxhd) === null || _a === void 0 ? void 0 : _a.source) === pxhd_1.PXHDSource.RISK && context.response) {
|
|
55
58
|
pxhd_1.PXHDUtils.addPxhdToOutgoingResponse(this.config, context, context.response);
|
|
56
59
|
}
|
|
60
|
+
return [4 /*yield*/, this.handleSnippetInjection(context)];
|
|
61
|
+
case 2:
|
|
62
|
+
_b.sent();
|
|
57
63
|
return [2 /*return*/, { done: false }];
|
|
58
64
|
}
|
|
59
65
|
});
|
|
60
66
|
});
|
|
61
67
|
};
|
|
68
|
+
ModifyOutgoingResponsePhase.prototype.handleSnippetInjection = function (context) {
|
|
69
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
70
|
+
var snippet, _a;
|
|
71
|
+
var _b, _c, _d;
|
|
72
|
+
return __generator(this, function (_e) {
|
|
73
|
+
switch (_e.label) {
|
|
74
|
+
case 0:
|
|
75
|
+
if (!(this.snippetRetriever &&
|
|
76
|
+
this.snippetInjector &&
|
|
77
|
+
this.config.snippetInjectionEnabled &&
|
|
78
|
+
!!((_d = (_c = (_b = context.response) === null || _b === void 0 ? void 0 : _b.headers) === null || _c === void 0 ? void 0 : _c.get(http_1.CONTENT_TYPE_HEADER_NAME)) === null || _d === void 0 ? void 0 : _d.includes(http_1.ContentType.TEXT_HTML)))) return [3 /*break*/, 3];
|
|
79
|
+
context.logger.debug('snippet injection enabled');
|
|
80
|
+
return [4 /*yield*/, this.snippetRetriever.retrieveSnippet(context)];
|
|
81
|
+
case 1:
|
|
82
|
+
snippet = _e.sent();
|
|
83
|
+
_a = context;
|
|
84
|
+
return [4 /*yield*/, this.snippetInjector.injectSnippet(context.response, snippet)];
|
|
85
|
+
case 2:
|
|
86
|
+
_a.response = _e.sent();
|
|
87
|
+
_e.label = 3;
|
|
88
|
+
case 3: return [2 /*return*/];
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
};
|
|
62
93
|
return ModifyOutgoingResponsePhase;
|
|
63
94
|
}());
|
|
64
95
|
exports.ModifyOutgoingResponsePhase = ModifyOutgoingResponsePhase;
|
|
@@ -109,10 +109,10 @@ var DefaultBotDefenderFilter = /** @class */ (function () {
|
|
|
109
109
|
return this.config.filteredIps.some(function (range) { return _this.ipRangeChecker.isIpInRange(ip, range); });
|
|
110
110
|
};
|
|
111
111
|
DefaultBotDefenderFilter.prototype.shouldFilterByRoute = function (route) {
|
|
112
|
-
return (0, utils_1.
|
|
112
|
+
return (0, utils_1.isStringInPatterns)(route, this.config.filteredRoutes);
|
|
113
113
|
};
|
|
114
114
|
DefaultBotDefenderFilter.prototype.shouldFilterByUserAgent = function (userAgent) {
|
|
115
|
-
return this.config.filteredUserAgents
|
|
115
|
+
return (0, utils_1.isStringInPatterns)(userAgent, this.config.filteredUserAgents);
|
|
116
116
|
};
|
|
117
117
|
DefaultBotDefenderFilter.prototype.shouldFilterByCustomFunction = function (context) {
|
|
118
118
|
return __awaiter(this, void 0, void 0, function () {
|
|
@@ -58,7 +58,7 @@ var SensitiveRequestUtils;
|
|
|
58
58
|
});
|
|
59
59
|
}); };
|
|
60
60
|
SensitiveRequestUtils.isSensitiveRoute = function (url, sensitiveRoutes) {
|
|
61
|
-
return (0, utils_1.
|
|
61
|
+
return (0, utils_1.isStringInPatterns)(url.pathname, sensitiveRoutes);
|
|
62
62
|
};
|
|
63
63
|
SensitiveRequestUtils.isSensitiveGraphqlOperation = function (graphQLData) {
|
|
64
64
|
return !!(graphQLData === null || graphQLData === void 0 ? void 0 : graphQLData.some(function (operation) { return operation.sensitive; }));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./snippet_injector"), exports);
|
|
18
|
+
__exportStar(require("./snippet_retriever"), exports);
|
|
19
|
+
__exportStar(require("./utils"), exports);
|
|
20
|
+
__exportStar(require("./CustomSnippetFunction"), exports);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ISnippetInjector"), exports);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
12
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
13
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
14
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
15
|
+
function step(op) {
|
|
16
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
17
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
18
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
19
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
20
|
+
switch (op[0]) {
|
|
21
|
+
case 0: case 1: t = op; break;
|
|
22
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
23
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
24
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
25
|
+
default:
|
|
26
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
27
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
28
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
29
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
30
|
+
if (t[2]) _.ops.pop();
|
|
31
|
+
_.trys.pop(); continue;
|
|
32
|
+
}
|
|
33
|
+
op = body.call(thisArg, _);
|
|
34
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
35
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.DefaultSnippetRetriever = void 0;
|
|
40
|
+
var products_1 = require("../../products");
|
|
41
|
+
var utils_1 = require("../utils");
|
|
42
|
+
var DefaultSnippetRetriever = /** @class */ (function () {
|
|
43
|
+
function DefaultSnippetRetriever(config) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
}
|
|
46
|
+
DefaultSnippetRetriever.prototype.retrieveSnippet = function (context) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
48
|
+
return __generator(this, function (_a) {
|
|
49
|
+
switch (_a.label) {
|
|
50
|
+
case 0: return [4 /*yield*/, this.retrieveCustomSnippet(context)];
|
|
51
|
+
case 1: return [2 /*return*/, (_a.sent()) || this.retrieveDefaultSnippet(context)];
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
DefaultSnippetRetriever.prototype.retrieveCustomSnippet = function (context) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
58
|
+
var snippet, e_1;
|
|
59
|
+
return __generator(this, function (_a) {
|
|
60
|
+
switch (_a.label) {
|
|
61
|
+
case 0:
|
|
62
|
+
if (!this.config.createCustomSnippet) {
|
|
63
|
+
return [2 /*return*/, null];
|
|
64
|
+
}
|
|
65
|
+
_a.label = 1;
|
|
66
|
+
case 1:
|
|
67
|
+
_a.trys.push([1, 3, , 4]);
|
|
68
|
+
return [4 /*yield*/, this.config.createCustomSnippet(this.config.getActiveConfig(), context.requestData.request.getUnderlyingRequest(), context.response.getUnderlyingResponse())];
|
|
69
|
+
case 2:
|
|
70
|
+
snippet = _a.sent();
|
|
71
|
+
if (!snippet || typeof snippet !== 'string') {
|
|
72
|
+
context.logger.debug("invalid custom snippet of type ".concat(typeof snippet));
|
|
73
|
+
return [2 /*return*/, null];
|
|
74
|
+
}
|
|
75
|
+
context.logger.debug('retrieving custom snippet');
|
|
76
|
+
return [2 /*return*/, snippet];
|
|
77
|
+
case 3:
|
|
78
|
+
e_1 = _a.sent();
|
|
79
|
+
context.logger.debug("failed retrieving custom snippet: ".concat(e_1));
|
|
80
|
+
return [3 /*break*/, 4];
|
|
81
|
+
case 4: return [2 /*return*/, null];
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
DefaultSnippetRetriever.prototype.retrieveDefaultSnippet = function (context) {
|
|
87
|
+
var sensorSrc = this.config.firstPartyEnabled
|
|
88
|
+
? (0, products_1.getMostCustomizedFirstPartyPath)(this.config, products_1.FirstPartySuffix.SENSOR)
|
|
89
|
+
: "".concat(this.config.backendClientUrl, "/").concat(this.config.appId, "/main.min.js");
|
|
90
|
+
context.logger.debug("retrieving default snippet with src ".concat(sensorSrc));
|
|
91
|
+
return (0, utils_1.createDefaultSnippet)(this.config.appId, sensorSrc);
|
|
92
|
+
};
|
|
93
|
+
return DefaultSnippetRetriever;
|
|
94
|
+
}());
|
|
95
|
+
exports.DefaultSnippetRetriever = DefaultSnippetRetriever;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./ISnippetRetriever"), exports);
|
|
18
|
+
__exportStar(require("./DefaultSnippetRetriever"), exports);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createDefaultSnippet = void 0;
|
|
4
|
+
var createDefaultSnippet = function (appId, sensorSrc) { return "<script type=\"text/javascript\">\n (function(){\n window._pxAppId = '".concat(appId, "';\n var p = document.getElementsByTagName('script')[0], s = document.createElement('script');\n s.async = 1;\n s.src = '").concat(sensorSrc, "';\n p.parentNode.insertBefore(s,p);\n }());\n</script>"); };
|
|
5
|
+
exports.createDefaultSnippet = createDefaultSnippet;
|
|
@@ -12,6 +12,6 @@ exports.X_PX_BYPASS_REASON_HEADER_NAME = 'x-px-bypass-reason';
|
|
|
12
12
|
exports.PUSH_DATA_HMAC_HEADER_NAME = 'x-px-pushdata';
|
|
13
13
|
exports.PUSH_DATA_FEATURE_HEADER_NAME = 'x-px-feature';
|
|
14
14
|
exports.EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
|
|
15
|
-
exports.URL_REGEX = /^(https
|
|
15
|
+
exports.URL_REGEX = /^(https?:)\/\/(([^@\s:\/]+):?([^@\s\/]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/;
|
|
16
16
|
exports.REGEX_STRUCTURE = /^\/(.+?)\/([gimsuyvd]*)$/;
|
|
17
|
-
exports.CORE_MODULE_VERSION = 'JS Core 0.
|
|
17
|
+
exports.CORE_MODULE_VERSION = 'JS Core 0.24.0';
|
package/lib/cjs/utils/utils.js
CHANGED
|
@@ -36,7 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
36
36
|
}
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.isNullOrUndefined = exports.isValidTokenVersion = exports.telemetryConfigReplacer = exports.convertRegexStringToRegex = exports.algoToCryptoString = exports.algoToSubtleCryptoString = exports.sleep = exports.getPropertyFromObject = exports.rejectOnTimeout = exports.transferExistingProperties = exports.
|
|
39
|
+
exports.isNullOrUndefined = exports.isValidTokenVersion = exports.telemetryConfigReplacer = exports.convertRegexStringToRegex = exports.algoToCryptoString = exports.algoToSubtleCryptoString = exports.sleep = exports.getPropertyFromObject = exports.rejectOnTimeout = exports.transferExistingProperties = exports.isStringMatch = exports.isStringInPatterns = exports.removeSensitiveHeaders = exports.redactSensitiveFields = exports.getExtension = exports.getAuthorizationHeader = exports.getCollectorDomain = exports.getScoreApiDomain = exports.isEmailAddress = exports.isValidUuid = exports.isValidEnumValue = void 0;
|
|
40
40
|
var http_1 = require("../http");
|
|
41
41
|
var error_1 = require("./error");
|
|
42
42
|
var constants_1 = require("./constants");
|
|
@@ -80,21 +80,37 @@ var getExtension = function (route) {
|
|
|
80
80
|
};
|
|
81
81
|
exports.getExtension = getExtension;
|
|
82
82
|
var redactSensitiveFields = function (object, sensitiveFields) {
|
|
83
|
-
var NUMBER_OF_TRAILING_CHARS_TO_EXPOSE = 5;
|
|
84
|
-
var SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS = NUMBER_OF_TRAILING_CHARS_TO_EXPOSE * 10;
|
|
85
83
|
var newObj = Object.assign({}, object);
|
|
86
84
|
sensitiveFields.forEach(function (fieldName) {
|
|
87
85
|
var sensitiveValue = object[fieldName];
|
|
88
86
|
if (!sensitiveValue) {
|
|
89
87
|
return;
|
|
90
88
|
}
|
|
91
|
-
|
|
92
|
-
var trailingChars = sensitiveValue.substring(sensitiveValue.length - (trailingCharsExposed ? NUMBER_OF_TRAILING_CHARS_TO_EXPOSE : 0));
|
|
93
|
-
newObj[fieldName] = "***REDACTED***".concat(trailingChars);
|
|
89
|
+
newObj[fieldName] = redactUnknownValue(sensitiveValue);
|
|
94
90
|
});
|
|
95
91
|
return newObj;
|
|
96
92
|
};
|
|
97
93
|
exports.redactSensitiveFields = redactSensitiveFields;
|
|
94
|
+
var redactUnknownValue = function (value) {
|
|
95
|
+
switch (typeof value) {
|
|
96
|
+
case 'string':
|
|
97
|
+
return redactSensitiveString(value);
|
|
98
|
+
case 'object':
|
|
99
|
+
if (Array.isArray(value)) {
|
|
100
|
+
return value.flatMap(redactUnknownValue).filter(function (v) { return !(0, exports.isNullOrUndefined)(v); });
|
|
101
|
+
}
|
|
102
|
+
return null;
|
|
103
|
+
default:
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var redactSensitiveString = function (sensitiveValue) {
|
|
108
|
+
var NUMBER_OF_TRAILING_CHARS_TO_EXPOSE = 5;
|
|
109
|
+
var SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS = NUMBER_OF_TRAILING_CHARS_TO_EXPOSE * 10;
|
|
110
|
+
var trailingCharsExposed = sensitiveValue.length >= SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS;
|
|
111
|
+
var trailingChars = sensitiveValue.substring(sensitiveValue.length - (trailingCharsExposed ? NUMBER_OF_TRAILING_CHARS_TO_EXPOSE : 0));
|
|
112
|
+
return "***REDACTED***".concat(trailingChars);
|
|
113
|
+
};
|
|
98
114
|
var removeSensitiveHeaders = function (headers, sensitiveHeaderNames) {
|
|
99
115
|
var ret = (0, http_1.toMutableHeaders)(headers);
|
|
100
116
|
sensitiveHeaderNames.forEach(function (name) {
|
|
@@ -103,23 +119,23 @@ var removeSensitiveHeaders = function (headers, sensitiveHeaderNames) {
|
|
|
103
119
|
return ret;
|
|
104
120
|
};
|
|
105
121
|
exports.removeSensitiveHeaders = removeSensitiveHeaders;
|
|
106
|
-
var
|
|
107
|
-
return patterns.some(function (pattern) { return (0, exports.
|
|
122
|
+
var isStringInPatterns = function (str, patterns) {
|
|
123
|
+
return patterns.some(function (pattern) { return (0, exports.isStringMatch)(str, pattern); });
|
|
108
124
|
};
|
|
109
|
-
exports.
|
|
110
|
-
var
|
|
111
|
-
if (!
|
|
125
|
+
exports.isStringInPatterns = isStringInPatterns;
|
|
126
|
+
var isStringMatch = function (str, pattern) {
|
|
127
|
+
if (!str || !pattern) {
|
|
112
128
|
return false;
|
|
113
129
|
}
|
|
114
|
-
if (pattern instanceof RegExp && !!
|
|
130
|
+
if (pattern instanceof RegExp && !!str.match(pattern)) {
|
|
115
131
|
return true;
|
|
116
132
|
}
|
|
117
|
-
if (typeof pattern === 'string' &&
|
|
133
|
+
if (typeof pattern === 'string' && str.startsWith(pattern)) {
|
|
118
134
|
return true;
|
|
119
135
|
}
|
|
120
136
|
return false;
|
|
121
137
|
};
|
|
122
|
-
exports.
|
|
138
|
+
exports.isStringMatch = isStringMatch;
|
|
123
139
|
/**
|
|
124
140
|
* @deprecated this method is not typesafe. Copy the fields manually instead,
|
|
125
141
|
* unsafe - ignores compilerOptions.strictNullChecks
|
|
@@ -75,9 +75,10 @@ export function redactCookieSecret(secret) {
|
|
|
75
75
|
return '***'.concat(secret.substring(secret.length - 3, secret.length));
|
|
76
76
|
}
|
|
77
77
|
export const addConfigDataToDetails = (details, config) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
transferExistingProperties(config, details, {
|
|
79
|
+
remoteConfigId: 'remote_config_id',
|
|
80
|
+
remoteConfigVersion: 'remote_config_version',
|
|
81
|
+
});
|
|
81
82
|
};
|
|
82
83
|
export const addCustomParametersToDetails = (details, customParameters) => {
|
|
83
84
|
if (customParameters) {
|
|
@@ -363,6 +363,12 @@ export class ConfigurationBase {
|
|
|
363
363
|
get tokenVersion() {
|
|
364
364
|
return this.configParams.px_token_version;
|
|
365
365
|
}
|
|
366
|
+
get snippetInjectionEnabled() {
|
|
367
|
+
return this.configParams.px_snippet_injection_enabled;
|
|
368
|
+
}
|
|
369
|
+
get createCustomSnippet() {
|
|
370
|
+
return this.configParams.px_create_custom_snippet;
|
|
371
|
+
}
|
|
366
372
|
get enableBlockedUrlOnCaptchaBlockPage() {
|
|
367
373
|
return true;
|
|
368
374
|
}
|
|
@@ -127,6 +127,8 @@ export const defaultConfigurationParams = () => ({
|
|
|
127
127
|
px_remote_config_retry_interval_ms: 1000,
|
|
128
128
|
px_url_decode_reserved_characters: false,
|
|
129
129
|
px_secured_pxhd_enabled: false,
|
|
130
|
+
px_snippet_injection_enabled: false,
|
|
131
|
+
px_create_custom_snippet: null,
|
|
130
132
|
px_custom_is_sensitive_request: null,
|
|
131
133
|
px_custom_is_monitored_request: null,
|
|
132
134
|
px_custom_is_enforced_request: null,
|
|
@@ -9,6 +9,7 @@ import { HttpActivityClient, HttpBatchedActivityClient } from '../activities';
|
|
|
9
9
|
import { HttpLogServiceClient } from '../logger';
|
|
10
10
|
import { AccountDefender, BotDefender, CredentialIntelligence, HypeSaleChallenge, ProductName, } from '../products';
|
|
11
11
|
import { EnforcerError, isValidTokenVersion } from '../utils';
|
|
12
|
+
import { DefaultSnippetRetriever } from '../snippet_injection';
|
|
12
13
|
export const createEnforcerInitializationBlock = (config, options) => {
|
|
13
14
|
const tokenVersion = config.tokenVersion;
|
|
14
15
|
if (!isValidTokenVersion(tokenVersion)) {
|
|
@@ -50,6 +51,8 @@ export const createEnforcerInitializationBlock = (config, options) => {
|
|
|
50
51
|
hmacUtils,
|
|
51
52
|
})
|
|
52
53
|
: null);
|
|
54
|
+
const snippetInjector = options.snippetInjector ?? null;
|
|
55
|
+
const snippetRetriever = options.snippetRetriever || (snippetInjector ? new DefaultSnippetRetriever(config) : null);
|
|
53
56
|
const allOptions = {
|
|
54
57
|
httpClient,
|
|
55
58
|
base64Utils,
|
|
@@ -69,6 +72,8 @@ export const createEnforcerInitializationBlock = (config, options) => {
|
|
|
69
72
|
remoteConfigStorageClient,
|
|
70
73
|
remoteConfigServiceClient,
|
|
71
74
|
remoteConfigUpdater,
|
|
75
|
+
snippetInjector,
|
|
76
|
+
snippetRetriever,
|
|
72
77
|
};
|
|
73
78
|
const products = createEnforcerProducts(config, options.products, base64Utils, hashUtils, urlUtils, ipRangeChecker);
|
|
74
79
|
return { products, ...allOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isNullOrUndefined,
|
|
1
|
+
import { isNullOrUndefined, isStringInPatterns } from '../utils';
|
|
2
2
|
import { HttpMethod } from '../http';
|
|
3
3
|
import { GraphQLOperationType } from './model';
|
|
4
4
|
export class DefaultGraphQLParser {
|
|
@@ -12,7 +12,7 @@ export class DefaultGraphQLParser {
|
|
|
12
12
|
}
|
|
13
13
|
isGraphQLRequest({ requestData }) {
|
|
14
14
|
return (requestData.method === HttpMethod.POST &&
|
|
15
|
-
|
|
15
|
+
isStringInPatterns(requestData.url.pathname, this.config.graphqlRoutes));
|
|
16
16
|
}
|
|
17
17
|
async parseGraphQLRequest(context) {
|
|
18
18
|
try {
|
package/lib/esm/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './context';
|
|
|
7
7
|
export * from './custom_parameters';
|
|
8
8
|
export * from './sensitive_request';
|
|
9
9
|
export * from './monitored_request';
|
|
10
|
+
export * from './snippet_injection';
|
|
10
11
|
export * from './cors';
|
|
11
12
|
export * from './enforcer';
|
|
12
13
|
export * from './filter';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BYPASS_MONITOR_HEADER_VALUE,
|
|
1
|
+
import { BYPASS_MONITOR_HEADER_VALUE, isStringInPatterns, ModuleMode } from '../utils';
|
|
2
2
|
export var MonitoredRequestUtils;
|
|
3
3
|
(function (MonitoredRequestUtils) {
|
|
4
4
|
MonitoredRequestUtils.isMonitoredRequest = async (config, context) => {
|
|
@@ -10,10 +10,10 @@ export var MonitoredRequestUtils;
|
|
|
10
10
|
return (monitorMode || monitoredRoute) && !MonitoredRequestUtils.isAllowedToBypassMonitor(config.bypassMonitorHeader, request);
|
|
11
11
|
};
|
|
12
12
|
MonitoredRequestUtils.isMonitoredRoute = (url, isMonitoredRoute) => {
|
|
13
|
-
return
|
|
13
|
+
return isStringInPatterns(url.pathname, isMonitoredRoute);
|
|
14
14
|
};
|
|
15
15
|
MonitoredRequestUtils.isEnforcedRoute = (url, enforcedRoutes) => {
|
|
16
|
-
return
|
|
16
|
+
return isStringInPatterns(url.pathname, enforcedRoutes);
|
|
17
17
|
};
|
|
18
18
|
MonitoredRequestUtils.invokeCustomIsEnforcedRequest = async (config, context) => {
|
|
19
19
|
if (config.customIsEnforcedRequest && typeof config.customIsEnforcedRequest === 'function') {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { CompositePhase, EnrichContextFromResponsePhase, ModifyOutgoingResponsePhase, SendAsyncActivitiesOnResponsePhase, } from '../impl';
|
|
2
2
|
export class PostEnforceFlow extends CompositePhase {
|
|
3
|
-
constructor(config, { products, activityClient, }) {
|
|
3
|
+
constructor(config, { products, activityClient, snippetRetriever, snippetInjector, }) {
|
|
4
4
|
super([
|
|
5
5
|
new EnrichContextFromResponsePhase(config, products),
|
|
6
|
-
new ModifyOutgoingResponsePhase(config, Object.values(products)),
|
|
6
|
+
new ModifyOutgoingResponsePhase(config, Object.values(products), snippetRetriever, snippetInjector),
|
|
7
7
|
new SendAsyncActivitiesOnResponsePhase(activityClient),
|
|
8
8
|
]);
|
|
9
9
|
}
|
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
import { PXHDSource, PXHDUtils } from '../../pxhd';
|
|
2
|
+
import { CONTENT_TYPE_HEADER_NAME, ContentType } from '../../http';
|
|
2
3
|
export class ModifyOutgoingResponsePhase {
|
|
3
4
|
config;
|
|
4
5
|
products;
|
|
5
|
-
|
|
6
|
+
snippetRetriever;
|
|
7
|
+
snippetInjector;
|
|
8
|
+
constructor(config, products, snippetRetriever, snippetInjector) {
|
|
6
9
|
this.config = config;
|
|
7
10
|
this.products = products;
|
|
11
|
+
this.snippetRetriever = snippetRetriever;
|
|
12
|
+
this.snippetInjector = snippetInjector;
|
|
8
13
|
}
|
|
9
14
|
async execute(context) {
|
|
10
15
|
await Promise.all(this.products.map((product) => product?.modifyOutgoingResponse(context)));
|
|
11
16
|
if (context.pxhd?.source === PXHDSource.RISK && context.response) {
|
|
12
17
|
PXHDUtils.addPxhdToOutgoingResponse(this.config, context, context.response);
|
|
13
18
|
}
|
|
19
|
+
await this.handleSnippetInjection(context);
|
|
14
20
|
return { done: false };
|
|
15
21
|
}
|
|
22
|
+
async handleSnippetInjection(context) {
|
|
23
|
+
if (this.snippetRetriever &&
|
|
24
|
+
this.snippetInjector &&
|
|
25
|
+
this.config.snippetInjectionEnabled &&
|
|
26
|
+
!!context.response?.headers?.get(CONTENT_TYPE_HEADER_NAME)?.includes(ContentType.TEXT_HTML)) {
|
|
27
|
+
context.logger.debug('snippet injection enabled');
|
|
28
|
+
const snippet = await this.snippetRetriever.retrieveSnippet(context);
|
|
29
|
+
context.response = await this.snippetInjector.injectSnippet(context.response, snippet);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
16
32
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { HttpMethod } from '../../../http';
|
|
2
2
|
import { FilterReason } from '../../../filter';
|
|
3
|
-
import { getExtension,
|
|
3
|
+
import { getExtension, isStringInPatterns } from '../../../utils';
|
|
4
4
|
export class DefaultBotDefenderFilter {
|
|
5
5
|
config;
|
|
6
6
|
ipRangeChecker;
|
|
@@ -52,10 +52,10 @@ export class DefaultBotDefenderFilter {
|
|
|
52
52
|
return this.config.filteredIps.some((range) => this.ipRangeChecker.isIpInRange(ip, range));
|
|
53
53
|
}
|
|
54
54
|
shouldFilterByRoute(route) {
|
|
55
|
-
return
|
|
55
|
+
return isStringInPatterns(route, this.config.filteredRoutes);
|
|
56
56
|
}
|
|
57
57
|
shouldFilterByUserAgent(userAgent) {
|
|
58
|
-
return this.config.filteredUserAgents
|
|
58
|
+
return isStringInPatterns(userAgent, this.config.filteredUserAgents);
|
|
59
59
|
}
|
|
60
60
|
async shouldFilterByCustomFunction(context) {
|
|
61
61
|
if (this.config.customIsFilteredRequest && typeof this.config.customIsFilteredRequest === 'function') {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isStringInPatterns } from '../utils';
|
|
2
2
|
export var SensitiveRequestUtils;
|
|
3
3
|
(function (SensitiveRequestUtils) {
|
|
4
4
|
SensitiveRequestUtils.isSensitiveRequest = async (config, context) => {
|
|
@@ -7,7 +7,7 @@ export var SensitiveRequestUtils;
|
|
|
7
7
|
(await SensitiveRequestUtils.invokeCustomIsSensitiveRequest(config, context)));
|
|
8
8
|
};
|
|
9
9
|
SensitiveRequestUtils.isSensitiveRoute = (url, sensitiveRoutes) => {
|
|
10
|
-
return
|
|
10
|
+
return isStringInPatterns(url.pathname, sensitiveRoutes);
|
|
11
11
|
};
|
|
12
12
|
SensitiveRequestUtils.isSensitiveGraphqlOperation = (graphQLData) => {
|
|
13
13
|
return !!graphQLData?.some((operation) => operation.sensitive);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ISnippetInjector';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { FirstPartySuffix, getMostCustomizedFirstPartyPath } from '../../products';
|
|
2
|
+
import { createDefaultSnippet } from '../utils';
|
|
3
|
+
export class DefaultSnippetRetriever {
|
|
4
|
+
config;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.config = config;
|
|
7
|
+
}
|
|
8
|
+
async retrieveSnippet(context) {
|
|
9
|
+
return (await this.retrieveCustomSnippet(context)) || this.retrieveDefaultSnippet(context);
|
|
10
|
+
}
|
|
11
|
+
async retrieveCustomSnippet(context) {
|
|
12
|
+
if (!this.config.createCustomSnippet) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const snippet = await this.config.createCustomSnippet(this.config.getActiveConfig(), context.requestData.request.getUnderlyingRequest(), context.response.getUnderlyingResponse());
|
|
17
|
+
if (!snippet || typeof snippet !== 'string') {
|
|
18
|
+
context.logger.debug(`invalid custom snippet of type ${typeof snippet}`);
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
context.logger.debug('retrieving custom snippet');
|
|
22
|
+
return snippet;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
context.logger.debug(`failed retrieving custom snippet: ${e}`);
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
retrieveDefaultSnippet(context) {
|
|
30
|
+
const sensorSrc = this.config.firstPartyEnabled
|
|
31
|
+
? getMostCustomizedFirstPartyPath(this.config, FirstPartySuffix.SENSOR)
|
|
32
|
+
: `${this.config.backendClientUrl}/${this.config.appId}/main.min.js`;
|
|
33
|
+
context.logger.debug(`retrieving default snippet with src ${sensorSrc}`);
|
|
34
|
+
return createDefaultSnippet(this.config.appId, sensorSrc);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export const createDefaultSnippet = (appId, sensorSrc) => `<script type="text/javascript">
|
|
2
|
+
(function(){
|
|
3
|
+
window._pxAppId = '${appId}';
|
|
4
|
+
var p = document.getElementsByTagName('script')[0], s = document.createElement('script');
|
|
5
|
+
s.async = 1;
|
|
6
|
+
s.src = '${sensorSrc}';
|
|
7
|
+
p.parentNode.insertBefore(s,p);
|
|
8
|
+
}());
|
|
9
|
+
</script>`;
|
|
@@ -9,6 +9,6 @@ export const X_PX_BYPASS_REASON_HEADER_NAME = 'x-px-bypass-reason';
|
|
|
9
9
|
export const PUSH_DATA_HMAC_HEADER_NAME = 'x-px-pushdata';
|
|
10
10
|
export const PUSH_DATA_FEATURE_HEADER_NAME = 'x-px-feature';
|
|
11
11
|
export const EMAIL_ADDRESS_REGEX = /^[a-zA-Z0-9_+&*-]+(?:\.[a-zA-Z0-9_+&*-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,7}$/;
|
|
12
|
-
export const URL_REGEX = /^(https
|
|
12
|
+
export const URL_REGEX = /^(https?:)\/\/(([^@\s:\/]+):?([^@\s\/]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)(\/?[^?#]*)(\?[^#]*|)(#.*|)$/;
|
|
13
13
|
export const REGEX_STRUCTURE = /^\/(.+?)\/([gimsuyvd]*)$/;
|
|
14
|
-
export const CORE_MODULE_VERSION = 'JS Core 0.
|
|
14
|
+
export const CORE_MODULE_VERSION = 'JS Core 0.24.0';
|
package/lib/esm/utils/utils.js
CHANGED
|
@@ -33,20 +33,36 @@ export const getExtension = (route) => {
|
|
|
33
33
|
return endOfPath.substring(extensionIndex);
|
|
34
34
|
};
|
|
35
35
|
export const redactSensitiveFields = (object, sensitiveFields) => {
|
|
36
|
-
const NUMBER_OF_TRAILING_CHARS_TO_EXPOSE = 5;
|
|
37
|
-
const SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS = NUMBER_OF_TRAILING_CHARS_TO_EXPOSE * 10;
|
|
38
36
|
const newObj = Object.assign({}, object);
|
|
39
37
|
sensitiveFields.forEach((fieldName) => {
|
|
40
38
|
const sensitiveValue = object[fieldName];
|
|
41
39
|
if (!sensitiveValue) {
|
|
42
40
|
return;
|
|
43
41
|
}
|
|
44
|
-
|
|
45
|
-
const trailingChars = sensitiveValue.substring(sensitiveValue.length - (trailingCharsExposed ? NUMBER_OF_TRAILING_CHARS_TO_EXPOSE : 0));
|
|
46
|
-
newObj[fieldName] = `***REDACTED***${trailingChars}`;
|
|
42
|
+
newObj[fieldName] = redactUnknownValue(sensitiveValue);
|
|
47
43
|
});
|
|
48
44
|
return newObj;
|
|
49
45
|
};
|
|
46
|
+
const redactUnknownValue = (value) => {
|
|
47
|
+
switch (typeof value) {
|
|
48
|
+
case 'string':
|
|
49
|
+
return redactSensitiveString(value);
|
|
50
|
+
case 'object':
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
return value.flatMap(redactUnknownValue).filter((v) => !isNullOrUndefined(v));
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
default:
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const redactSensitiveString = (sensitiveValue) => {
|
|
60
|
+
const NUMBER_OF_TRAILING_CHARS_TO_EXPOSE = 5;
|
|
61
|
+
const SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS = NUMBER_OF_TRAILING_CHARS_TO_EXPOSE * 10;
|
|
62
|
+
const trailingCharsExposed = sensitiveValue.length >= SENSITIVE_VALUE_MINIMUM_LENGTH_TO_EXPOSE_TRAILING_CHARS;
|
|
63
|
+
const trailingChars = sensitiveValue.substring(sensitiveValue.length - (trailingCharsExposed ? NUMBER_OF_TRAILING_CHARS_TO_EXPOSE : 0));
|
|
64
|
+
return `***REDACTED***${trailingChars}`;
|
|
65
|
+
};
|
|
50
66
|
export const removeSensitiveHeaders = (headers, sensitiveHeaderNames) => {
|
|
51
67
|
const ret = toMutableHeaders(headers);
|
|
52
68
|
sensitiveHeaderNames.forEach((name) => {
|
|
@@ -54,17 +70,17 @@ export const removeSensitiveHeaders = (headers, sensitiveHeaderNames) => {
|
|
|
54
70
|
});
|
|
55
71
|
return ret;
|
|
56
72
|
};
|
|
57
|
-
export const
|
|
58
|
-
return patterns.some((pattern) =>
|
|
73
|
+
export const isStringInPatterns = (str, patterns) => {
|
|
74
|
+
return patterns.some((pattern) => isStringMatch(str, pattern));
|
|
59
75
|
};
|
|
60
|
-
export const
|
|
61
|
-
if (!
|
|
76
|
+
export const isStringMatch = (str, pattern) => {
|
|
77
|
+
if (!str || !pattern) {
|
|
62
78
|
return false;
|
|
63
79
|
}
|
|
64
|
-
if (pattern instanceof RegExp && !!
|
|
80
|
+
if (pattern instanceof RegExp && !!str.match(pattern)) {
|
|
65
81
|
return true;
|
|
66
82
|
}
|
|
67
|
-
if (typeof pattern === 'string' &&
|
|
83
|
+
if (typeof pattern === 'string' && str.startsWith(pattern)) {
|
|
68
84
|
return true;
|
|
69
85
|
}
|
|
70
86
|
return false;
|
|
@@ -9,6 +9,7 @@ import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomL
|
|
|
9
9
|
import { CustomRequestFunction } from './CustomRequestFunction';
|
|
10
10
|
import { ExtractGraphQLKeywordsFunction } from '../graphql';
|
|
11
11
|
import { TokenVersion } from '../risk_token';
|
|
12
|
+
import { CustomSnippetFunction } from '../snippet_injection';
|
|
12
13
|
export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends string> implements IConfiguration<Req, Res, Added, Removed> {
|
|
13
14
|
protected configParams: RequiredAllConfigurationParams<Req, Res, Added, Removed>;
|
|
14
15
|
protected readonly staticConfigParams: StaticConfigurationParams<Req, Res, Added, Removed>;
|
|
@@ -43,7 +44,7 @@ export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends
|
|
|
43
44
|
get filteredHttpMethods(): string[];
|
|
44
45
|
get filteredIps(): string[];
|
|
45
46
|
get filteredRoutes(): Array<string | RegExp>;
|
|
46
|
-
get filteredUserAgents(): string
|
|
47
|
+
get filteredUserAgents(): Array<string | RegExp>;
|
|
47
48
|
get firstPartyEnabled(): boolean;
|
|
48
49
|
get customIsFilteredRequest(): CustomRequestFunction<Req> | null;
|
|
49
50
|
get customFirstPartyPrefix(): string;
|
|
@@ -115,5 +116,7 @@ export declare abstract class ConfigurationBase<Req, Res, Added, Removed extends
|
|
|
115
116
|
get urlDecodeReservedCharacters(): boolean;
|
|
116
117
|
get securedPxhdEnabled(): boolean;
|
|
117
118
|
get tokenVersion(): `${TokenVersion}`;
|
|
119
|
+
get snippetInjectionEnabled(): boolean;
|
|
120
|
+
get createCustomSnippet(): CustomSnippetFunction<Req, Res, Added, Removed> | null;
|
|
118
121
|
get enableBlockedUrlOnCaptchaBlockPage(): boolean;
|
|
119
122
|
}
|
|
@@ -8,6 +8,7 @@ import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomL
|
|
|
8
8
|
import { CustomRequestFunction } from './CustomRequestFunction';
|
|
9
9
|
import { ExtractGraphQLKeywordsFunction } from '../graphql';
|
|
10
10
|
import { TokenVersion } from '../risk_token';
|
|
11
|
+
import { CustomSnippetFunction } from '../snippet_injection';
|
|
11
12
|
export interface IConfiguration<Req, Res, Added, Removed> {
|
|
12
13
|
/**
|
|
13
14
|
* The application ID.
|
|
@@ -48,7 +49,7 @@ export interface IConfiguration<Req, Res, Added, Removed> {
|
|
|
48
49
|
/**
|
|
49
50
|
* An array of agents that should not go through the enforcer flow.
|
|
50
51
|
*/
|
|
51
|
-
readonly filteredUserAgents: string
|
|
52
|
+
readonly filteredUserAgents: Array<string | RegExp>;
|
|
52
53
|
/**
|
|
53
54
|
* An array of regular expressions or prefixes of routes that should not go through the enforcer flow.
|
|
54
55
|
*/
|
|
@@ -370,6 +371,14 @@ export interface IConfiguration<Req, Res, Added, Removed> {
|
|
|
370
371
|
* Whether to add the Secure attribute when setting the PXHD cookie.
|
|
371
372
|
*/
|
|
372
373
|
readonly securedPxhdEnabled: boolean;
|
|
374
|
+
/**
|
|
375
|
+
* Whether snippet injection on response bodies is enabled.
|
|
376
|
+
*/
|
|
377
|
+
readonly snippetInjectionEnabled: boolean;
|
|
378
|
+
/**
|
|
379
|
+
* A custom function that returns a string representing the snippet to be injected.
|
|
380
|
+
*/
|
|
381
|
+
readonly createCustomSnippet: CustomSnippetFunction<Req, Res, Added, Removed> | null;
|
|
373
382
|
/**
|
|
374
383
|
* Whether to include the request url in captcha block page
|
|
375
384
|
*/
|
|
@@ -7,6 +7,7 @@ import { CustomParametersFunction } from '../../custom_parameters';
|
|
|
7
7
|
import { CustomBlockResponseHeadersHandler, CustomPreflightHandler } from '../../cors';
|
|
8
8
|
import { CustomRequestFunction } from '../CustomRequestFunction';
|
|
9
9
|
import { TokenVersion } from '../../risk_token';
|
|
10
|
+
import { CustomSnippetFunction } from '../../snippet_injection';
|
|
10
11
|
export type StaticConfigurationParamsOnly = {
|
|
11
12
|
px_app_id: string;
|
|
12
13
|
px_cookie_secret: string | string[];
|
|
@@ -34,7 +35,11 @@ export type TokenV3ConfigurationParamsOnly = {
|
|
|
34
35
|
px_risk_cookie_min_iterations?: number;
|
|
35
36
|
px_risk_cookie_max_iterations?: number;
|
|
36
37
|
};
|
|
37
|
-
export type
|
|
38
|
+
export type SnippetInjectionParamsOnly<Req, Res, Added, Removed> = {
|
|
39
|
+
px_snippet_injection_enabled?: boolean;
|
|
40
|
+
px_create_custom_snippet?: CustomSnippetFunction<Req, Res, Added, Removed> | null;
|
|
41
|
+
};
|
|
42
|
+
export type CommonConfigurationParams<Req, Res, Added, Removed> = TokenV3ConfigurationParamsOnly & BatchedActivitiesConfigParamsOnly & SnippetInjectionParamsOnly<Req, Res, Added, Removed> & {
|
|
38
43
|
px_s2s_timeout?: number;
|
|
39
44
|
px_backend_url?: string;
|
|
40
45
|
px_user_agent_max_length?: number;
|
|
@@ -74,7 +79,7 @@ export type CommonConfigurationParams<Req, Res, Added, Removed> = TokenV3Configu
|
|
|
74
79
|
px_filter_by_http_method?: string[];
|
|
75
80
|
px_filter_by_ip?: string[];
|
|
76
81
|
px_filter_by_route?: Array<string | RegExp>;
|
|
77
|
-
px_filter_by_user_agent?: string
|
|
82
|
+
px_filter_by_user_agent?: Array<string | RegExp>;
|
|
78
83
|
px_css_ref?: string;
|
|
79
84
|
px_js_ref?: string;
|
|
80
85
|
px_custom_cookie_header?: string;
|
|
@@ -10,6 +10,7 @@ import { Products } from '../products';
|
|
|
10
10
|
import { IGraphQLParser } from '../graphql';
|
|
11
11
|
import { ILogServiceClient } from '../logger';
|
|
12
12
|
import { IRemoteConfigStorageClient, IRemoteConfigServiceClient, IRemoteConfigUpdater } from '../config';
|
|
13
|
+
import { ISnippetInjector, ISnippetRetriever } from '../snippet_injection';
|
|
13
14
|
export type EnforcerOptions<Req, Res, Added, Removed> = {
|
|
14
15
|
httpClient: IHttpClient;
|
|
15
16
|
base64Utils: IBase64Utils;
|
|
@@ -30,4 +31,6 @@ export type EnforcerOptions<Req, Res, Added, Removed> = {
|
|
|
30
31
|
remoteConfigUpdater?: IRemoteConfigUpdater<Req, Res> | null;
|
|
31
32
|
remoteConfigStorageClient?: IRemoteConfigStorageClient<Req, Res, Added, Removed> | null;
|
|
32
33
|
remoteConfigServiceClient?: IRemoteConfigServiceClient<Req, Res, Added, Removed> | null;
|
|
34
|
+
snippetRetriever?: ISnippetRetriever<Req, Res> | null;
|
|
35
|
+
snippetInjector?: ISnippetInjector<Res> | null;
|
|
33
36
|
};
|
package/lib/types/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './context';
|
|
|
7
7
|
export * from './custom_parameters';
|
|
8
8
|
export * from './sensitive_request';
|
|
9
9
|
export * from './monitored_request';
|
|
10
|
+
export * from './snippet_injection';
|
|
10
11
|
export * from './cors';
|
|
11
12
|
export * from './enforcer';
|
|
12
13
|
export * from './filter';
|
|
@@ -2,5 +2,5 @@ import { IConfiguration } from '../../config';
|
|
|
2
2
|
import { EnforcerOptions } from '../../enforcer';
|
|
3
3
|
import { CompositePhase } from '../impl';
|
|
4
4
|
export declare class PostEnforceFlow<Req, Res, Added, Removed> extends CompositePhase<Req, Res> {
|
|
5
|
-
constructor(config: IConfiguration<Req, Res, Added, Removed>, { products, activityClient, }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, 'products' | 'activityClient'>);
|
|
5
|
+
constructor(config: IConfiguration<Req, Res, Added, Removed>, { products, activityClient, snippetRetriever, snippetInjector, }: Pick<Required<EnforcerOptions<Req, Res, Added, Removed>>, 'products' | 'activityClient' | 'snippetRetriever' | 'snippetInjector'>);
|
|
6
6
|
}
|
|
@@ -3,9 +3,13 @@ import { IProduct } from '../../products';
|
|
|
3
3
|
import { IPhase } from '../IPhase';
|
|
4
4
|
import { PhaseResult } from '../PhaseResult';
|
|
5
5
|
import { IConfiguration } from '../../config';
|
|
6
|
+
import { ISnippetInjector, ISnippetRetriever } from '../../snippet_injection';
|
|
6
7
|
export declare class ModifyOutgoingResponsePhase<Req, Res, Added, Removed> implements IPhase<Req, Res> {
|
|
7
8
|
protected readonly config: IConfiguration<Req, Res, Added, Removed>;
|
|
8
9
|
protected readonly products: IProduct<any, Req, Res>[];
|
|
9
|
-
|
|
10
|
+
protected readonly snippetRetriever?: ISnippetRetriever<Req, Res> | null;
|
|
11
|
+
protected readonly snippetInjector?: ISnippetInjector<Res> | null;
|
|
12
|
+
constructor(config: IConfiguration<Req, Res, Added, Removed>, products: IProduct<any, Req, Res>[], snippetRetriever?: ISnippetRetriever<Req, Res> | null, snippetInjector?: ISnippetInjector<Res> | null);
|
|
10
13
|
execute(context: IContext<Req, Res>): Promise<PhaseResult>;
|
|
14
|
+
protected handleSnippetInjection(context: IContext<Req, Res>): Promise<void>;
|
|
11
15
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ISnippetInjector';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { IConfiguration } from '../../config';
|
|
2
|
+
import { ReadonlyContext } from '../../context';
|
|
3
|
+
import { ISnippetRetriever } from './ISnippetRetriever';
|
|
4
|
+
export declare class DefaultSnippetRetriever<Req, Res, Added, Removed> implements ISnippetRetriever<Req, Res> {
|
|
5
|
+
protected readonly config: IConfiguration<Req, Res, Added, Removed>;
|
|
6
|
+
constructor(config: IConfiguration<Req, Res, Added, Removed>);
|
|
7
|
+
retrieveSnippet(context: ReadonlyContext<Req, Res>): Promise<string>;
|
|
8
|
+
protected retrieveCustomSnippet(context: ReadonlyContext<Req, Res>): Promise<string | null>;
|
|
9
|
+
protected retrieveDefaultSnippet(context: ReadonlyContext<Req, Res>): string;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createDefaultSnippet: (appId: string, sensorSrc: string) => string;
|
|
@@ -11,4 +11,4 @@ export declare const PUSH_DATA_FEATURE_HEADER_NAME = "x-px-feature";
|
|
|
11
11
|
export declare const EMAIL_ADDRESS_REGEX: RegExp;
|
|
12
12
|
export declare const URL_REGEX: RegExp;
|
|
13
13
|
export declare const REGEX_STRUCTURE: RegExp;
|
|
14
|
-
export declare const CORE_MODULE_VERSION = "JS Core 0.
|
|
14
|
+
export declare const CORE_MODULE_VERSION = "JS Core 0.24.0";
|
|
@@ -11,8 +11,8 @@ export declare const getAuthorizationHeader: (authToken: string) => string;
|
|
|
11
11
|
export declare const getExtension: (route: string) => string;
|
|
12
12
|
export declare const redactSensitiveFields: <T extends Record<string, any>>(object: T, sensitiveFields: Array<keyof T | string>) => T;
|
|
13
13
|
export declare const removeSensitiveHeaders: (headers: ReadonlyHeaders, sensitiveHeaderNames: string[]) => Record<string, string[]>;
|
|
14
|
-
export declare const
|
|
15
|
-
export declare const
|
|
14
|
+
export declare const isStringInPatterns: (str: string, patterns: Array<string | RegExp>) => boolean;
|
|
15
|
+
export declare const isStringMatch: (str: string, pattern: string | RegExp) => boolean;
|
|
16
16
|
/**
|
|
17
17
|
* @deprecated this method is not typesafe. Copy the fields manually instead,
|
|
18
18
|
* unsafe - ignores compilerOptions.strictNullChecks
|