@netacea/cloudfront 5.2.53 → 6.0.1

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.
@@ -1,966 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /* eslint-disable max-lines */
4
- const netaceaintegrationbase_1 = require("@netacea/netaceaintegrationbase");
5
- const pack = require("../package.json");
6
- const axios_1 = require("axios");
7
- const kinesisingest_1 = require("@netacea/kinesisingest");
8
- const cookieEncryption_1 = require("./cookieEncryption");
9
- const captchaGet_1 = require("./captchaGet");
10
- const dynamicContentType_1 = require("./dynamicContentType");
11
- const retrieveClientIp_1 = require("./retrieveClientIp");
12
- const headers_1 = require("./headers");
13
- const { extractCookieAttr, extractAndRemoveCookieAttr, removeDuplicateAttrs } = netaceaintegrationbase_1.lib.cookie.attributes;
14
- const parseSetCookie = netaceaintegrationbase_1.lib.cookie.parse.parseSetCookie;
15
- const { configureCookiesDomain } = netaceaintegrationbase_1.lib.cookie.attributes;
16
- const { matchMap, mitigationTypes, mitigateMap, bestMitigationMap, bestMitigationCaptchaMap, captchaMap, netaceaHeaders } = netaceaintegrationbase_1.dictionary;
17
- const ONE_HOUR_IN_SECONDS = 60 * 60;
18
- const ONE_DAY_IN_SECONDS = ONE_HOUR_IN_SECONDS * 24;
19
- class Cloudfront {
20
- // eslint-disable-next-line complexity, max-lines-per-function
21
- constructor(options) {
22
- var _a, _b, _c, _d, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
23
- this.ingestEnabled = true;
24
- this.encryptedCookies = [];
25
- // Only kinesis ingest is currently supported.
26
- options.ingestType = netaceaintegrationbase_1.NetaceaIngestType.KINESIS;
27
- if (options.kinesis === undefined) {
28
- // eslint-disable-next-line no-console
29
- console.warn([
30
- 'NETACEA :: Please move kinesis params to "kinesis" object in config.',
31
- 'Backwards compatibility will soon be removed.'
32
- ].join(' '));
33
- options.kinesis = {
34
- kinesisStreamName: options.kinesisStreamName,
35
- kinesisAccessKey: options.kinesisAccessKey,
36
- kinesisSecretKey: options.kinesisSecretKey,
37
- maxLogAgeSeconds: 1
38
- };
39
- }
40
- if (options.apiKey === null || options.apiKey === undefined) {
41
- throw new Error('apiKey is a required parameter');
42
- }
43
- this.apiKey = options.apiKey;
44
- this.secretKey = options.secretKey;
45
- this.mitigationServiceUrl = (_a = options.mitigationServiceUrl) !== null && _a !== void 0 ? _a : 'https://mitigations.netacea.net';
46
- this.ingestServiceUrl = (_b = options.ingestServiceUrl) !== null && _b !== void 0 ? _b : 'https://ingest.netacea.net';
47
- this.mitigationType = (_c = options.mitigationType) !== null && _c !== void 0 ? _c : netaceaintegrationbase_1.NetaceaMitigationType.INGEST;
48
- this.ingestType = (_d = options.ingestType) !== null && _d !== void 0 ? _d : netaceaintegrationbase_1.NetaceaIngestType.HTTP;
49
- this.logVersion = (_f = options.logVersion) !== null && _f !== void 0 ? _f : netaceaintegrationbase_1.NetaceaLogVersion.V1;
50
- if (this.ingestType === netaceaintegrationbase_1.NetaceaIngestType.KINESIS) {
51
- if (options.kinesis === undefined) {
52
- // eslint-disable-next-line no-console
53
- console.warn(`NETACEA WARN: no kinesis args provided, when ingestType is ${this.ingestType}`);
54
- }
55
- else {
56
- this.kinesis = new kinesisingest_1.default({
57
- ...options.kinesis,
58
- apiKey: this.apiKey
59
- });
60
- }
61
- }
62
- if (options.captchaSiteKey !== undefined || options.captchaSecretKey !== undefined) {
63
- this.captchaSiteKey = options.captchaSiteKey;
64
- this.captchaSecretKey = options.captchaSecretKey;
65
- }
66
- this.timeout = (0, netaceaintegrationbase_1.correctTimeout)((_g = options.timeout) !== null && _g !== void 0 ? _g : 3000);
67
- this.netaceaCookieName = (_h = options.netaceaCookieName) !== null && _h !== void 0 ? _h : '_mitata';
68
- this.netaceaCaptchaCookieName = (_j = options.netaceaCaptchaCookieName) !== null && _j !== void 0 ? _j : '_mitatacaptcha';
69
- this.netaceaCaptchaPath = options.netaceaCaptchaPath;
70
- this.dynamicCaptchaContentType = (_k = options.dynamicCaptchaContentType) !== null && _k !== void 0 ? _k : false;
71
- const configuredCookieAttributes = configureCookiesDomain((_l = options.netaceaCookieAttributes) !== null && _l !== void 0 ? _l : '', (_m = options.netaceaCaptchaCookieAttributes) !== null && _m !== void 0 ? _m : '');
72
- this.netaceaCookieAttributes = (_o = configuredCookieAttributes.cookieAttributes) !== null && _o !== void 0 ? _o : '';
73
- this.netaceaCaptchaCookieAttributes = (_p = configuredCookieAttributes.captchaCookieAttributes) !== null && _p !== void 0 ? _p : '';
74
- this.captchaHeader = options.captchaHeader;
75
- this.ipHeaderName = (_r = (_q = options.ipHeaderName) === null || _q === void 0 ? void 0 : _q.toLowerCase()) === null || _r === void 0 ? void 0 : _r.trim();
76
- this.encryptedCookies = [
77
- this.netaceaCookieName,
78
- this.netaceaCaptchaCookieName
79
- ];
80
- this.mitataCookieExpirySeconds = (0, netaceaintegrationbase_1.configureMitataExpiry)(this.mitigationType, (_s = options.netaceaCookieExpirySeconds) !== null && _s !== void 0 ? _s : options.mitataCookieExpirySeconds);
81
- this.ingestEnabled = (_t = options.ingestEnabled) !== null && _t !== void 0 ? _t : true;
82
- this.cookieEncryptionKey = options.cookieEncryptionKey;
83
- }
84
- async run(cloudfrontEvent) {
85
- try {
86
- const { uri, method } = this.getRequestResponseFromEvent(cloudfrontEvent).request;
87
- if (this.isUrlCaptchaGet(uri, method)) {
88
- const response = await (0, captchaGet_1.handleCaptchaGetRequest)({
89
- event: cloudfrontEvent,
90
- secretKey: this.secretKey,
91
- mitigationCallFn: this.makeMitigateAPICall.bind(this),
92
- composeResultFn: this.composeResult.bind(this),
93
- cookieEncryptionKey: this.cookieEncryptionKey,
94
- netaceaCookieName: this.netaceaCookieName,
95
- netaceaCaptchaCookieName: this.netaceaCaptchaCookieName,
96
- ipHeaderName: this.ipHeaderName
97
- });
98
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
99
- this.ingest(response);
100
- return response;
101
- }
102
- const netaceaResult = await this.runMitigation(cloudfrontEvent);
103
- const { request } = this.getRequestResponseFromEvent(cloudfrontEvent);
104
- this.addNetaceaCookiesToRequest(request, netaceaResult);
105
- request.headers[Cloudfront.NetaceaTrueUserAgentHeader] = [{
106
- key: Cloudfront.NetaceaTrueUserAgentHeader,
107
- value: this.getValueFromHeaderOrDefault(request.headers, 'user-agent', '-')
108
- }];
109
- if ((netaceaResult === null || netaceaResult === void 0 ? void 0 : netaceaResult.response) !== undefined) {
110
- cloudfrontEvent.Records[0].cf.response = netaceaResult.response;
111
- }
112
- return cloudfrontEvent;
113
- }
114
- catch (err) {
115
- // eslint-disable-next-line no-console
116
- console.error('Netacea FailOpen - ', err.message);
117
- return cloudfrontEvent;
118
- }
119
- }
120
- async makeRequest({ host, path, method, body, headers, timeout }) {
121
- const url = `${host}${path}`;
122
- const data = await axios_1.default.request({
123
- url,
124
- data: body,
125
- headers,
126
- method,
127
- timeout,
128
- transformResponse: (data) => data
129
- });
130
- return {
131
- headers: data.headers,
132
- status: data.status,
133
- body: data.data
134
- };
135
- }
136
- // eslint-disable-next-line max-lines-per-function, complexity
137
- async mitigate(cloudfrontEvent) {
138
- var _a;
139
- try {
140
- const { netaceaResult, request } = await this.getMitigationResponse(cloudfrontEvent);
141
- let res;
142
- if (netaceaResult.mitigated) {
143
- const headers = {
144
- 'set-cookie': []
145
- };
146
- for (const cookie of netaceaResult.setCookie) {
147
- headers['set-cookie'] = (_a = headers['set-cookie']) !== null && _a !== void 0 ? _a : [];
148
- headers['set-cookie'].push({
149
- key: 'set-cookie',
150
- value: cookie
151
- });
152
- }
153
- if (netaceaResult.mitigation === 'captcha' && this.captchaHeader !== undefined) {
154
- headers[this.captchaHeader.name] = [{
155
- key: this.captchaHeader.name,
156
- value: this.captchaHeader.value
157
- }];
158
- }
159
- const responseStatus = this.isUrlCaptchaPost(request.uri, request.method)
160
- ? {
161
- status: '200',
162
- statusDescription: 'OK',
163
- body: ''
164
- }
165
- : {
166
- status: '403',
167
- statusDescription: 'Forbidden',
168
- body: 'Forbidden'
169
- };
170
- res = {
171
- headers,
172
- ...responseStatus
173
- };
174
- let len = 0;
175
- if (netaceaResult.body !== undefined && netaceaResult.body.length > 0) {
176
- len = netaceaResult.body.length;
177
- const isCaptchaJson = (0, dynamicContentType_1.isCaptchaJsonResponse)(netaceaResult.body);
178
- // lambdas won't let you set body on 403 status codes
179
- res.status = isCaptchaJson ? '403' : '200';
180
- res.statusDescription = isCaptchaJson ? 'Forbidden' : 'OK';
181
- res.body = netaceaResult.body;
182
- res.bodyEncoding = 'text';
183
- }
184
- // We have to ingest here because viewer response doesn't get called
185
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
186
- this.ingest({
187
- Records: [{
188
- cf: {
189
- request,
190
- response: {
191
- status: res.status,
192
- headers: {
193
- 'content-length': [
194
- {
195
- key: 'content-length',
196
- value: len.toString()
197
- }
198
- ],
199
- 'set-cookie': netaceaResult.setCookie.map(cookie => {
200
- return {
201
- key: 'set-cookie',
202
- value: cookie
203
- };
204
- })
205
- }
206
- }
207
- }
208
- }]
209
- });
210
- }
211
- this.addNetaceaCookiesToRequest(request, netaceaResult);
212
- return {
213
- response: res,
214
- sessionStatus: netaceaResult.sessionStatus,
215
- setCookie: netaceaResult.setCookie
216
- };
217
- }
218
- catch (err) {
219
- // eslint-disable-next-line no-console
220
- console.error('Netacea FailOpen Error: ', err);
221
- return {
222
- sessionStatus: ''
223
- };
224
- }
225
- }
226
- async inject(cloudfrontEvent) {
227
- try {
228
- const { netaceaResult } = await this.getMitigationResponse(cloudfrontEvent);
229
- return {
230
- injectHeaders: netaceaResult.injectHeaders,
231
- sessionStatus: netaceaResult.sessionStatus,
232
- setCookie: netaceaResult.setCookie
233
- };
234
- }
235
- catch (err) {
236
- // eslint-disable-next-line no-console
237
- console.error('Netacea FailOpen Error: ', err);
238
- return {
239
- sessionStatus: '',
240
- injectHeaders: undefined,
241
- setCookie: undefined
242
- };
243
- }
244
- }
245
- // eslint-disable-next-line max-lines-per-function
246
- async ingest(cloudfrontEvent) {
247
- var _a, _b, _c;
248
- if (!this.ingestEnabled) {
249
- return;
250
- }
251
- const { request, response } = this.getRequestResponseFromEvent(cloudfrontEvent);
252
- if (response === undefined) {
253
- throw new Error('Cloudfront response is required to ingest');
254
- }
255
- const setCookie = this.getMitataValueFromHeaderOrDefault(response.headers, 'set-cookie');
256
- const cookieString = setCookie !== ''
257
- ? setCookie
258
- : this.getMitataValueFromHeaderOrDefault(request.headers, 'cookie');
259
- let mitata = (_a = await this.readCookie(this.netaceaCookieName, cookieString)) !== null && _a !== void 0 ? _a : '';
260
- if (mitata === undefined || mitata === '') {
261
- const cookieString = this.getMitataValueFromHeaderOrDefault(request.headers, 'cookie');
262
- mitata = (_b = await this.readCookie(this.netaceaCookieName, cookieString)) !== null && _b !== void 0 ? _b : '';
263
- }
264
- let match = 0;
265
- let mitigate = 0;
266
- let captcha = 0;
267
- const mitataBits = (0, netaceaintegrationbase_1.matchMitataCookie)(mitata);
268
- if (mitataBits !== undefined) {
269
- match = mitataBits.match;
270
- mitigate = mitataBits.mitigate;
271
- captcha = mitataBits.captcha;
272
- }
273
- const isCaptchaPass = this.shouldSetCaptchaPass(cloudfrontEvent);
274
- const { sessionStatus } = this.findBestMitigation(match, mitigate, captcha, isCaptchaPass);
275
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
276
- await this.callIngest({
277
- bytesSent: this.getValueFromHeaderOrDefault(response.headers, 'content-length', '0'),
278
- ip: (0, retrieveClientIp_1.retrieveClientIp)(cloudfrontEvent, this.ipHeaderName),
279
- method: request.method,
280
- path: request.uri,
281
- protocol: (_c = request.httpVersion) !== null && _c !== void 0 ? _c : 'HTTP/1.1',
282
- referer: this.getValueFromHeaderOrDefault(request.headers, 'referer'),
283
- requestTime: '0',
284
- status: response.status,
285
- userAgent: this.getValueFromHeaderOrDefault(request.headers, Cloudfront.NetaceaTrueUserAgentHeader, this.getValueFromHeaderOrDefault(request.headers, 'user-agent')),
286
- mitataCookie: mitata,
287
- sessionStatus,
288
- integrationType: pack.name.replace('@netacea/', ''),
289
- integrationVersion: pack.version,
290
- xForwardedFor: this.getValueFromHeaderOrDefault(request.headers, 'x-forwarded-for')
291
- });
292
- }
293
- addNetaceaCookiesToResponse(cloudfrontEvent) {
294
- const { response, request } = this.getRequestResponseFromEvent(cloudfrontEvent);
295
- if (response === undefined) {
296
- throw new Error('Response required to add cookies to response');
297
- }
298
- const headers = request.headers[Cloudfront.NetaceaCookieHeader];
299
- if (headers !== undefined) {
300
- let setCookieIncludesNetaceaCookies = false;
301
- if (response.headers['set-cookie'] === undefined) {
302
- response.headers['set-cookie'] = [];
303
- }
304
- else {
305
- setCookieIncludesNetaceaCookies = response.headers['set-cookie'].find(v => {
306
- return v.value.includes(this.netaceaCookieName) || v.value.includes(this.netaceaCaptchaCookieName);
307
- }) !== undefined;
308
- }
309
- if (!setCookieIncludesNetaceaCookies) {
310
- for (const header of headers) {
311
- response.headers['set-cookie'].push({
312
- key: 'set-cookie',
313
- value: header.value
314
- });
315
- }
316
- }
317
- }
318
- this.setInjectHeaders(cloudfrontEvent);
319
- }
320
- setInjectHeaders(cloudfrontEvent) {
321
- const { response, request } = this.getRequestResponseFromEvent(cloudfrontEvent);
322
- if (response !== undefined) {
323
- const match = request.headers['x-netacea-match'];
324
- const mitigate = request.headers['x-netacea-mitigate'];
325
- const captcha = request.headers['x-netacea-captcha'];
326
- const eventId = request.headers['x-netacea-event-id'];
327
- if (match !== undefined &&
328
- mitigate !== undefined &&
329
- captcha !== undefined) {
330
- response.headers['x-netacea-match'] = match;
331
- response.headers['x-netacea-mitigate'] = mitigate;
332
- response.headers['x-netacea-captcha'] = this.shouldSetCaptchaPass(cloudfrontEvent)
333
- ? [{
334
- key: 'x-netacea-captcha',
335
- value: '2'
336
- }]
337
- : captcha;
338
- }
339
- if (eventId !== undefined) {
340
- response.headers['x-netacea-event-id'] = eventId;
341
- }
342
- }
343
- }
344
- getValueFromHeaderOrDefault(headers, key, def = '') {
345
- if (headers[key] !== undefined) {
346
- const v = headers[key];
347
- if (v !== undefined) {
348
- return v[0].value;
349
- }
350
- }
351
- return def;
352
- }
353
- getMitataValueFromHeaderOrDefault(headers, key, def = '') {
354
- if (headers[key] !== undefined) {
355
- const v = headers[key];
356
- if (v !== undefined) {
357
- const mitataItem = v.find(x => x.value.includes(this.netaceaCookieName));
358
- if (mitataItem !== undefined) {
359
- return mitataItem.value;
360
- }
361
- }
362
- }
363
- return def;
364
- }
365
- getRequestResponseFromEvent(event) {
366
- return event.Records[0].cf;
367
- }
368
- async getMitigationResponse(cloudfrontEvent) {
369
- const { request } = this.getRequestResponseFromEvent(cloudfrontEvent);
370
- const cookies = this.getMitataValueFromHeaderOrDefault(request.headers, 'cookie');
371
- const mitata = await this.readCookie(this.netaceaCookieName, cookies);
372
- const mitataCaptcha = await this.readCookie(this.netaceaCaptchaCookieName, cookies);
373
- const clientIp = (0, retrieveClientIp_1.retrieveClientIp)(cloudfrontEvent, this.ipHeaderName);
374
- const userAgent = this.getValueFromHeaderOrDefault(request.headers, 'user-agent');
375
- const accept = this.getValueFromHeaderOrDefault(request.headers, 'accept', 'text/html');
376
- const host = this.getValueFromHeaderOrDefault(request.headers, 'host');
377
- return {
378
- netaceaResult: await this.processMitigateRequest({
379
- getBodyFn: async () => { var _a, _b; return await Promise.resolve(Buffer.from((_b = (_a = request.body) === null || _a === void 0 ? void 0 : _a.data) !== null && _b !== void 0 ? _b : '', 'base64').toString()); },
380
- clientIp,
381
- method: request.method,
382
- url: request.uri,
383
- userAgent,
384
- accept,
385
- host,
386
- mitata,
387
- mitataCaptcha
388
- }),
389
- request
390
- };
391
- }
392
- addNetaceaCookiesToRequest(request, netaceaResult) {
393
- var _a, _b;
394
- if (netaceaResult === undefined) {
395
- return request;
396
- }
397
- request.headers[Cloudfront.NetaceaCookieHeader] = [];
398
- if (netaceaResult.setCookie !== undefined) {
399
- for (const cookie of netaceaResult.setCookie) {
400
- const cookieHeader = (_a = request.headers[Cloudfront.NetaceaCookieHeader]) !== null && _a !== void 0 ? _a : [];
401
- cookieHeader.push({
402
- key: Cloudfront.NetaceaCookieHeader,
403
- value: cookie
404
- });
405
- request.headers[Cloudfront.NetaceaCookieHeader] = cookieHeader;
406
- }
407
- }
408
- if (this.mitigationType === netaceaintegrationbase_1.NetaceaMitigationType.INJECT) {
409
- for (const [key, value] of Object.entries((_b = netaceaResult.injectHeaders) !== null && _b !== void 0 ? _b : {})) {
410
- request.headers[key] = [
411
- {
412
- key,
413
- value
414
- }
415
- ];
416
- }
417
- }
418
- return request;
419
- }
420
- getCookieHeader(args) {
421
- const { request } = this.getRequestResponseFromEvent(args);
422
- return this.getMitataValueFromHeaderOrDefault(request.headers, 'cookie');
423
- }
424
- async encryptCookieValue(cookieValue) {
425
- if (this.cookieEncryptionKey !== undefined) {
426
- return await (0, cookieEncryption_1.encrypt)(cookieValue, this.cookieEncryptionKey);
427
- }
428
- return cookieValue;
429
- }
430
- async decryptCookieValue(encryptedCookieValue) {
431
- if (this.cookieEncryptionKey !== undefined) {
432
- return await (0, cookieEncryption_1.decrypt)(encryptedCookieValue, this.cookieEncryptionKey);
433
- }
434
- return encryptedCookieValue;
435
- }
436
- /**
437
- * START -- NETACEA BASE METHODS
438
- */
439
- async runMitigation(args) {
440
- try {
441
- switch (this.mitigationType) {
442
- case netaceaintegrationbase_1.NetaceaMitigationType.MITIGATE:
443
- return await this.mitigate(args);
444
- case netaceaintegrationbase_1.NetaceaMitigationType.INJECT:
445
- return await this.inject(args);
446
- case netaceaintegrationbase_1.NetaceaMitigationType.INGEST:
447
- return await this.processIngest(args);
448
- default:
449
- // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
450
- throw new Error(`Netacea Error: Mitigation type ${this.mitigationType} not recognised`);
451
- }
452
- }
453
- catch (e) {
454
- // eslint-disable-next-line no-console
455
- console.error('Netacea FAILOPEN Error:', e);
456
- return {
457
- injectHeaders: {
458
- 'x-netacea-captcha': '0',
459
- 'x-netacea-match': '0',
460
- 'x-netacea-mitigate': '0'
461
- },
462
- sessionStatus: ''
463
- };
464
- }
465
- }
466
- /**
467
- * Returns the value of the cookie with the given name from a string or list of cookies.
468
- * If the cookie name is included in the encryptedCookies class property,
469
- * then the cookie value will be decrypted automatically.
470
- * The method may operate of either the HTTP Cookie or Set-Cookie headers.
471
- * @param cookieName the name of the cookie to find.
472
- * @param cookies the full list of cookies, either as a string or an array of strings.
473
- * @returns the value of the cookie, if found.
474
- */
475
- async readCookie(cookieName, cookies) {
476
- if (cookies === null || cookies === undefined) {
477
- return undefined;
478
- }
479
- if (typeof cookies === 'string') {
480
- return await this.readCookie(cookieName, cookies.split(';'));
481
- }
482
- const valuePrefix = `${cookieName}=`;
483
- for (const cookie of cookies) {
484
- // split again on ; to handle Set-Cookie header format.
485
- const trimmedCookie = cookie.split(';')[0].trimStart();
486
- if (trimmedCookie.startsWith(valuePrefix)) {
487
- const cookieValue = trimmedCookie.slice(valuePrefix.length);
488
- if (this.encryptedCookies.includes(cookieName)) {
489
- try {
490
- return await this.decryptCookieValue(cookieValue);
491
- }
492
- catch (_e) {
493
- return undefined;
494
- }
495
- }
496
- return cookieValue;
497
- }
498
- }
499
- return undefined;
500
- }
501
- async processMitigateRequest(args) {
502
- const isCaptchaPost = this.isUrlCaptchaPost(args.url, args.method);
503
- return await (isCaptchaPost
504
- ? this.processCaptcha(args.mitata, args.clientIp, args.userAgent, await args.getBodyFn())
505
- : this.check(args.mitata, args.clientIp, args.userAgent, args.accept, args.host, args.mitataCaptcha));
506
- }
507
- isUrlCaptchaPost(url, method) {
508
- return url.includes('/AtaVerifyCaptcha') && method.toLowerCase() === 'post';
509
- }
510
- isUrlCaptchaGet(url, method) {
511
- var _a;
512
- return url.toLowerCase() === ((_a = this.netaceaCaptchaPath) === null || _a === void 0 ? void 0 : _a.toLowerCase()) && method.toLowerCase() === 'get';
513
- }
514
- shouldSetCaptchaPass(cloudfrontEvent) {
515
- const { request, response } = this.getRequestResponseFromEvent(cloudfrontEvent);
516
- if (this.isUrlCaptchaPost(request.uri, request.method)) {
517
- return true;
518
- }
519
- if (response === undefined) {
520
- return false;
521
- }
522
- const cookies = response.headers['set-cookie'];
523
- const mitataCaptcha = cookies === null || cookies === void 0 ? void 0 : cookies.find(cookie => cookie.value.split('=')[0] === this.netaceaCaptchaCookieName);
524
- const mitataCaptchaIsSet = mitataCaptcha !== undefined;
525
- return this.mitigationType === netaceaintegrationbase_1.NetaceaMitigationType.INJECT && mitataCaptchaIsSet;
526
- }
527
- async processCaptcha(netaceaCookie, clientIP, userAgent, captchaData) {
528
- const { status, match, mitigate, captcha, body, setCookie } = await this.makeCaptchaAPICall(netaceaCookie, clientIP, userAgent, captchaData);
529
- return this.composeResult(body, setCookie, status, match, mitigate, captcha, true);
530
- }
531
- async makeCaptchaAPICall(netaceaCookie, clientIP, userAgent, captchaData) {
532
- const headers = {
533
- 'X-Netacea-API-Key': this.apiKey,
534
- 'X-Netacea-Client-IP': clientIP,
535
- 'user-agent': userAgent,
536
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
537
- };
538
- const mitata = (0, netaceaintegrationbase_1.matchMitataCookie)(netaceaCookie);
539
- if (mitata !== undefined) {
540
- headers['X-Netacea-UserId'] = mitata.userId;
541
- }
542
- if (this.captchaSiteKey !== undefined && this.captchaSecretKey !== undefined) {
543
- headers['X-Netacea-Captcha-Site-Key'] = this.captchaSiteKey;
544
- headers['X-Netacea-Captcha-Secret-Key'] = this.captchaSecretKey;
545
- }
546
- const res = await this.makeRequest({
547
- host: this.mitigationServiceUrl,
548
- path: '/AtaVerifyCaptcha',
549
- headers,
550
- method: 'POST',
551
- body: captchaData,
552
- timeout: this.timeout
553
- });
554
- return await this.getApiCallResponseFromResponse(res, mitata === null || mitata === void 0 ? void 0 : mitata.userId, clientIP);
555
- }
556
- async getApiCallResponseFromResponse(response, userId, clientIP, defaultMitataCodes) {
557
- var _a, _b, _c, _d, _f, _g, _h;
558
- if (response.status !== 200) {
559
- throw this.APIError(response);
560
- }
561
- let match = (_a = (0, headers_1.getHeaderAsInt)(response.headers, netaceaHeaders.match)) !== null && _a !== void 0 ? _a : NaN;
562
- let mitigate = (_b = (0, headers_1.getHeaderAsInt)(response.headers, netaceaHeaders.mitigate)) !== null && _b !== void 0 ? _b : NaN;
563
- let captcha = (_c = (0, headers_1.getHeaderAsInt)(response.headers, netaceaHeaders.captcha)) !== null && _c !== void 0 ? _c : NaN;
564
- if (isNaN(match)) {
565
- match = (_d = defaultMitataCodes === null || defaultMitataCodes === void 0 ? void 0 : defaultMitataCodes.match) !== null && _d !== void 0 ? _d : 0;
566
- }
567
- if (isNaN(mitigate)) {
568
- mitigate = (_f = defaultMitataCodes === null || defaultMitataCodes === void 0 ? void 0 : defaultMitataCodes.mitigate) !== null && _f !== void 0 ? _f : 0;
569
- }
570
- if (isNaN(captcha)) {
571
- captcha = (_g = defaultMitataCodes === null || defaultMitataCodes === void 0 ? void 0 : defaultMitataCodes.captcha) !== null && _g !== void 0 ? _g : 0;
572
- }
573
- let mitataMaxAge = (_h = (0, headers_1.getHeaderAsInt)(response.headers, netaceaHeaders.mitataExpiry)) !== null && _h !== void 0 ? _h : NaN;
574
- if (isNaN(mitataMaxAge)) {
575
- mitataMaxAge = 86400;
576
- }
577
- const mitata = await this.createMitata(clientIP, userId, match, mitigate, captcha);
578
- const mitataCaptcha = await this.createMitataCaptcha(response.headers);
579
- const setCookie = [
580
- mitata,
581
- mitataCaptcha
582
- ].filter(c => c !== undefined);
583
- const eventId = (0, headers_1.getHeader)(response.headers, netaceaHeaders.eventId);
584
- return {
585
- status: response.status,
586
- match,
587
- mitigate,
588
- captcha,
589
- setCookie,
590
- body: response.body,
591
- eventId,
592
- mitataMaxAge
593
- };
594
- }
595
- APIError(response) {
596
- let message = 'Unknown error';
597
- switch (response.status) {
598
- case 403:
599
- message = 'Invalid credentials';
600
- break;
601
- case 500:
602
- message = 'Server error';
603
- break;
604
- case 502:
605
- message = 'Bad Gateway';
606
- break;
607
- case 503:
608
- message = 'Service Unavailable';
609
- break;
610
- case 400:
611
- message = 'Invalid request';
612
- break;
613
- }
614
- return new Error(`Error reaching Netacea API (${message}), status: ${response.status}`);
615
- }
616
- async createMitata(clientIP, userId, match, mitigate, captcha, maxAge = 86400, expiry = undefined) {
617
- // serve, fail, cookiefail
618
- const isCaptchaServe = [1, 3, 5].includes(captcha);
619
- const isHardBlocked = mitigate === 3;
620
- const expiryDelta = (isCaptchaServe || isHardBlocked)
621
- ? -60
622
- : this.mitataCookieExpirySeconds;
623
- const mitataExpiry = expiry !== null && expiry !== void 0 ? expiry : Math.floor(Date.now() / 1000) + expiryDelta;
624
- if (this.secretKey === undefined) {
625
- throw new Error('Cannot build cookie without secret key.');
626
- }
627
- const mitataCode = [match, mitigate, captcha].join('');
628
- const mitataValue = (0, netaceaintegrationbase_1.createMitataCookie)(clientIP, userId, mitataExpiry, this.secretKey, mitataCode);
629
- let maxAgeAttr = maxAge;
630
- let path;
631
- let finalCookieAttr;
632
- if (this.netaceaCookieAttributes !== '') {
633
- const { extractedAttribute: exctractedMaxAge, cookieAttributes: cookieAttrsWithoutMaxAge } = extractAndRemoveCookieAttr(this.netaceaCookieAttributes, 'Max-Age');
634
- maxAgeAttr = exctractedMaxAge !== undefined ? Number(exctractedMaxAge) : maxAge;
635
- const { extractedAttribute: extractedPath, cookieAttributes } = extractAndRemoveCookieAttr(cookieAttrsWithoutMaxAge, 'Path');
636
- path = extractedPath !== null && extractedPath !== void 0 ? extractedPath : '/';
637
- finalCookieAttr = cookieAttributes !== null && cookieAttributes !== void 0 ? cookieAttributes : undefined;
638
- }
639
- return await this.buildCookieFromValues(this.netaceaCookieName, mitataValue, maxAgeAttr, finalCookieAttr, path);
640
- }
641
- async createMitataCaptcha(headers) {
642
- var _a, _b;
643
- let setCookieHeaders = (_a = headers['set-cookie']) !== null && _a !== void 0 ? _a : [];
644
- setCookieHeaders = typeof setCookieHeaders === 'string'
645
- ? [setCookieHeaders]
646
- : setCookieHeaders;
647
- const mitataCaptcha = setCookieHeaders
648
- .find(cookie => cookie.startsWith('_mitatacaptcha='));
649
- let mitataCaptchaValue;
650
- let mitataCaptchaMaxAge = '86400';
651
- if (mitataCaptcha !== undefined && mitataCaptcha !== '') {
652
- try {
653
- const parsed = parseSetCookie(mitataCaptcha);
654
- mitataCaptchaValue = parsed.value;
655
- mitataCaptchaMaxAge = (_b = extractCookieAttr(parsed.attributes, 'Max-Age')) !== null && _b !== void 0 ? _b : '86400';
656
- }
657
- catch (e) {
658
- return undefined;
659
- }
660
- }
661
- if (mitataCaptchaValue === '' || mitataCaptchaValue === undefined) {
662
- return undefined;
663
- }
664
- const attributes = removeDuplicateAttrs([
665
- this.netaceaCaptchaCookieAttributes,
666
- 'Path=/',
667
- `Max-Age=${mitataCaptchaMaxAge}`
668
- ]);
669
- mitataCaptchaValue = this.encryptedCookies.includes(this.netaceaCaptchaCookieName)
670
- ? await this.encryptCookieValue(mitataCaptchaValue)
671
- : mitataCaptchaValue;
672
- return `${this.netaceaCaptchaCookieName}=${mitataCaptchaValue}; ${attributes}`;
673
- }
674
- async buildCookieFromValues(cookieName, value, maxAge, cookieAttributes, path = '/') {
675
- const cookieValue = this.encryptedCookies.includes(cookieName)
676
- ? await this.encryptCookieValue(value)
677
- : value;
678
- const setCookie = `${cookieName}=${cookieValue}; Max-Age=${maxAge}; Path=${path}`;
679
- return cookieAttributes !== undefined && cookieAttributes !== ''
680
- ? `${setCookie}; ${cookieAttributes}`
681
- : setCookie;
682
- }
683
- async callIngest(args) {
684
- const body = this.constructWebLog(args);
685
- if (this.ingestType === netaceaintegrationbase_1.NetaceaIngestType.KINESIS) {
686
- if (this.kinesis === undefined) {
687
- // eslint-disable-next-line no-console
688
- console.error('Netacea Error: Unable to log as Kinesis has not been defined.');
689
- return;
690
- }
691
- try {
692
- await this.kinesis.ingest({
693
- ...body,
694
- apiKey: this.apiKey
695
- }, this.makeRequest.bind(this));
696
- }
697
- catch (e) {
698
- // eslint-disable-next-line no-console
699
- console.error('NETACEA Error: ', e.message);
700
- }
701
- }
702
- else {
703
- const headers = {
704
- 'X-Netacea-API-Key': this.apiKey,
705
- 'content-type': 'application/json'
706
- };
707
- const res = await this.makeIngestApiCall(headers, body);
708
- if (res.status !== 200) {
709
- throw this.APIError(res);
710
- }
711
- }
712
- }
713
- constructWebLog(args) {
714
- args.bytesSent = args.bytesSent === '' ? '0' : args.bytesSent;
715
- if (this.logVersion === netaceaintegrationbase_1.NetaceaLogVersion.V2) {
716
- return this.constructV2WebLog(args);
717
- }
718
- return this.constructV1WebLog(args);
719
- }
720
- constructV2WebLog({ ip, userAgent, status, method, path, protocol, referer, bytesSent, requestTime, mitataCookie, sessionStatus, integrationType, integrationVersion, xForwardedFor }) {
721
- var _a;
722
- const now = new Date();
723
- // Paths must be prefixed with '/'
724
- if (path[0] !== '/') {
725
- path = `/${path}`;
726
- }
727
- let actualQuery;
728
- const splitPath = path.split('?');
729
- if (splitPath.length > 1) {
730
- actualQuery = `?${splitPath[1]}`;
731
- }
732
- const actualPath = splitPath[0];
733
- const userId = (_a = (0, netaceaintegrationbase_1.matchMitataCookie)(mitataCookie)) === null || _a === void 0 ? void 0 : _a.userId;
734
- const log = {
735
- status,
736
- method,
737
- 'bytes_sent': (0, netaceaintegrationbase_1.safeParseInt)(bytesSent),
738
- 'referrer': referer === '' ? undefined : referer,
739
- 'request': `${method} ${actualPath}${actualQuery !== null && actualQuery !== void 0 ? actualQuery : ''} ${protocol}`,
740
- 'request_time': (0, netaceaintegrationbase_1.safeParseInt)(requestTime),
741
- 'integration_type': integrationType,
742
- 'integration_version': integrationVersion,
743
- 'client': ip,
744
- 'user_agent': userAgent,
745
- 'bc_type': sessionStatus === '' ? undefined : sessionStatus,
746
- 'hour': now.getUTCHours(),
747
- 'minute': now.getUTCMinutes(),
748
- // '2021-08-04T14:22:41.155Z' = toISOString()
749
- '@timestamp': now.toISOString().replace('Z', '+00:00'),
750
- 'path': actualPath,
751
- protocol,
752
- 'query': actualQuery,
753
- 'user_id': userId,
754
- 'x_forwarded_for': xForwardedFor
755
- };
756
- return log;
757
- }
758
- constructV1WebLog({ ip, userAgent, status, method, path, protocol, referer, bytesSent, requestTime, mitataCookie, sessionStatus, integrationType, integrationVersion, xForwardedFor }) {
759
- const timestamp = new Date().toUTCString();
760
- return {
761
- Request: `${method} ${path} ${protocol}`,
762
- TimeLocal: timestamp,
763
- RealIp: ip,
764
- UserAgent: userAgent,
765
- Status: status,
766
- RequestTime: requestTime === null || requestTime === void 0 ? void 0 : requestTime.toString(),
767
- BytesSent: bytesSent === null || bytesSent === void 0 ? void 0 : bytesSent.toString(),
768
- Referer: referer === '' ? '-' : referer,
769
- NetaceaUserIdCookie: mitataCookie !== null && mitataCookie !== void 0 ? mitataCookie : '',
770
- NetaceaMitigationApplied: sessionStatus !== null && sessionStatus !== void 0 ? sessionStatus : '',
771
- IntegrationType: integrationType !== null && integrationType !== void 0 ? integrationType : '',
772
- IntegrationVersion: integrationVersion !== null && integrationVersion !== void 0 ? integrationVersion : '',
773
- XForwardedFor: xForwardedFor
774
- };
775
- }
776
- async makeIngestApiCall(headers, body) {
777
- return await this.makeRequest({
778
- host: this.ingestServiceUrl,
779
- method: 'POST',
780
- path: '/',
781
- headers,
782
- body: JSON.stringify(body),
783
- timeout: this.timeout
784
- });
785
- }
786
- async processIngest(args) {
787
- var _a;
788
- if (this.secretKey === undefined) {
789
- throw new Error('Secret key is required for ingest');
790
- }
791
- const cookies = this.getCookieHeader(args);
792
- const netaceaCookie = await this.readCookie(this.netaceaCookieName, cookies);
793
- const cookieInfo = (0, netaceaintegrationbase_1.checkMitataCookie)(netaceaCookie, netaceaintegrationbase_1.ingestIgnoredIpValue, this.secretKey);
794
- if (!cookieInfo.isPrimaryHashValid) {
795
- return await this.setIngestOnlyMitataCookie(undefined);
796
- }
797
- if (cookieInfo.requiresReissue) {
798
- return await this.setIngestOnlyMitataCookie((_a = cookieInfo.mitata) === null || _a === void 0 ? void 0 : _a.userId);
799
- }
800
- return {
801
- sessionStatus: '',
802
- setCookie: []
803
- };
804
- }
805
- async setIngestOnlyMitataCookie(userId) {
806
- const mitataCookie = await this.createMitata(netaceaintegrationbase_1.ingestIgnoredIpValue, userId, 0, 0, 0, ONE_DAY_IN_SECONDS);
807
- return {
808
- sessionStatus: '',
809
- setCookie: [mitataCookie]
810
- };
811
- }
812
- findBestMitigation(match, mitigate, captcha, isCaptchaPost) {
813
- var _a, _b, _c;
814
- const UNKNOWN = 'unknown';
815
- captcha = this.adjustCaptchaCode(captcha, isCaptchaPost);
816
- let sessionStatus = (_a = matchMap[match]) !== null && _a !== void 0 ? _a : (UNKNOWN + '_');
817
- sessionStatus += (_b = mitigateMap[mitigate]) !== null && _b !== void 0 ? _b : UNKNOWN;
818
- let mitigation = bestMitigationMap[mitigate];
819
- if (captcha !== 0) {
820
- sessionStatus += ',' + ((_c = captchaMap[captcha]) !== null && _c !== void 0 ? _c : UNKNOWN);
821
- const bestCaptchaMitigation = bestMitigationCaptchaMap[captcha];
822
- if (bestCaptchaMitigation !== undefined) {
823
- mitigation = bestCaptchaMitigation;
824
- }
825
- }
826
- if (this.mitigationType === netaceaintegrationbase_1.NetaceaMitigationType.INJECT) {
827
- mitigation = mitigationTypes.none;
828
- }
829
- return {
830
- sessionStatus,
831
- mitigation,
832
- parts: {
833
- match,
834
- mitigate,
835
- captcha
836
- }
837
- };
838
- }
839
- adjustCaptchaCode(captcha, isCaptchaPost) {
840
- let adjustedCaptchaCode = captcha;
841
- if (!isCaptchaPost) {
842
- if (captcha === 2) {
843
- adjustedCaptchaCode = 4;
844
- }
845
- else if (captcha === 3) {
846
- adjustedCaptchaCode = 5;
847
- }
848
- }
849
- return adjustedCaptchaCode;
850
- }
851
- async check(netaceaCookie, clientIP, userAgent, accept, host, captchaCookie) {
852
- var _a, _b;
853
- let status, match, mitigate, captcha, body, setCookie, eventId;
854
- if (this.secretKey === undefined) {
855
- throw new Error('Secret key is required to mitigate');
856
- }
857
- const cookieInfo = (0, netaceaintegrationbase_1.checkMitataCookie)(netaceaCookie, clientIP, this.secretKey);
858
- if (!cookieInfo.isPrimaryHashValid || cookieInfo.requiresReissue) {
859
- // Get latest mitigation information
860
- const result = await this.makeMitigateAPICall({
861
- userId: (_a = cookieInfo.mitata) === null || _a === void 0 ? void 0 : _a.userId,
862
- clientIP,
863
- userAgent,
864
- captchaCookie,
865
- accept,
866
- host
867
- });
868
- status = result.status;
869
- match = result.match;
870
- mitigate = result.mitigate;
871
- captcha = result.captcha;
872
- body = result.body;
873
- setCookie = [
874
- await this.createMitata(clientIP, (_b = cookieInfo.mitata) === null || _b === void 0 ? void 0 : _b.userId, match, mitigate, captcha, result.mitataMaxAge)
875
- ];
876
- eventId = result.eventId;
877
- }
878
- else {
879
- status = -1;
880
- match = cookieInfo.match;
881
- mitigate = cookieInfo.mitigate;
882
- captcha = cookieInfo.captcha;
883
- body = undefined;
884
- setCookie = [];
885
- }
886
- return this.composeResult(body, setCookie, status, match, mitigate, captcha, false, eventId);
887
- }
888
- async makeMitigateAPICall({ userId, clientIP, userAgent, captchaCookie, accept, host, isCaptchaGet = false, defaultMitataCodes, trackingId }) {
889
- const headers = {
890
- 'X-Netacea-API-Key': this.apiKey,
891
- 'X-Netacea-Client-IP': clientIP,
892
- 'user-agent': userAgent,
893
- 'cookie': this.buildCookieHeader({
894
- _mitatacaptcha: captchaCookie
895
- })
896
- };
897
- if (userId !== undefined) {
898
- headers['X-Netacea-UserId'] = userId;
899
- }
900
- if (this.captchaSiteKey !== undefined && this.captchaSecretKey !== undefined) {
901
- headers['X-Netacea-Captcha-Site-Key'] = this.captchaSiteKey;
902
- headers['X-Netacea-Captcha-Secret-Key'] = this.captchaSecretKey;
903
- }
904
- if (this.dynamicCaptchaContentType && this.netaceaCaptchaPath !== undefined) {
905
- headers['X-Netacea-Captcha-Content-Type'] = (0, dynamicContentType_1.acceptHeaderToNetaceaContentType)(accept);
906
- }
907
- const isJsonRequest = headers['X-Netacea-Captcha-Content-Type'] === 'application/json';
908
- const trackingIdQuery = trackingId !== undefined ? `?trackingId=${trackingId}` : '';
909
- const res = await this.makeRequest({
910
- host: this.mitigationServiceUrl,
911
- path: isCaptchaGet ? `/captcha${trackingIdQuery}` : '/',
912
- headers,
913
- method: 'GET',
914
- timeout: this.timeout
915
- });
916
- if (isJsonRequest && this.netaceaCaptchaPath !== undefined) {
917
- res.body = (0, dynamicContentType_1.mitSvcJsonResponseToUrlResponse)(res.body, host, this.netaceaCaptchaPath);
918
- }
919
- return await this.getApiCallResponseFromResponse(res, userId, clientIP, defaultMitataCodes);
920
- }
921
- buildCookieHeader(cookies) {
922
- let cookiestr = '';
923
- let separator = '';
924
- for (const cookie in cookies) {
925
- const cookieValue = cookies[cookie];
926
- if (cookieValue !== undefined) {
927
- cookiestr = `${cookiestr}${separator}${cookie}=${cookieValue}`;
928
- separator = '; ';
929
- }
930
- }
931
- return cookiestr;
932
- }
933
- composeResult(body, setCookie, status, match, mitigate, captcha, isCaptchaPost, eventId) {
934
- const bestMitigation = this.findBestMitigation(match, mitigate, captcha, isCaptchaPost);
935
- const result = {
936
- body,
937
- apiCallStatus: status,
938
- setCookie,
939
- sessionStatus: bestMitigation.sessionStatus,
940
- mitigation: bestMitigation.mitigation,
941
- mitigated: [
942
- mitigationTypes.block,
943
- mitigationTypes.captcha,
944
- mitigationTypes.captchaPass
945
- ].includes(bestMitigation.mitigation)
946
- };
947
- if (this.mitigationType === netaceaintegrationbase_1.NetaceaMitigationType.INJECT) {
948
- const injectHeaders = {
949
- 'x-netacea-match': bestMitigation.parts.match.toString(),
950
- 'x-netacea-mitigate': bestMitigation.parts.mitigate.toString(),
951
- 'x-netacea-captcha': bestMitigation.parts.captcha.toString()
952
- };
953
- if (eventId !== undefined) {
954
- injectHeaders['x-netacea-event-id'] = eventId;
955
- }
956
- result.injectHeaders = injectHeaders;
957
- }
958
- return result;
959
- }
960
- }
961
- Cloudfront.NetaceaCookieHeader = 'x-netacea-cloudfront-mitata-cookie';
962
- // The `NetaceaTrueUserAgentHeader` header is needed when the Origin server returns an error
963
- // We need to ingest in OriginResponse, but for some reason cloudfront overrides the header
964
- Cloudfront.NetaceaTrueUserAgentHeader = 'x-netacea-true-useragent-header';
965
- exports.default = Cloudfront;
966
- //# sourceMappingURL=Cloudfront.js.map