perimeterx-js-core 0.19.0 → 0.20.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.
@@ -427,6 +427,20 @@ var ConfigurationBase = /** @class */ (function () {
427
427
  enumerable: false,
428
428
  configurable: true
429
429
  });
430
+ Object.defineProperty(ConfigurationBase.prototype, "graphqlKeywords", {
431
+ get: function () {
432
+ return this.activeConfigParams.px_graphql_keywords;
433
+ },
434
+ enumerable: false,
435
+ configurable: true
436
+ });
437
+ Object.defineProperty(ConfigurationBase.prototype, "extractGraphQLKeywords", {
438
+ get: function () {
439
+ return this.activeConfigParams.px_extract_graphql_keywords;
440
+ },
441
+ enumerable: false,
442
+ configurable: true
443
+ });
430
444
  Object.defineProperty(ConfigurationBase.prototype, "sensitiveGraphqlOperationNames", {
431
445
  get: function () {
432
446
  return this.activeConfigParams.px_sensitive_graphql_operation_names;
@@ -19,7 +19,7 @@ exports.DEFAULT_COMMON_CONFIGURATION_PARAMS = {
19
19
  px_advanced_blocking_response_enabled: true,
20
20
  px_max_activity_batch_size: 0,
21
21
  px_batch_activities_timeout_ms: 1000,
22
- px_bypass_monitor_header: '',
22
+ px_bypass_monitor_header: 'x-px-block',
23
23
  px_enforced_routes: [],
24
24
  px_first_party_enabled: true,
25
25
  px_custom_first_party_prefix: '',
@@ -97,6 +97,7 @@ exports.DEFAULT_COMMON_CONFIGURATION_PARAMS = {
97
97
  px_custom_logo: '',
98
98
  px_graphql_enabled: true,
99
99
  px_graphql_routes: ['/graphql'],
100
+ px_graphql_keywords: [],
100
101
  px_sensitive_graphql_operation_names: [],
101
102
  px_sensitive_graphql_operation_types: [],
102
103
  px_enrich_custom_parameters: null,
@@ -118,4 +119,5 @@ exports.DEFAULT_COMMON_CONFIGURATION_PARAMS = {
118
119
  px_custom_is_monitored_request: null,
119
120
  px_custom_is_enforced_request: null,
120
121
  px_custom_is_filtered_request: null,
122
+ px_extract_graphql_keywords: null,
121
123
  };
@@ -42,13 +42,14 @@ var http_1 = require("../http");
42
42
  var model_1 = require("./model");
43
43
  var DefaultGraphQLParser = /** @class */ (function () {
44
44
  function DefaultGraphQLParser(config) {
45
- this.graphqlRoutes = config.graphqlRoutes;
46
- this.sensitiveOperationNames = config.sensitiveGraphqlOperationNames;
47
- this.sensitiveOperationTypes = config.sensitiveGraphqlOperationTypes;
45
+ this.config = config;
46
+ this.maxCharactersInGraphqlKeyword = 100;
47
+ this.maxGraphqlKeywordCount = 500;
48
48
  }
49
49
  DefaultGraphQLParser.prototype.isGraphQLRequest = function (_a) {
50
50
  var requestData = _a.requestData;
51
- return (requestData.method === http_1.HttpMethod.POST && (0, utils_1.isRouteInPatterns)(requestData.url.pathname, this.graphqlRoutes));
51
+ return (requestData.method === http_1.HttpMethod.POST &&
52
+ (0, utils_1.isRouteInPatterns)(requestData.url.pathname, this.config.graphqlRoutes));
52
53
  };
53
54
  DefaultGraphQLParser.prototype.parseGraphQLRequest = function (context) {
54
55
  return __awaiter(this, void 0, void 0, function () {
@@ -56,7 +57,7 @@ var DefaultGraphQLParser = /** @class */ (function () {
56
57
  return __generator(this, function (_a) {
57
58
  switch (_a.label) {
58
59
  case 0:
59
- _a.trys.push([0, 2, , 3]);
60
+ _a.trys.push([0, 3, , 4]);
60
61
  requestData = context.requestData;
61
62
  return [4 /*yield*/, this.getGraphQLOperationsFromBody(requestData.request, context)];
62
63
  case 1:
@@ -65,18 +66,20 @@ var DefaultGraphQLParser = /** @class */ (function () {
65
66
  context.logger.debug('unable to get graphql operations from request body');
66
67
  return [2 /*return*/, null];
67
68
  }
68
- data = this.parseGraphQLOperations(graphQLOperations);
69
+ return [4 /*yield*/, this.parseGraphQLOperations(graphQLOperations, context)];
70
+ case 2:
71
+ data = _a.sent();
69
72
  if (!data || data.length === 0) {
70
73
  context.logger.debug('unable to parse graphql operations');
71
74
  return [2 /*return*/, null];
72
75
  }
73
76
  context.logger.debug("".concat(data.length, " graphql operation").concat(data.length === 1 ? '' : 's', " parsed successfully"));
74
77
  return [2 /*return*/, data];
75
- case 2:
78
+ case 3:
76
79
  e_1 = _a.sent();
77
80
  context.logger.debug("unable to parse graphql request: ".concat(e_1));
78
81
  return [2 /*return*/, null];
79
- case 3: return [2 /*return*/];
82
+ case 4: return [2 /*return*/];
80
83
  }
81
84
  });
82
85
  });
@@ -92,6 +95,7 @@ var DefaultGraphQLParser = /** @class */ (function () {
92
95
  case 1:
93
96
  body = _a.sent();
94
97
  if (!body) {
98
+ context.logger.debug("received empty graphql body when calling .json()");
95
99
  return [2 /*return*/, null];
96
100
  }
97
101
  return [2 /*return*/, Array.isArray(body) ? body : [body]];
@@ -104,19 +108,31 @@ var DefaultGraphQLParser = /** @class */ (function () {
104
108
  });
105
109
  });
106
110
  };
107
- DefaultGraphQLParser.prototype.parseGraphQLOperations = function (operations) {
108
- var _this = this;
109
- return operations.map(function (operation) { return _this.parseGraphQlOperation(operation); }).filter(function (x) { return x; });
111
+ DefaultGraphQLParser.prototype.parseGraphQLOperations = function (operations, context) {
112
+ return __awaiter(this, void 0, void 0, function () {
113
+ var data;
114
+ var _this = this;
115
+ return __generator(this, function (_a) {
116
+ switch (_a.label) {
117
+ case 0: return [4 /*yield*/, Promise.all(operations.map(function (operation) { return _this.parseGraphQLOperation(operation, context); }))];
118
+ case 1:
119
+ data = _a.sent();
120
+ return [2 /*return*/, data.filter(Boolean)];
121
+ }
122
+ });
123
+ });
110
124
  };
111
- DefaultGraphQLParser.prototype.parseGraphQlOperation = function (operation) {
125
+ DefaultGraphQLParser.prototype.parseGraphQLOperation = function (operation, context) {
112
126
  if (!operation.query || typeof operation.query !== 'string') {
127
+ context.logger.debug('no query found');
113
128
  return null;
114
129
  }
115
130
  var operationNameToTypeMap = this.getOperationNameToTypeMap(operation.query);
116
131
  if (!operationNameToTypeMap) {
132
+ context.logger.debug('operationNameToTypeMap returned null');
117
133
  return null;
118
134
  }
119
- return this.getGraphQLData(operationNameToTypeMap, operation);
135
+ return this.getGraphQLData(operationNameToTypeMap, operation, context);
120
136
  };
121
137
  DefaultGraphQLParser.prototype.getOperationNameToTypeMap = function (query) {
122
138
  var operationTypesString = Object.values(model_1.GraphQLOperationType).join('|');
@@ -136,28 +152,128 @@ var DefaultGraphQLParser = /** @class */ (function () {
136
152
  }
137
153
  return map;
138
154
  };
139
- DefaultGraphQLParser.prototype.getGraphQLData = function (operationNameToTypeMap, operation) {
140
- var name = operation.operationName ||
141
- (Object.keys(operationNameToTypeMap).length === 1 ? Object.keys(operationNameToTypeMap)[0] : undefined);
142
- var type = operationNameToTypeMap[name];
143
- if (!type && /^\s*{/.test(operation.query)) {
144
- type = model_1.GraphQLOperationType.QUERY;
155
+ DefaultGraphQLParser.prototype.getGraphQLData = function (operationNameToTypeMap, operation, context) {
156
+ return __awaiter(this, void 0, void 0, function () {
157
+ var name, type, data, keywords;
158
+ return __generator(this, function (_a) {
159
+ switch (_a.label) {
160
+ case 0:
161
+ name = this.getOperationName(operationNameToTypeMap, operation);
162
+ type = this.getOperationType(operation, name, operationNameToTypeMap);
163
+ if (!type) {
164
+ return [2 /*return*/, null];
165
+ }
166
+ data = { type: type };
167
+ if (name) {
168
+ data.name = name;
169
+ }
170
+ return [4 /*yield*/, this.getQueryKeywords(operation.query, context)];
171
+ case 1:
172
+ keywords = _a.sent();
173
+ if (keywords) {
174
+ data.keywords = this.cleanKeywords(keywords);
175
+ }
176
+ if (this.isSensitiveOperation(name, type, keywords)) {
177
+ data.sensitive = true;
178
+ }
179
+ if (operation.variables && typeof operation.variables === 'object') {
180
+ data.variables = this.extractGraphQLVariableNames(operation.variables);
181
+ }
182
+ return [2 /*return*/, data];
183
+ }
184
+ });
185
+ });
186
+ };
187
+ DefaultGraphQLParser.prototype.getOperationType = function (operation, operationName, operationNameToTypeMap) {
188
+ if (operationName && operationNameToTypeMap[operationName]) {
189
+ return operationNameToTypeMap[operationName];
145
190
  }
146
- if (!type) {
147
- return null;
191
+ if (this.isGraphqlQueryShorthand(operation.query)) {
192
+ return model_1.GraphQLOperationType.QUERY;
148
193
  }
149
- var data = { name: name, type: type };
150
- if (this.isSensitiveOperation(name, type)) {
151
- data.sensitive = true;
194
+ var match = operation.query.match(new RegExp("^\\s*(".concat(Object.values(model_1.GraphQLOperationType).join('|'), ")(?:\\s|{)")));
195
+ if ((match === null || match === void 0 ? void 0 : match[1]) && !operationName) {
196
+ return match[1];
152
197
  }
153
- if (operation.variables && typeof operation.variables === 'object') {
154
- data.variables = this.extractGraphQLVariableNames(operation.variables);
198
+ return null;
199
+ };
200
+ DefaultGraphQLParser.prototype.isGraphqlQueryShorthand = function (query) {
201
+ return /^\s*{/.test(query);
202
+ };
203
+ DefaultGraphQLParser.prototype.getOperationName = function (operationNameToTypeMap, operation) {
204
+ return (operation.operationName ||
205
+ (Object.keys(operationNameToTypeMap).length === 1 ? Object.keys(operationNameToTypeMap)[0] : undefined));
206
+ };
207
+ DefaultGraphQLParser.prototype.getQueryKeywords = function (query, context) {
208
+ var _a;
209
+ return __awaiter(this, void 0, void 0, function () {
210
+ var keywords;
211
+ return __generator(this, function (_b) {
212
+ switch (_b.label) {
213
+ case 0:
214
+ if (!(this.config.extractGraphQLKeywords && typeof this.config.extractGraphQLKeywords === 'function')) return [3 /*break*/, 2];
215
+ return [4 /*yield*/, this.getQueryKeywordsFromCustomFunction(query, context)];
216
+ case 1:
217
+ keywords = _b.sent();
218
+ if (Array.isArray(keywords)) {
219
+ return [2 /*return*/, keywords];
220
+ }
221
+ _b.label = 2;
222
+ case 2:
223
+ if (((_a = this.config.graphqlKeywords) === null || _a === void 0 ? void 0 : _a.length) > 0) {
224
+ return [2 /*return*/, this.getQueryKeywordsFromArray(query, context)];
225
+ }
226
+ return [2 /*return*/, null];
227
+ }
228
+ });
229
+ });
230
+ };
231
+ DefaultGraphQLParser.prototype.cleanKeywords = function (keywords) {
232
+ var _this = this;
233
+ return keywords
234
+ .slice(0, this.maxGraphqlKeywordCount)
235
+ .map(function (kw) { return kw.trim().substring(0, _this.maxCharactersInGraphqlKeyword); });
236
+ };
237
+ DefaultGraphQLParser.prototype.getQueryKeywordsFromCustomFunction = function (query, context) {
238
+ return __awaiter(this, void 0, void 0, function () {
239
+ return __generator(this, function (_a) {
240
+ try {
241
+ return [2 /*return*/, this.config.extractGraphQLKeywords(query)];
242
+ }
243
+ catch (e) {
244
+ context.logger.debug("unable to extract graphql keywords via custom function: ".concat(e));
245
+ return [2 /*return*/, null];
246
+ }
247
+ return [2 /*return*/];
248
+ });
249
+ });
250
+ };
251
+ DefaultGraphQLParser.prototype.getQueryKeywordsFromArray = function (query, context) {
252
+ var _this = this;
253
+ var keywords = [];
254
+ try {
255
+ this.config.graphqlKeywords.forEach(function (keyword) {
256
+ var pattern = _this.toGlobalRegExp(keyword);
257
+ var matchGroup = query.match(pattern);
258
+ if (!matchGroup) {
259
+ return;
260
+ }
261
+ keywords = keywords.concat(matchGroup);
262
+ });
263
+ }
264
+ catch (e) {
265
+ context.logger.debug("unable to extract graphql keywords via array: ".concat(e));
266
+ return null;
155
267
  }
156
- return data;
268
+ return keywords;
157
269
  };
158
- DefaultGraphQLParser.prototype.isSensitiveOperation = function (operationName, operationType) {
159
- return (this.sensitiveOperationTypes.some(function (type) { return type === operationType; }) ||
160
- this.sensitiveOperationNames.some(function (name) { return name === operationName; }));
270
+ DefaultGraphQLParser.prototype.isSensitiveOperation = function (operationName, operationType, keywords) {
271
+ var _this = this;
272
+ return (this.config.sensitiveGraphqlOperationTypes.some(function (type) { return type === operationType; }) ||
273
+ this.config.sensitiveGraphqlOperationNames.some(function (name) {
274
+ var pattern = _this.toGlobalRegExp(name);
275
+ return pattern.test(operationName) || (keywords === null || keywords === void 0 ? void 0 : keywords.some(function (kw) { return pattern.test(kw); }));
276
+ }));
161
277
  };
162
278
  DefaultGraphQLParser.prototype.extractGraphQLVariableNames = function (variables) {
163
279
  var processVariables = function (variablesObj, prefix) {
@@ -174,6 +290,15 @@ var DefaultGraphQLParser = /** @class */ (function () {
174
290
  };
175
291
  return processVariables(variables, '');
176
292
  };
293
+ DefaultGraphQLParser.prototype.toGlobalRegExp = function (pattern) {
294
+ if (typeof pattern === 'string') {
295
+ return new RegExp(pattern, 'g');
296
+ }
297
+ if (pattern.global) {
298
+ return pattern;
299
+ }
300
+ return new RegExp(pattern, pattern.flags + 'g');
301
+ };
177
302
  return DefaultGraphQLParser;
178
303
  }());
179
304
  exports.DefaultGraphQLParser = DefaultGraphQLParser;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./model"), exports);
18
18
  __exportStar(require("./IGraphQLParser"), exports);
19
19
  __exportStar(require("./DefaultGraphQLParser"), exports);
20
+ __exportStar(require("./ExtractGraphQLKeywordsFunction"), exports);
@@ -13,4 +13,4 @@ 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
15
  exports.URL_REGEX = /^(https?\:)\/\/(([^@\s:]+):?([^@\s]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/;
16
- exports.CORE_MODULE_VERSION = 'JS Core 0.19.0';
16
+ exports.CORE_MODULE_VERSION = 'JS Core 0.20.0';
@@ -223,6 +223,12 @@ export class ConfigurationBase {
223
223
  get graphqlRoutes() {
224
224
  return this.activeConfigParams.px_graphql_routes;
225
225
  }
226
+ get graphqlKeywords() {
227
+ return this.activeConfigParams.px_graphql_keywords;
228
+ }
229
+ get extractGraphQLKeywords() {
230
+ return this.activeConfigParams.px_extract_graphql_keywords;
231
+ }
226
232
  get sensitiveGraphqlOperationNames() {
227
233
  return this.activeConfigParams.px_sensitive_graphql_operation_names;
228
234
  }
@@ -16,7 +16,7 @@ export const DEFAULT_COMMON_CONFIGURATION_PARAMS = {
16
16
  px_advanced_blocking_response_enabled: true,
17
17
  px_max_activity_batch_size: 0,
18
18
  px_batch_activities_timeout_ms: 1000,
19
- px_bypass_monitor_header: '',
19
+ px_bypass_monitor_header: 'x-px-block',
20
20
  px_enforced_routes: [],
21
21
  px_first_party_enabled: true,
22
22
  px_custom_first_party_prefix: '',
@@ -94,6 +94,7 @@ export const DEFAULT_COMMON_CONFIGURATION_PARAMS = {
94
94
  px_custom_logo: '',
95
95
  px_graphql_enabled: true,
96
96
  px_graphql_routes: ['/graphql'],
97
+ px_graphql_keywords: [],
97
98
  px_sensitive_graphql_operation_names: [],
98
99
  px_sensitive_graphql_operation_types: [],
99
100
  px_enrich_custom_parameters: null,
@@ -115,4 +116,5 @@ export const DEFAULT_COMMON_CONFIGURATION_PARAMS = {
115
116
  px_custom_is_monitored_request: null,
116
117
  px_custom_is_enforced_request: null,
117
118
  px_custom_is_filtered_request: null,
119
+ px_extract_graphql_keywords: null,
118
120
  };
@@ -2,16 +2,17 @@ import { isRouteInPatterns } from '../utils';
2
2
  import { HttpMethod } from '../http';
3
3
  import { GraphQLOperationType } from './model';
4
4
  export class DefaultGraphQLParser {
5
- graphqlRoutes;
6
- sensitiveOperationTypes;
7
- sensitiveOperationNames;
5
+ config;
6
+ maxCharactersInGraphqlKeyword;
7
+ maxGraphqlKeywordCount;
8
8
  constructor(config) {
9
- this.graphqlRoutes = config.graphqlRoutes;
10
- this.sensitiveOperationNames = config.sensitiveGraphqlOperationNames;
11
- this.sensitiveOperationTypes = config.sensitiveGraphqlOperationTypes;
9
+ this.config = config;
10
+ this.maxCharactersInGraphqlKeyword = 100;
11
+ this.maxGraphqlKeywordCount = 500;
12
12
  }
13
13
  isGraphQLRequest({ requestData }) {
14
- return (requestData.method === HttpMethod.POST && isRouteInPatterns(requestData.url.pathname, this.graphqlRoutes));
14
+ return (requestData.method === HttpMethod.POST &&
15
+ isRouteInPatterns(requestData.url.pathname, this.config.graphqlRoutes));
15
16
  }
16
17
  async parseGraphQLRequest(context) {
17
18
  try {
@@ -21,7 +22,7 @@ export class DefaultGraphQLParser {
21
22
  context.logger.debug('unable to get graphql operations from request body');
22
23
  return null;
23
24
  }
24
- const data = this.parseGraphQLOperations(graphQLOperations);
25
+ const data = await this.parseGraphQLOperations(graphQLOperations, context);
25
26
  if (!data || data.length === 0) {
26
27
  context.logger.debug('unable to parse graphql operations');
27
28
  return null;
@@ -36,8 +37,9 @@ export class DefaultGraphQLParser {
36
37
  }
37
38
  async getGraphQLOperationsFromBody(request, context) {
38
39
  try {
39
- let body = await request.json();
40
+ const body = await request.json();
40
41
  if (!body) {
42
+ context.logger.debug(`received empty graphql body when calling .json()`);
41
43
  return null;
42
44
  }
43
45
  return Array.isArray(body) ? body : [body];
@@ -47,18 +49,21 @@ export class DefaultGraphQLParser {
47
49
  return null;
48
50
  }
49
51
  }
50
- parseGraphQLOperations(operations) {
51
- return operations.map((operation) => this.parseGraphQlOperation(operation)).filter((x) => x);
52
+ async parseGraphQLOperations(operations, context) {
53
+ const data = await Promise.all(operations.map((operation) => this.parseGraphQLOperation(operation, context)));
54
+ return data.filter(Boolean);
52
55
  }
53
- parseGraphQlOperation(operation) {
56
+ parseGraphQLOperation(operation, context) {
54
57
  if (!operation.query || typeof operation.query !== 'string') {
58
+ context.logger.debug('no query found');
55
59
  return null;
56
60
  }
57
61
  const operationNameToTypeMap = this.getOperationNameToTypeMap(operation.query);
58
62
  if (!operationNameToTypeMap) {
63
+ context.logger.debug('operationNameToTypeMap returned null');
59
64
  return null;
60
65
  }
61
- return this.getGraphQLData(operationNameToTypeMap, operation);
66
+ return this.getGraphQLData(operationNameToTypeMap, operation, context);
62
67
  }
63
68
  getOperationNameToTypeMap(query) {
64
69
  const operationTypesString = Object.values(GraphQLOperationType).join('|');
@@ -78,18 +83,21 @@ export class DefaultGraphQLParser {
78
83
  }
79
84
  return map;
80
85
  }
81
- getGraphQLData(operationNameToTypeMap, operation) {
82
- let name = operation.operationName ||
83
- (Object.keys(operationNameToTypeMap).length === 1 ? Object.keys(operationNameToTypeMap)[0] : undefined);
84
- let type = operationNameToTypeMap[name];
85
- if (!type && /^\s*{/.test(operation.query)) {
86
- type = GraphQLOperationType.QUERY;
87
- }
86
+ async getGraphQLData(operationNameToTypeMap, operation, context) {
87
+ const name = this.getOperationName(operationNameToTypeMap, operation);
88
+ const type = this.getOperationType(operation, name, operationNameToTypeMap);
88
89
  if (!type) {
89
90
  return null;
90
91
  }
91
- const data = { name, type };
92
- if (this.isSensitiveOperation(name, type)) {
92
+ const data = { type };
93
+ if (name) {
94
+ data.name = name;
95
+ }
96
+ const keywords = await this.getQueryKeywords(operation.query, context);
97
+ if (keywords) {
98
+ data.keywords = this.cleanKeywords(keywords);
99
+ }
100
+ if (this.isSensitiveOperation(name, type, keywords)) {
93
101
  data.sensitive = true;
94
102
  }
95
103
  if (operation.variables && typeof operation.variables === 'object') {
@@ -97,9 +105,76 @@ export class DefaultGraphQLParser {
97
105
  }
98
106
  return data;
99
107
  }
100
- isSensitiveOperation(operationName, operationType) {
101
- return (this.sensitiveOperationTypes.some((type) => type === operationType) ||
102
- this.sensitiveOperationNames.some((name) => name === operationName));
108
+ getOperationType(operation, operationName, operationNameToTypeMap) {
109
+ if (operationName && operationNameToTypeMap[operationName]) {
110
+ return operationNameToTypeMap[operationName];
111
+ }
112
+ if (this.isGraphqlQueryShorthand(operation.query)) {
113
+ return GraphQLOperationType.QUERY;
114
+ }
115
+ const match = operation.query.match(new RegExp(`^\\s*(${Object.values(GraphQLOperationType).join('|')})(?:\\s|{)`));
116
+ if (match?.[1] && !operationName) {
117
+ return match[1];
118
+ }
119
+ return null;
120
+ }
121
+ isGraphqlQueryShorthand(query) {
122
+ return /^\s*{/.test(query);
123
+ }
124
+ getOperationName(operationNameToTypeMap, operation) {
125
+ return (operation.operationName ||
126
+ (Object.keys(operationNameToTypeMap).length === 1 ? Object.keys(operationNameToTypeMap)[0] : undefined));
127
+ }
128
+ async getQueryKeywords(query, context) {
129
+ if (this.config.extractGraphQLKeywords && typeof this.config.extractGraphQLKeywords === 'function') {
130
+ const keywords = await this.getQueryKeywordsFromCustomFunction(query, context);
131
+ if (Array.isArray(keywords)) {
132
+ return keywords;
133
+ }
134
+ }
135
+ if (this.config.graphqlKeywords?.length > 0) {
136
+ return this.getQueryKeywordsFromArray(query, context);
137
+ }
138
+ return null;
139
+ }
140
+ cleanKeywords(keywords) {
141
+ return keywords
142
+ .slice(0, this.maxGraphqlKeywordCount)
143
+ .map((kw) => kw.trim().substring(0, this.maxCharactersInGraphqlKeyword));
144
+ }
145
+ async getQueryKeywordsFromCustomFunction(query, context) {
146
+ try {
147
+ return this.config.extractGraphQLKeywords(query);
148
+ }
149
+ catch (e) {
150
+ context.logger.debug(`unable to extract graphql keywords via custom function: ${e}`);
151
+ return null;
152
+ }
153
+ }
154
+ getQueryKeywordsFromArray(query, context) {
155
+ let keywords = [];
156
+ try {
157
+ this.config.graphqlKeywords.forEach((keyword) => {
158
+ const pattern = this.toGlobalRegExp(keyword);
159
+ let matchGroup = query.match(pattern);
160
+ if (!matchGroup) {
161
+ return;
162
+ }
163
+ keywords = keywords.concat(matchGroup);
164
+ });
165
+ }
166
+ catch (e) {
167
+ context.logger.debug(`unable to extract graphql keywords via array: ${e}`);
168
+ return null;
169
+ }
170
+ return keywords;
171
+ }
172
+ isSensitiveOperation(operationName, operationType, keywords) {
173
+ return (this.config.sensitiveGraphqlOperationTypes.some((type) => type === operationType) ||
174
+ this.config.sensitiveGraphqlOperationNames.some((name) => {
175
+ const pattern = this.toGlobalRegExp(name);
176
+ return pattern.test(operationName) || keywords?.some((kw) => pattern.test(kw));
177
+ }));
103
178
  }
104
179
  extractGraphQLVariableNames(variables) {
105
180
  const processVariables = (variablesObj, prefix) => Object.entries(variablesObj).reduce((total, [key, value]) => {
@@ -113,4 +188,13 @@ export class DefaultGraphQLParser {
113
188
  }, []);
114
189
  return processVariables(variables, '');
115
190
  }
191
+ toGlobalRegExp(pattern) {
192
+ if (typeof pattern === 'string') {
193
+ return new RegExp(pattern, 'g');
194
+ }
195
+ if (pattern.global) {
196
+ return pattern;
197
+ }
198
+ return new RegExp(pattern, pattern.flags + 'g');
199
+ }
116
200
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,3 +1,4 @@
1
1
  export * from './model';
2
2
  export * from './IGraphQLParser';
3
3
  export * from './DefaultGraphQLParser';
4
+ export * from './ExtractGraphQLKeywordsFunction';
@@ -10,4 +10,4 @@ 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
12
  export const URL_REGEX = /^(https?\:)\/\/(([^@\s:]+):?([^@\s]*)@)?(([^:\/?#]*)(?:\:([0-9]+))?)([\/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/;
13
- export const CORE_MODULE_VERSION = 'JS Core 0.19.0';
13
+ export const CORE_MODULE_VERSION = 'JS Core 0.20.0';
@@ -268,7 +268,8 @@ export declare const createAsyncActivity: <Req, Res>(activityType: ActivityType,
268
268
  readonly is_hype_sale?: boolean;
269
269
  };
270
270
  readonly graphqlData?: readonly {
271
- readonly type: import("..").GraphQLOperationType;
271
+ readonly type?: import("..").GraphQLOperationType;
272
+ readonly keywords?: readonly string[];
272
273
  readonly name?: string;
273
274
  readonly sensitive?: boolean;
274
275
  readonly variables?: readonly string[];
@@ -494,7 +495,8 @@ export declare const createAsyncActivity: <Req, Res>(activityType: ActivityType,
494
495
  readonly is_hype_sale?: boolean;
495
496
  };
496
497
  readonly graphqlData?: readonly {
497
- readonly type: import("..").GraphQLOperationType;
498
+ readonly type?: import("..").GraphQLOperationType;
499
+ readonly keywords?: readonly string[];
498
500
  readonly name?: string;
499
501
  readonly sensitive?: boolean;
500
502
  readonly variables?: readonly string[];
@@ -651,7 +653,8 @@ export declare const createAsyncActivity: <Req, Res>(activityType: ActivityType,
651
653
  readonly is_hype_sale?: boolean;
652
654
  };
653
655
  readonly graphqlData?: readonly {
654
- readonly type: import("..").GraphQLOperationType;
656
+ readonly type?: import("..").GraphQLOperationType;
657
+ readonly keywords?: readonly string[];
655
658
  readonly name?: string;
656
659
  readonly sensitive?: boolean;
657
660
  readonly variables?: readonly string[];
@@ -934,7 +937,8 @@ export declare const createActivityDetails: <Req, Res>(activityType: ActivityTyp
934
937
  readonly is_hype_sale?: boolean;
935
938
  };
936
939
  readonly graphqlData?: readonly {
937
- readonly type: import("..").GraphQLOperationType;
940
+ readonly type?: import("..").GraphQLOperationType;
941
+ readonly keywords?: readonly string[];
938
942
  readonly name?: string;
939
943
  readonly sensitive?: boolean;
940
944
  readonly variables?: readonly string[];
@@ -1160,7 +1164,8 @@ export declare const createActivityDetails: <Req, Res>(activityType: ActivityTyp
1160
1164
  readonly is_hype_sale?: boolean;
1161
1165
  };
1162
1166
  readonly graphqlData?: readonly {
1163
- readonly type: import("..").GraphQLOperationType;
1167
+ readonly type?: import("..").GraphQLOperationType;
1168
+ readonly keywords?: readonly string[];
1164
1169
  readonly name?: string;
1165
1170
  readonly sensitive?: boolean;
1166
1171
  readonly variables?: readonly string[];
@@ -1317,7 +1322,8 @@ export declare const createActivityDetails: <Req, Res>(activityType: ActivityTyp
1317
1322
  readonly is_hype_sale?: boolean;
1318
1323
  };
1319
1324
  readonly graphqlData?: readonly {
1320
- readonly type: import("..").GraphQLOperationType;
1325
+ readonly type?: import("..").GraphQLOperationType;
1326
+ readonly keywords?: readonly string[];
1321
1327
  readonly name?: string;
1322
1328
  readonly sensitive?: boolean;
1323
1329
  readonly variables?: readonly string[];
@@ -1600,7 +1606,8 @@ export declare const createAsyncActivityCommonDetails: <Req, Res>(context: {
1600
1606
  readonly is_hype_sale?: boolean;
1601
1607
  };
1602
1608
  readonly graphqlData?: readonly {
1603
- readonly type: import("..").GraphQLOperationType;
1609
+ readonly type?: import("..").GraphQLOperationType;
1610
+ readonly keywords?: readonly string[];
1604
1611
  readonly name?: string;
1605
1612
  readonly sensitive?: boolean;
1606
1613
  readonly variables?: readonly string[];
@@ -1826,7 +1833,8 @@ export declare const createAsyncActivityCommonDetails: <Req, Res>(context: {
1826
1833
  readonly is_hype_sale?: boolean;
1827
1834
  };
1828
1835
  readonly graphqlData?: readonly {
1829
- readonly type: import("..").GraphQLOperationType;
1836
+ readonly type?: import("..").GraphQLOperationType;
1837
+ readonly keywords?: readonly string[];
1830
1838
  readonly name?: string;
1831
1839
  readonly sensitive?: boolean;
1832
1840
  readonly variables?: readonly string[];
@@ -1983,7 +1991,8 @@ export declare const createAsyncActivityCommonDetails: <Req, Res>(context: {
1983
1991
  readonly is_hype_sale?: boolean;
1984
1992
  };
1985
1993
  readonly graphqlData?: readonly {
1986
- readonly type: import("..").GraphQLOperationType;
1994
+ readonly type?: import("..").GraphQLOperationType;
1995
+ readonly keywords?: readonly string[];
1987
1996
  readonly name?: string;
1988
1997
  readonly sensitive?: boolean;
1989
1998
  readonly variables?: readonly string[];
@@ -2266,7 +2275,8 @@ export declare const createCommonActivityDetails: <Req, Res>(config: IConfigurat
2266
2275
  readonly is_hype_sale?: boolean;
2267
2276
  };
2268
2277
  readonly graphqlData?: readonly {
2269
- readonly type: import("..").GraphQLOperationType;
2278
+ readonly type?: import("..").GraphQLOperationType;
2279
+ readonly keywords?: readonly string[];
2270
2280
  readonly name?: string;
2271
2281
  readonly sensitive?: boolean;
2272
2282
  readonly variables?: readonly string[];
@@ -2492,7 +2502,8 @@ export declare const createCommonActivityDetails: <Req, Res>(config: IConfigurat
2492
2502
  readonly is_hype_sale?: boolean;
2493
2503
  };
2494
2504
  readonly graphqlData?: readonly {
2495
- readonly type: import("..").GraphQLOperationType;
2505
+ readonly type?: import("..").GraphQLOperationType;
2506
+ readonly keywords?: readonly string[];
2496
2507
  readonly name?: string;
2497
2508
  readonly sensitive?: boolean;
2498
2509
  readonly variables?: readonly string[];
@@ -2649,7 +2660,8 @@ export declare const createCommonActivityDetails: <Req, Res>(config: IConfigurat
2649
2660
  readonly is_hype_sale?: boolean;
2650
2661
  };
2651
2662
  readonly graphqlData?: readonly {
2652
- readonly type: import("..").GraphQLOperationType;
2663
+ readonly type?: import("..").GraphQLOperationType;
2664
+ readonly keywords?: readonly string[];
2653
2665
  readonly name?: string;
2654
2666
  readonly sensitive?: boolean;
2655
2667
  readonly variables?: readonly string[];
@@ -2932,7 +2944,8 @@ export declare const addRootContextDataToDetails: <Req, Res>(details: CommonActi
2932
2944
  readonly is_hype_sale?: boolean;
2933
2945
  };
2934
2946
  readonly graphqlData?: readonly {
2935
- readonly type: import("..").GraphQLOperationType;
2947
+ readonly type?: import("..").GraphQLOperationType;
2948
+ readonly keywords?: readonly string[];
2936
2949
  readonly name?: string;
2937
2950
  readonly sensitive?: boolean;
2938
2951
  readonly variables?: readonly string[];
@@ -3158,7 +3171,8 @@ export declare const addRootContextDataToDetails: <Req, Res>(details: CommonActi
3158
3171
  readonly is_hype_sale?: boolean;
3159
3172
  };
3160
3173
  readonly graphqlData?: readonly {
3161
- readonly type: import("..").GraphQLOperationType;
3174
+ readonly type?: import("..").GraphQLOperationType;
3175
+ readonly keywords?: readonly string[];
3162
3176
  readonly name?: string;
3163
3177
  readonly sensitive?: boolean;
3164
3178
  readonly variables?: readonly string[];
@@ -3315,7 +3329,8 @@ export declare const addRootContextDataToDetails: <Req, Res>(details: CommonActi
3315
3329
  readonly is_hype_sale?: boolean;
3316
3330
  };
3317
3331
  readonly graphqlData?: readonly {
3318
- readonly type: import("..").GraphQLOperationType;
3332
+ readonly type?: import("..").GraphQLOperationType;
3333
+ readonly keywords?: readonly string[];
3319
3334
  readonly name?: string;
3320
3335
  readonly sensitive?: boolean;
3321
3336
  readonly variables?: readonly string[];
@@ -3656,7 +3671,8 @@ export declare const addRiskApiDataToAsyncActivityCommonDetails: <Req, Res>(deta
3656
3671
  readonly is_hype_sale?: boolean;
3657
3672
  };
3658
3673
  readonly graphqlData?: readonly {
3659
- readonly type: import("..").GraphQLOperationType;
3674
+ readonly type?: import("..").GraphQLOperationType;
3675
+ readonly keywords?: readonly string[];
3660
3676
  readonly name?: string;
3661
3677
  readonly sensitive?: boolean;
3662
3678
  readonly variables?: readonly string[];
@@ -3882,7 +3898,8 @@ export declare const addRiskApiDataToAsyncActivityCommonDetails: <Req, Res>(deta
3882
3898
  readonly is_hype_sale?: boolean;
3883
3899
  };
3884
3900
  readonly graphqlData?: readonly {
3885
- readonly type: import("..").GraphQLOperationType;
3901
+ readonly type?: import("..").GraphQLOperationType;
3902
+ readonly keywords?: readonly string[];
3886
3903
  readonly name?: string;
3887
3904
  readonly sensitive?: boolean;
3888
3905
  readonly variables?: readonly string[];
@@ -4039,7 +4056,8 @@ export declare const addRiskApiDataToAsyncActivityCommonDetails: <Req, Res>(deta
4039
4056
  readonly is_hype_sale?: boolean;
4040
4057
  };
4041
4058
  readonly graphqlData?: readonly {
4042
- readonly type: import("..").GraphQLOperationType;
4059
+ readonly type?: import("..").GraphQLOperationType;
4060
+ readonly keywords?: readonly string[];
4043
4061
  readonly name?: string;
4044
4062
  readonly sensitive?: boolean;
4045
4063
  readonly variables?: readonly string[];
@@ -4322,7 +4340,8 @@ export declare const addResponseDataToAsyncActivityCommonDetails: <Req, Res>(det
4322
4340
  readonly is_hype_sale?: boolean;
4323
4341
  };
4324
4342
  readonly graphqlData?: readonly {
4325
- readonly type: import("..").GraphQLOperationType;
4343
+ readonly type?: import("..").GraphQLOperationType;
4344
+ readonly keywords?: readonly string[];
4326
4345
  readonly name?: string;
4327
4346
  readonly sensitive?: boolean;
4328
4347
  readonly variables?: readonly string[];
@@ -4548,7 +4567,8 @@ export declare const addResponseDataToAsyncActivityCommonDetails: <Req, Res>(det
4548
4567
  readonly is_hype_sale?: boolean;
4549
4568
  };
4550
4569
  readonly graphqlData?: readonly {
4551
- readonly type: import("..").GraphQLOperationType;
4570
+ readonly type?: import("..").GraphQLOperationType;
4571
+ readonly keywords?: readonly string[];
4552
4572
  readonly name?: string;
4553
4573
  readonly sensitive?: boolean;
4554
4574
  readonly variables?: readonly string[];
@@ -4705,7 +4725,8 @@ export declare const addResponseDataToAsyncActivityCommonDetails: <Req, Res>(det
4705
4725
  readonly is_hype_sale?: boolean;
4706
4726
  };
4707
4727
  readonly graphqlData?: readonly {
4708
- readonly type: import("..").GraphQLOperationType;
4728
+ readonly type?: import("..").GraphQLOperationType;
4729
+ readonly keywords?: readonly string[];
4709
4730
  readonly name?: string;
4710
4731
  readonly sensitive?: boolean;
4711
4732
  readonly variables?: readonly string[];
@@ -4988,7 +5009,8 @@ export declare const createPageRequestedActivityDetails: <Req, Res>(context: {
4988
5009
  readonly is_hype_sale?: boolean;
4989
5010
  };
4990
5011
  readonly graphqlData?: readonly {
4991
- readonly type: import("..").GraphQLOperationType;
5012
+ readonly type?: import("..").GraphQLOperationType;
5013
+ readonly keywords?: readonly string[];
4992
5014
  readonly name?: string;
4993
5015
  readonly sensitive?: boolean;
4994
5016
  readonly variables?: readonly string[];
@@ -5214,7 +5236,8 @@ export declare const createPageRequestedActivityDetails: <Req, Res>(context: {
5214
5236
  readonly is_hype_sale?: boolean;
5215
5237
  };
5216
5238
  readonly graphqlData?: readonly {
5217
- readonly type: import("..").GraphQLOperationType;
5239
+ readonly type?: import("..").GraphQLOperationType;
5240
+ readonly keywords?: readonly string[];
5218
5241
  readonly name?: string;
5219
5242
  readonly sensitive?: boolean;
5220
5243
  readonly variables?: readonly string[];
@@ -5371,7 +5394,8 @@ export declare const createPageRequestedActivityDetails: <Req, Res>(context: {
5371
5394
  readonly is_hype_sale?: boolean;
5372
5395
  };
5373
5396
  readonly graphqlData?: readonly {
5374
- readonly type: import("..").GraphQLOperationType;
5397
+ readonly type?: import("..").GraphQLOperationType;
5398
+ readonly keywords?: readonly string[];
5375
5399
  readonly name?: string;
5376
5400
  readonly sensitive?: boolean;
5377
5401
  readonly variables?: readonly string[];
@@ -5654,7 +5678,8 @@ export declare const createBlockActivityDetails: <Req, Res>(context: {
5654
5678
  readonly is_hype_sale?: boolean;
5655
5679
  };
5656
5680
  readonly graphqlData?: readonly {
5657
- readonly type: import("..").GraphQLOperationType;
5681
+ readonly type?: import("..").GraphQLOperationType;
5682
+ readonly keywords?: readonly string[];
5658
5683
  readonly name?: string;
5659
5684
  readonly sensitive?: boolean;
5660
5685
  readonly variables?: readonly string[];
@@ -5880,7 +5905,8 @@ export declare const createBlockActivityDetails: <Req, Res>(context: {
5880
5905
  readonly is_hype_sale?: boolean;
5881
5906
  };
5882
5907
  readonly graphqlData?: readonly {
5883
- readonly type: import("..").GraphQLOperationType;
5908
+ readonly type?: import("..").GraphQLOperationType;
5909
+ readonly keywords?: readonly string[];
5884
5910
  readonly name?: string;
5885
5911
  readonly sensitive?: boolean;
5886
5912
  readonly variables?: readonly string[];
@@ -6037,7 +6063,8 @@ export declare const createBlockActivityDetails: <Req, Res>(context: {
6037
6063
  readonly is_hype_sale?: boolean;
6038
6064
  };
6039
6065
  readonly graphqlData?: readonly {
6040
- readonly type: import("..").GraphQLOperationType;
6066
+ readonly type?: import("..").GraphQLOperationType;
6067
+ readonly keywords?: readonly string[];
6041
6068
  readonly name?: string;
6042
6069
  readonly sensitive?: boolean;
6043
6070
  readonly variables?: readonly string[];
@@ -6320,7 +6347,8 @@ export declare const createAdditionalS2SActivityDetails: <Req, Res>({ ciSendRawU
6320
6347
  readonly is_hype_sale?: boolean;
6321
6348
  };
6322
6349
  readonly graphqlData?: readonly {
6323
- readonly type: import("..").GraphQLOperationType;
6350
+ readonly type?: import("..").GraphQLOperationType;
6351
+ readonly keywords?: readonly string[];
6324
6352
  readonly name?: string;
6325
6353
  readonly sensitive?: boolean;
6326
6354
  readonly variables?: readonly string[];
@@ -6546,7 +6574,8 @@ export declare const createAdditionalS2SActivityDetails: <Req, Res>({ ciSendRawU
6546
6574
  readonly is_hype_sale?: boolean;
6547
6575
  };
6548
6576
  readonly graphqlData?: readonly {
6549
- readonly type: import("..").GraphQLOperationType;
6577
+ readonly type?: import("..").GraphQLOperationType;
6578
+ readonly keywords?: readonly string[];
6550
6579
  readonly name?: string;
6551
6580
  readonly sensitive?: boolean;
6552
6581
  readonly variables?: readonly string[];
@@ -6703,7 +6732,8 @@ export declare const createAdditionalS2SActivityDetails: <Req, Res>({ ciSendRawU
6703
6732
  readonly is_hype_sale?: boolean;
6704
6733
  };
6705
6734
  readonly graphqlData?: readonly {
6706
- readonly type: import("..").GraphQLOperationType;
6735
+ readonly type?: import("..").GraphQLOperationType;
6736
+ readonly keywords?: readonly string[];
6707
6737
  readonly name?: string;
6708
6738
  readonly sensitive?: boolean;
6709
6739
  readonly variables?: readonly string[];
@@ -264,7 +264,8 @@ export declare const createBlockData: <Req, Res>(config: IConfiguration<Req, Res
264
264
  readonly is_hype_sale?: boolean;
265
265
  };
266
266
  readonly graphqlData?: readonly {
267
- readonly type: import("..").GraphQLOperationType;
267
+ readonly type?: import("..").GraphQLOperationType;
268
+ readonly keywords?: readonly string[];
268
269
  readonly name?: string;
269
270
  readonly sensitive?: boolean;
270
271
  readonly variables?: readonly string[];
@@ -490,7 +491,8 @@ export declare const createBlockData: <Req, Res>(config: IConfiguration<Req, Res
490
491
  readonly is_hype_sale?: boolean;
491
492
  };
492
493
  readonly graphqlData?: readonly {
493
- readonly type: import("..").GraphQLOperationType;
494
+ readonly type?: import("..").GraphQLOperationType;
495
+ readonly keywords?: readonly string[];
494
496
  readonly name?: string;
495
497
  readonly sensitive?: boolean;
496
498
  readonly variables?: readonly string[];
@@ -647,7 +649,8 @@ export declare const createBlockData: <Req, Res>(config: IConfiguration<Req, Res
647
649
  readonly is_hype_sale?: boolean;
648
650
  };
649
651
  readonly graphqlData?: readonly {
650
- readonly type: import("..").GraphQLOperationType;
652
+ readonly type?: import("..").GraphQLOperationType;
653
+ readonly keywords?: readonly string[];
651
654
  readonly name?: string;
652
655
  readonly sensitive?: boolean;
653
656
  readonly variables?: readonly string[];
@@ -7,6 +7,7 @@ import { CustomBlockResponseHeadersHandler, CustomPreflightHandler } from '../co
7
7
  import { ModuleMode } from '../utils';
8
8
  import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomLoginSuccessfulCallback, LoginSuccessfulReportingMethod } from '../products';
9
9
  import { CustomRequestFunction } from './CustomRequestFunction';
10
+ import { ExtractGraphQLKeywordsFunction } from '../graphql/ExtractGraphQLKeywordsFunction';
10
11
  export declare abstract class ConfigurationBase<Req, Res, ParamsType extends ConfigurationParams<Req, Res> = ConfigurationParams<Req, Res>> implements IConfiguration<Req, Res, ParamsType> {
11
12
  protected activeConfigParams: ParamsType;
12
13
  protected readonly staticConfigParams: ParamsType;
@@ -73,7 +74,9 @@ export declare abstract class ConfigurationBase<Req, Res, ParamsType extends Con
73
74
  get activityBatchTimeoutMs(): number;
74
75
  get graphqlEnabled(): boolean;
75
76
  get graphqlRoutes(): Array<string | RegExp>;
76
- get sensitiveGraphqlOperationNames(): string[];
77
+ get graphqlKeywords(): Array<string | RegExp>;
78
+ get extractGraphQLKeywords(): ExtractGraphQLKeywordsFunction<Req>;
79
+ get sensitiveGraphqlOperationNames(): Array<string | RegExp>;
77
80
  get sensitiveGraphqlOperationTypes(): string[];
78
81
  get enrichCustomParameters(): CustomParametersFunction<Req, Res>;
79
82
  get additionalActivityHandler(): AdditionalActivityHandler<Req, Res>;
@@ -6,6 +6,7 @@ import { ConfigurationParams, RemoteConfigurationParams } from './params';
6
6
  import { CustomPreflightHandler, CustomBlockResponseHeadersHandler } from '../cors';
7
7
  import { CredentialEndpointConfiguration, CredentialIntelligenceVersion, CustomLoginSuccessfulCallback, LoginSuccessfulReportingMethod } from '../products';
8
8
  import { CustomRequestFunction } from './CustomRequestFunction';
9
+ import { ExtractGraphQLKeywordsFunction } from '../graphql/ExtractGraphQLKeywordsFunction';
9
10
  export interface IConfiguration<Req, Res, ParamsType extends ConfigurationParams<Req, Res> = ConfigurationParams<Req, Res>> {
10
11
  /**
11
12
  * The application ID.
@@ -215,15 +216,26 @@ export interface IConfiguration<Req, Res, ParamsType extends ConfigurationParams
215
216
  */
216
217
  readonly graphqlRoutes: Array<string | RegExp>;
217
218
  /**
218
- * An array of GraphQL operation names that should trigger a risk API call
219
+ * An array of GraphQL operation names and keywords that should trigger a risk API call
219
220
  * even if a valid, unexpired, low-score risk cookie is present.
220
221
  */
221
- readonly sensitiveGraphqlOperationNames: string[];
222
+ readonly sensitiveGraphqlOperationNames: Array<string | RegExp>;
222
223
  /**
223
224
  * An array of GraphQL operation types (e.g., mutation) that should trigger a risk API call
224
225
  * even if a valid, unexpired, low-score risk cookie is present.
225
226
  */
226
227
  readonly sensitiveGraphqlOperationTypes: string[];
228
+ /**
229
+ * An array of strings or regular expressions representing important keywords in the GraphQL
230
+ * query that should be extracted by the enforcer.
231
+ */
232
+ readonly graphqlKeywords: Array<string | RegExp>;
233
+ /**
234
+ * A custom function that receives the GraphQL query as a string and returns the extracted
235
+ * keywords from the query. This function may be called multiple times if the HTTP request
236
+ * contains multiple GraphQL queries.
237
+ */
238
+ readonly extractGraphQLKeywords: ExtractGraphQLKeywordsFunction<Req>;
227
239
  /**
228
240
  * A function returning CustomParameters that will be added to the enforcer activities.
229
241
  */
@@ -6,6 +6,7 @@ import { AdditionalActivityHandler } from '../../additional_activity_handler';
6
6
  import { CustomParametersFunction } from '../../custom_parameters';
7
7
  import { CustomBlockResponseHeadersHandler, CustomPreflightHandler } from '../../cors';
8
8
  import { CustomRequestFunction } from '../CustomRequestFunction';
9
+ import { ExtractGraphQLKeywordsFunction } from '../../graphql/ExtractGraphQLKeywordsFunction';
9
10
  export type CommonConfigurationParams<Req, Res> = {
10
11
  px_s2s_timeout?: number;
11
12
  px_backend_url?: string;
@@ -64,7 +65,8 @@ export type CommonConfigurationParams<Req, Res> = {
64
65
  px_jwt_header_additional_field_names?: string[];
65
66
  px_graphql_enabled?: boolean;
66
67
  px_graphql_routes?: Array<string | RegExp>;
67
- px_sensitive_graphql_operation_names?: string[];
68
+ px_graphql_keywords?: Array<string | RegExp>;
69
+ px_sensitive_graphql_operation_names?: Array<string | RegExp>;
68
70
  px_sensitive_graphql_operation_types?: Array<'query' | 'mutation' | 'subscription' | GraphQLOperationType>;
69
71
  px_cors_support_enabled?: boolean;
70
72
  px_cors_preflight_request_filter_enabled?: boolean;
@@ -81,4 +83,5 @@ export type CommonConfigurationParams<Req, Res> = {
81
83
  px_custom_is_monitored_request?: CustomRequestFunction<Req>;
82
84
  px_custom_is_enforced_request?: CustomRequestFunction<Req>;
83
85
  px_custom_is_filtered_request?: CustomRequestFunction<Req>;
86
+ px_extract_graphql_keywords?: ExtractGraphQLKeywordsFunction<Req>;
84
87
  };
@@ -1,19 +1,28 @@
1
1
  import { ReadonlyContext } from '../context';
2
2
  import { IConfiguration } from '../config';
3
+ import { IIncomingRequest } from '../http';
3
4
  import { IGraphQLParser } from './IGraphQLParser';
4
- import { GraphQLData } from './model';
5
+ import { GraphQLData, GraphQLOperation, GraphQLOperationType } from './model';
5
6
  export declare class DefaultGraphQLParser<Req, Res> implements IGraphQLParser<Req, Res> {
6
- private readonly graphqlRoutes;
7
- private readonly sensitiveOperationTypes;
8
- private readonly sensitiveOperationNames;
7
+ protected readonly config: IConfiguration<Req, Res>;
8
+ protected readonly maxCharactersInGraphqlKeyword: number;
9
+ protected readonly maxGraphqlKeywordCount: number;
9
10
  constructor(config: IConfiguration<Req, Res>);
10
11
  isGraphQLRequest({ requestData }: ReadonlyContext<Req, Res>): boolean;
11
12
  parseGraphQLRequest(context: ReadonlyContext<Req, Res>): Promise<GraphQLData[]>;
12
- private getGraphQLOperationsFromBody;
13
- private parseGraphQLOperations;
14
- private parseGraphQlOperation;
15
- private getOperationNameToTypeMap;
16
- private getGraphQLData;
17
- private isSensitiveOperation;
18
- private extractGraphQLVariableNames;
13
+ protected getGraphQLOperationsFromBody(request: IIncomingRequest<Req>, context: ReadonlyContext<Req, Res>): Promise<Array<GraphQLOperation>>;
14
+ protected parseGraphQLOperations(operations: GraphQLOperation[], context: ReadonlyContext<Req, Res>): Promise<GraphQLData[]>;
15
+ protected parseGraphQLOperation(operation: GraphQLOperation, context: ReadonlyContext<Req, Res>): Promise<GraphQLData | null>;
16
+ protected getOperationNameToTypeMap(query: string): Record<string, GraphQLOperationType> | null;
17
+ protected getGraphQLData(operationNameToTypeMap: Record<string, GraphQLOperationType>, operation: GraphQLOperation, context: ReadonlyContext<Req, Res>): Promise<GraphQLData>;
18
+ protected getOperationType(operation: GraphQLOperation, operationName: string, operationNameToTypeMap: Record<string, GraphQLOperationType>): GraphQLOperationType;
19
+ protected isGraphqlQueryShorthand(query: string): boolean;
20
+ protected getOperationName(operationNameToTypeMap: Record<string, GraphQLOperationType>, operation: GraphQLOperation): string;
21
+ protected getQueryKeywords(query: string, context: ReadonlyContext<Req, Res>): Promise<string[]>;
22
+ protected cleanKeywords(keywords: string[]): string[];
23
+ protected getQueryKeywordsFromCustomFunction(query: string, context: ReadonlyContext<Req, Res>): Promise<string[]>;
24
+ protected getQueryKeywordsFromArray(query: string, context: ReadonlyContext<Req, Res>): string[];
25
+ protected isSensitiveOperation(operationName: string, operationType: GraphQLOperationType, keywords: string[]): boolean;
26
+ protected extractGraphQLVariableNames(variables: Record<string, unknown>): string[];
27
+ protected toGlobalRegExp(pattern: string | RegExp): RegExp;
19
28
  }
@@ -0,0 +1 @@
1
+ export type ExtractGraphQLKeywordsFunction<Req> = (graphqlQuery: string) => string[] | Promise<string[]>;
@@ -1,3 +1,4 @@
1
1
  export * from './model';
2
2
  export * from './IGraphQLParser';
3
3
  export * from './DefaultGraphQLParser';
4
+ export * from './ExtractGraphQLKeywordsFunction';
@@ -1,6 +1,7 @@
1
1
  import { GraphQLOperationType } from './GraphQLOperationType';
2
2
  export type GraphQLData = {
3
- type: GraphQLOperationType;
3
+ type?: GraphQLOperationType;
4
+ keywords?: string[];
4
5
  name?: string;
5
6
  sensitive?: boolean;
6
7
  variables?: string[];
@@ -264,7 +264,8 @@ export declare namespace MonitoredRequestUtils {
264
264
  readonly is_hype_sale?: boolean;
265
265
  };
266
266
  readonly graphqlData?: readonly {
267
- readonly type: import("..").GraphQLOperationType;
267
+ readonly type?: import("..").GraphQLOperationType;
268
+ readonly keywords?: readonly string[];
268
269
  readonly name?: string;
269
270
  readonly sensitive?: boolean;
270
271
  readonly variables?: readonly string[];
@@ -490,7 +491,8 @@ export declare namespace MonitoredRequestUtils {
490
491
  readonly is_hype_sale?: boolean;
491
492
  };
492
493
  readonly graphqlData?: readonly {
493
- readonly type: import("..").GraphQLOperationType;
494
+ readonly type?: import("..").GraphQLOperationType;
495
+ readonly keywords?: readonly string[];
494
496
  readonly name?: string;
495
497
  readonly sensitive?: boolean;
496
498
  readonly variables?: readonly string[];
@@ -647,7 +649,8 @@ export declare namespace MonitoredRequestUtils {
647
649
  readonly is_hype_sale?: boolean;
648
650
  };
649
651
  readonly graphqlData?: readonly {
650
- readonly type: import("..").GraphQLOperationType;
652
+ readonly type?: import("..").GraphQLOperationType;
653
+ readonly keywords?: readonly string[];
651
654
  readonly name?: string;
652
655
  readonly sensitive?: boolean;
653
656
  readonly variables?: readonly string[];
@@ -932,7 +935,8 @@ export declare namespace MonitoredRequestUtils {
932
935
  readonly is_hype_sale?: boolean;
933
936
  };
934
937
  readonly graphqlData?: readonly {
935
- readonly type: import("..").GraphQLOperationType;
938
+ readonly type?: import("..").GraphQLOperationType;
939
+ readonly keywords?: readonly string[];
936
940
  readonly name?: string;
937
941
  readonly sensitive?: boolean;
938
942
  readonly variables?: readonly string[];
@@ -1158,7 +1162,8 @@ export declare namespace MonitoredRequestUtils {
1158
1162
  readonly is_hype_sale?: boolean;
1159
1163
  };
1160
1164
  readonly graphqlData?: readonly {
1161
- readonly type: import("..").GraphQLOperationType;
1165
+ readonly type?: import("..").GraphQLOperationType;
1166
+ readonly keywords?: readonly string[];
1162
1167
  readonly name?: string;
1163
1168
  readonly sensitive?: boolean;
1164
1169
  readonly variables?: readonly string[];
@@ -1315,7 +1320,8 @@ export declare namespace MonitoredRequestUtils {
1315
1320
  readonly is_hype_sale?: boolean;
1316
1321
  };
1317
1322
  readonly graphqlData?: readonly {
1318
- readonly type: import("..").GraphQLOperationType;
1323
+ readonly type?: import("..").GraphQLOperationType;
1324
+ readonly keywords?: readonly string[];
1319
1325
  readonly name?: string;
1320
1326
  readonly sensitive?: boolean;
1321
1327
  readonly variables?: readonly string[];
@@ -1598,7 +1604,8 @@ export declare namespace MonitoredRequestUtils {
1598
1604
  readonly is_hype_sale?: boolean;
1599
1605
  };
1600
1606
  readonly graphqlData?: readonly {
1601
- readonly type: import("..").GraphQLOperationType;
1607
+ readonly type?: import("..").GraphQLOperationType;
1608
+ readonly keywords?: readonly string[];
1602
1609
  readonly name?: string;
1603
1610
  readonly sensitive?: boolean;
1604
1611
  readonly variables?: readonly string[];
@@ -1824,7 +1831,8 @@ export declare namespace MonitoredRequestUtils {
1824
1831
  readonly is_hype_sale?: boolean;
1825
1832
  };
1826
1833
  readonly graphqlData?: readonly {
1827
- readonly type: import("..").GraphQLOperationType;
1834
+ readonly type?: import("..").GraphQLOperationType;
1835
+ readonly keywords?: readonly string[];
1828
1836
  readonly name?: string;
1829
1837
  readonly sensitive?: boolean;
1830
1838
  readonly variables?: readonly string[];
@@ -1981,7 +1989,8 @@ export declare namespace MonitoredRequestUtils {
1981
1989
  readonly is_hype_sale?: boolean;
1982
1990
  };
1983
1991
  readonly graphqlData?: readonly {
1984
- readonly type: import("..").GraphQLOperationType;
1992
+ readonly type?: import("..").GraphQLOperationType;
1993
+ readonly keywords?: readonly string[];
1985
1994
  readonly name?: string;
1986
1995
  readonly sensitive?: boolean;
1987
1996
  readonly variables?: readonly string[];
@@ -266,7 +266,8 @@ export declare namespace PXHDUtils {
266
266
  readonly is_hype_sale?: boolean;
267
267
  };
268
268
  readonly graphqlData?: readonly {
269
- readonly type: import("..").GraphQLOperationType;
269
+ readonly type?: import("..").GraphQLOperationType;
270
+ readonly keywords?: readonly string[];
270
271
  readonly name?: string;
271
272
  readonly sensitive?: boolean;
272
273
  readonly variables?: readonly string[];
@@ -492,7 +493,8 @@ export declare namespace PXHDUtils {
492
493
  readonly is_hype_sale?: boolean;
493
494
  };
494
495
  readonly graphqlData?: readonly {
495
- readonly type: import("..").GraphQLOperationType;
496
+ readonly type?: import("..").GraphQLOperationType;
497
+ readonly keywords?: readonly string[];
496
498
  readonly name?: string;
497
499
  readonly sensitive?: boolean;
498
500
  readonly variables?: readonly string[];
@@ -649,7 +651,8 @@ export declare namespace PXHDUtils {
649
651
  readonly is_hype_sale?: boolean;
650
652
  };
651
653
  readonly graphqlData?: readonly {
652
- readonly type: import("..").GraphQLOperationType;
654
+ readonly type?: import("..").GraphQLOperationType;
655
+ readonly keywords?: readonly string[];
653
656
  readonly name?: string;
654
657
  readonly sensitive?: boolean;
655
658
  readonly variables?: readonly string[];
@@ -932,7 +935,8 @@ export declare namespace PXHDUtils {
932
935
  readonly is_hype_sale?: boolean;
933
936
  };
934
937
  readonly graphqlData?: readonly {
935
- readonly type: import("..").GraphQLOperationType;
938
+ readonly type?: import("..").GraphQLOperationType;
939
+ readonly keywords?: readonly string[];
936
940
  readonly name?: string;
937
941
  readonly sensitive?: boolean;
938
942
  readonly variables?: readonly string[];
@@ -1158,7 +1162,8 @@ export declare namespace PXHDUtils {
1158
1162
  readonly is_hype_sale?: boolean;
1159
1163
  };
1160
1164
  readonly graphqlData?: readonly {
1161
- readonly type: import("..").GraphQLOperationType;
1165
+ readonly type?: import("..").GraphQLOperationType;
1166
+ readonly keywords?: readonly string[];
1162
1167
  readonly name?: string;
1163
1168
  readonly sensitive?: boolean;
1164
1169
  readonly variables?: readonly string[];
@@ -1315,7 +1320,8 @@ export declare namespace PXHDUtils {
1315
1320
  readonly is_hype_sale?: boolean;
1316
1321
  };
1317
1322
  readonly graphqlData?: readonly {
1318
- readonly type: import("..").GraphQLOperationType;
1323
+ readonly type?: import("..").GraphQLOperationType;
1324
+ readonly keywords?: readonly string[];
1319
1325
  readonly name?: string;
1320
1326
  readonly sensitive?: boolean;
1321
1327
  readonly variables?: readonly string[];
@@ -265,7 +265,8 @@ export declare namespace SensitiveRequestUtils {
265
265
  readonly is_hype_sale?: boolean;
266
266
  };
267
267
  readonly graphqlData?: readonly {
268
- readonly type: import("../graphql").GraphQLOperationType;
268
+ readonly type?: import("../graphql").GraphQLOperationType;
269
+ readonly keywords?: readonly string[];
269
270
  readonly name?: string;
270
271
  readonly sensitive?: boolean;
271
272
  readonly variables?: readonly string[];
@@ -491,7 +492,8 @@ export declare namespace SensitiveRequestUtils {
491
492
  readonly is_hype_sale?: boolean;
492
493
  };
493
494
  readonly graphqlData?: readonly {
494
- readonly type: import("../graphql").GraphQLOperationType;
495
+ readonly type?: import("../graphql").GraphQLOperationType;
496
+ readonly keywords?: readonly string[];
495
497
  readonly name?: string;
496
498
  readonly sensitive?: boolean;
497
499
  readonly variables?: readonly string[];
@@ -648,7 +650,8 @@ export declare namespace SensitiveRequestUtils {
648
650
  readonly is_hype_sale?: boolean;
649
651
  };
650
652
  readonly graphqlData?: readonly {
651
- readonly type: import("../graphql").GraphQLOperationType;
653
+ readonly type?: import("../graphql").GraphQLOperationType;
654
+ readonly keywords?: readonly string[];
652
655
  readonly name?: string;
653
656
  readonly sensitive?: boolean;
654
657
  readonly variables?: readonly string[];
@@ -933,7 +936,8 @@ export declare namespace SensitiveRequestUtils {
933
936
  readonly is_hype_sale?: boolean;
934
937
  };
935
938
  readonly graphqlData?: readonly {
936
- readonly type: import("../graphql").GraphQLOperationType;
939
+ readonly type?: import("../graphql").GraphQLOperationType;
940
+ readonly keywords?: readonly string[];
937
941
  readonly name?: string;
938
942
  readonly sensitive?: boolean;
939
943
  readonly variables?: readonly string[];
@@ -1159,7 +1163,8 @@ export declare namespace SensitiveRequestUtils {
1159
1163
  readonly is_hype_sale?: boolean;
1160
1164
  };
1161
1165
  readonly graphqlData?: readonly {
1162
- readonly type: import("../graphql").GraphQLOperationType;
1166
+ readonly type?: import("../graphql").GraphQLOperationType;
1167
+ readonly keywords?: readonly string[];
1163
1168
  readonly name?: string;
1164
1169
  readonly sensitive?: boolean;
1165
1170
  readonly variables?: readonly string[];
@@ -1316,7 +1321,8 @@ export declare namespace SensitiveRequestUtils {
1316
1321
  readonly is_hype_sale?: boolean;
1317
1322
  };
1318
1323
  readonly graphqlData?: readonly {
1319
- readonly type: import("../graphql").GraphQLOperationType;
1324
+ readonly type?: import("../graphql").GraphQLOperationType;
1325
+ readonly keywords?: readonly string[];
1320
1326
  readonly name?: string;
1321
1327
  readonly sensitive?: boolean;
1322
1328
  readonly variables?: readonly string[];
@@ -10,4 +10,4 @@ export declare const PUSH_DATA_HMAC_HEADER_NAME = "x-px-pushdata";
10
10
  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
- export declare const CORE_MODULE_VERSION = "JS Core 0.19.0";
13
+ export declare const CORE_MODULE_VERSION = "JS Core 0.20.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "perimeterx-js-core",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "typesVersions": {