@netacea/cloudfront 5.2.54 → 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.
- package/dist/index.d.ts +508 -0
- package/dist/index.js +2 -0
- package/package.json +10 -5
- package/.vscode/launch.json +0 -21
- package/.vscode/settings.json +0 -5
- package/CHANGELOG.md +0 -1526
- package/dist/package.json +0 -51
- package/dist/src/Cloudfront.d.ts +0 -93
- package/dist/src/Cloudfront.js +0 -954
- package/dist/src/Cloudfront.js.map +0 -1
- package/dist/src/captchaGet.d.ts +0 -14
- package/dist/src/captchaGet.js +0 -90
- package/dist/src/captchaGet.js.map +0 -1
- package/dist/src/cookieEncryption.d.ts +0 -2
- package/dist/src/cookieEncryption.js +0 -23
- package/dist/src/cookieEncryption.js.map +0 -1
- package/dist/src/dynamicContentType.d.ts +0 -3
- package/dist/src/dynamicContentType.js +0 -47
- package/dist/src/dynamicContentType.js.map +0 -1
- package/dist/src/headers.d.ts +0 -14
- package/dist/src/headers.js +0 -31
- package/dist/src/headers.js.map +0 -1
- package/dist/src/index.d.ts +0 -4
- package/dist/src/index.js +0 -8
- package/dist/src/index.js.map +0 -1
- package/dist/src/retrieveClientIp.d.ts +0 -7
- package/dist/src/retrieveClientIp.js +0 -26
- package/dist/src/retrieveClientIp.js.map +0 -1
- package/dist/src/types.d.ts +0 -70
- package/dist/src/types.js +0 -3
- package/dist/src/types.js.map +0 -1
- package/package.json.bak +0 -51
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
import { CloudFrontRequestEvent, CloudFrontResultResponse, CloudFrontRequest, CloudFrontResponseEvent, CloudFrontResponse } from 'aws-lambda';
|
|
2
|
+
|
|
3
|
+
type KinesisMakeRequest = (args: {
|
|
4
|
+
headers: Record<string, string>;
|
|
5
|
+
method: 'POST' | 'GET';
|
|
6
|
+
host: string;
|
|
7
|
+
path: string;
|
|
8
|
+
body?: any;
|
|
9
|
+
}) => Promise<any>;
|
|
10
|
+
interface KinesisIngestWebLog {
|
|
11
|
+
apiKey: string;
|
|
12
|
+
}
|
|
13
|
+
interface KinesisIngestConfigArgs {
|
|
14
|
+
kinesisStreamName: string;
|
|
15
|
+
kinesisAccessKey?: string;
|
|
16
|
+
kinesisSecretKey?: string;
|
|
17
|
+
logBatchSize?: number;
|
|
18
|
+
maxLogAgeSeconds?: number;
|
|
19
|
+
}
|
|
20
|
+
interface KinesisIngestArgs extends KinesisIngestConfigArgs {
|
|
21
|
+
apiKey: string;
|
|
22
|
+
}
|
|
23
|
+
declare class KinesisIngest {
|
|
24
|
+
protected readonly kinesisStreamName: string;
|
|
25
|
+
protected readonly kinesisAccessKey?: string;
|
|
26
|
+
protected readonly kinesisSecretKey?: string;
|
|
27
|
+
protected readonly logBatchSize: number;
|
|
28
|
+
protected readonly maxLogAgeSeconds: number;
|
|
29
|
+
protected logCache: KinesisIngestWebLog[];
|
|
30
|
+
private intervalSet;
|
|
31
|
+
constructor({ kinesisStreamName, kinesisAccessKey, kinesisSecretKey, maxLogAgeSeconds, logBatchSize }: KinesisIngestArgs);
|
|
32
|
+
putToKinesis<MakeRequest extends KinesisMakeRequest>(makeRequest: MakeRequest): Promise<void>;
|
|
33
|
+
ingest<LogFormat extends KinesisIngestWebLog, MakeRequest extends KinesisMakeRequest>(log: LogFormat, makeRequest: MakeRequest): Promise<any>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
declare enum NetaceaIngestType {
|
|
37
|
+
/**
|
|
38
|
+
* ORIGIN Ingest mode; data to be ingested is set by headers, so it can be forwarded via a seperate mechanism
|
|
39
|
+
*/
|
|
40
|
+
ORIGIN = "ORIGIN",
|
|
41
|
+
/**
|
|
42
|
+
* HTTP Ingest mode, this is the standard implementation
|
|
43
|
+
*/
|
|
44
|
+
HTTP = "HTTP",
|
|
45
|
+
/**
|
|
46
|
+
* Ingest over Kinesis, Netacea will inform you if this is required
|
|
47
|
+
* and will provide you with kinesis credentials.
|
|
48
|
+
*/
|
|
49
|
+
KINESIS = "KINESIS",
|
|
50
|
+
/**
|
|
51
|
+
* Data to be Ingest via some mechanism native to the host/CDN, e.g. log shipping.
|
|
52
|
+
*/
|
|
53
|
+
NATIVE = "NATIVE"
|
|
54
|
+
}
|
|
55
|
+
declare enum NetaceaLogVersion {
|
|
56
|
+
/**
|
|
57
|
+
* Current style log version
|
|
58
|
+
*/
|
|
59
|
+
V1 = "V1",
|
|
60
|
+
/**
|
|
61
|
+
* V2 Log format, should only be used if Netacea instructs
|
|
62
|
+
*/
|
|
63
|
+
V2 = "V2"
|
|
64
|
+
}
|
|
65
|
+
declare enum NetaceaMitigationType {
|
|
66
|
+
/**
|
|
67
|
+
* Run Netacea with mitigation mode enabled.
|
|
68
|
+
* This will serve Captcha pages and Forbidden pages when instructed to do so
|
|
69
|
+
*/
|
|
70
|
+
MITIGATE = "MITIGATE",
|
|
71
|
+
/**
|
|
72
|
+
* Run Netacea with Inject mode enabled.
|
|
73
|
+
* The end-user will only receive a cookie.
|
|
74
|
+
* The origin server will receive 3-4 headers,
|
|
75
|
+
*
|
|
76
|
+
* 'x-netacea-match' indicating what was matched (nothing(0), ua(1), ip(2), etc...)
|
|
77
|
+
*
|
|
78
|
+
* 'x-netacea-mitigate' indicating what action would've be taken (nothing (0), block(1), allow(2), etc...)
|
|
79
|
+
*
|
|
80
|
+
* 'x-netacea-captcha' indicating what captcha action would've been taken
|
|
81
|
+
*
|
|
82
|
+
* 'x-netacea-event-id' event id value that should be injected to the captcha
|
|
83
|
+
* page if using `@netacea/captchafeedback` module on the origin server
|
|
84
|
+
*/
|
|
85
|
+
INJECT = "INJECT",
|
|
86
|
+
/**
|
|
87
|
+
* Run Netacea with Ingest only mode
|
|
88
|
+
* No cookies will be set for the end user.
|
|
89
|
+
* No mitigations will be applied.
|
|
90
|
+
*
|
|
91
|
+
* **It's recommended to start in this mode!**
|
|
92
|
+
*/
|
|
93
|
+
INGEST = "INGEST"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
interface MakeRequestArgs {
|
|
97
|
+
/**
|
|
98
|
+
* Hostname of the request. For example https://mitigations.netacea.net
|
|
99
|
+
*/
|
|
100
|
+
host: string;
|
|
101
|
+
/**
|
|
102
|
+
* Path for the request, i.e captcha requests will be `/AtaVerifyCaptcha`
|
|
103
|
+
*/
|
|
104
|
+
path: string;
|
|
105
|
+
/**
|
|
106
|
+
* Key value collection of the request headers
|
|
107
|
+
*/
|
|
108
|
+
headers: Record<string, string>;
|
|
109
|
+
/**
|
|
110
|
+
* HTTP Method
|
|
111
|
+
*/
|
|
112
|
+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
113
|
+
/**
|
|
114
|
+
* Request body value
|
|
115
|
+
*/
|
|
116
|
+
body?: string;
|
|
117
|
+
/**
|
|
118
|
+
* Request timeout value in ms
|
|
119
|
+
*/
|
|
120
|
+
timeout?: number;
|
|
121
|
+
}
|
|
122
|
+
interface NetaceaBaseArgs {
|
|
123
|
+
/**
|
|
124
|
+
* Netacea APIKey
|
|
125
|
+
*/
|
|
126
|
+
apiKey: string;
|
|
127
|
+
/**
|
|
128
|
+
* Netacea Secret Key
|
|
129
|
+
*/
|
|
130
|
+
secretKey: string;
|
|
131
|
+
/**
|
|
132
|
+
* Google RECaptcha Site Key.
|
|
133
|
+
* This is used for providing your own captcha values without updating these in the Netacea console.
|
|
134
|
+
*/
|
|
135
|
+
captchaSiteKey?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Google RECaptcha Secret Key.
|
|
138
|
+
* This is used for providing your own captcha values without updating these in the Netacea console.
|
|
139
|
+
*/
|
|
140
|
+
captchaSecretKey?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Request timeout in ms
|
|
143
|
+
*/
|
|
144
|
+
timeout?: number;
|
|
145
|
+
/**
|
|
146
|
+
* URL of the Netacea ingest service.
|
|
147
|
+
* DEFAULT: https://ingest.netacea.net
|
|
148
|
+
*/
|
|
149
|
+
ingestServiceUrl?: string;
|
|
150
|
+
/**
|
|
151
|
+
* URL of the Netacea mitigation service.
|
|
152
|
+
* DEFAULT: https://mitigations.netacea.net
|
|
153
|
+
*/
|
|
154
|
+
mitigationServiceUrl?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Type of mitigation applied, see the `NetaceaMitigationType` ENUM
|
|
157
|
+
* - INGEST - Ingest only mode, no mitigations applied
|
|
158
|
+
* - MITIGATION - Mitigation mode, active blocking/captcha rules will be applied.
|
|
159
|
+
* - INJECT - Inject mode, headers will be sent to your origin server
|
|
160
|
+
* indicating what actions Netacea would have taken.
|
|
161
|
+
* DEFAULT: NetaceaMitigationType.INGEST
|
|
162
|
+
*/
|
|
163
|
+
mitigationType?: NetaceaMitigationType;
|
|
164
|
+
/**
|
|
165
|
+
* Type of ingest, see the `NetaceaIngestType` ENUM
|
|
166
|
+
* - HTTP - Ingest via HTTP.
|
|
167
|
+
* - KINESIS - Ingest via KINESIS
|
|
168
|
+
* DEFAULT: NetaceaIngestType.HTTP
|
|
169
|
+
*/
|
|
170
|
+
ingestType?: NetaceaIngestType;
|
|
171
|
+
/**
|
|
172
|
+
* Kinesis ingest definition, see the `KinesisIngestConfigArgs` type.
|
|
173
|
+
* Only to be provided if ingestType is set to KINESIS.
|
|
174
|
+
* Netacea will provide you with the details for this stream.
|
|
175
|
+
*/
|
|
176
|
+
kinesis?: KinesisIngestConfigArgs;
|
|
177
|
+
/**
|
|
178
|
+
* Version of ingest, see the `NetaceaLogVersion` ENUM
|
|
179
|
+
* - V1 - Standard ingest.
|
|
180
|
+
* - V2 - New BETA ingest.
|
|
181
|
+
* Use NetaceaLogVersion.V1 unless instructed otherwise.
|
|
182
|
+
* DEFAULT: NetaceaLogVersion.V1
|
|
183
|
+
*/
|
|
184
|
+
logVersion?: NetaceaLogVersion;
|
|
185
|
+
/**
|
|
186
|
+
* Deprecated: alias for netaceaCookieExpirySeconds.
|
|
187
|
+
* If both are set, netaceaCookieExpirySeconds is prefered.
|
|
188
|
+
* Seconds for the netacea cookie to be revalidated after.
|
|
189
|
+
*/
|
|
190
|
+
mitataCookieExpirySeconds?: number;
|
|
191
|
+
/**
|
|
192
|
+
* Seconds for the netacea cookie to be revalidated after.
|
|
193
|
+
*/
|
|
194
|
+
netaceaCookieExpirySeconds?: number;
|
|
195
|
+
/**
|
|
196
|
+
* The name of the netacea cookie. Defaults to _mitata.
|
|
197
|
+
*/
|
|
198
|
+
netaceaCookieName?: string;
|
|
199
|
+
/**
|
|
200
|
+
* The name of the netacea captcha cookie. Defaults to _mitatacaptcha.
|
|
201
|
+
*/
|
|
202
|
+
netaceaCaptchaCookieName?: string;
|
|
203
|
+
}
|
|
204
|
+
interface InjectHeaders {
|
|
205
|
+
'x-netacea-match': string;
|
|
206
|
+
'x-netacea-mitigate': string;
|
|
207
|
+
'x-netacea-captcha': string;
|
|
208
|
+
'x-netacea-event-id'?: string;
|
|
209
|
+
}
|
|
210
|
+
interface ComposeResultResponse {
|
|
211
|
+
/**
|
|
212
|
+
* Body value of the response, should be in text format
|
|
213
|
+
*/
|
|
214
|
+
body?: string;
|
|
215
|
+
/**
|
|
216
|
+
* Response status code
|
|
217
|
+
*/
|
|
218
|
+
apiCallStatus: number;
|
|
219
|
+
/**
|
|
220
|
+
* setCookie values
|
|
221
|
+
*/
|
|
222
|
+
setCookie: string[];
|
|
223
|
+
/**
|
|
224
|
+
* Netacea session status string for ingest
|
|
225
|
+
*/
|
|
226
|
+
sessionStatus: string;
|
|
227
|
+
/**
|
|
228
|
+
* Netacea mitigation string
|
|
229
|
+
*/
|
|
230
|
+
mitigation: string;
|
|
231
|
+
/**
|
|
232
|
+
* Indicates if response should be mitigated or not
|
|
233
|
+
*/
|
|
234
|
+
mitigated: boolean;
|
|
235
|
+
/**
|
|
236
|
+
* Headers to ingest to origin server
|
|
237
|
+
*/
|
|
238
|
+
injectHeaders?: InjectHeaders;
|
|
239
|
+
}
|
|
240
|
+
interface IngestArgs {
|
|
241
|
+
/**
|
|
242
|
+
* Client IP Address
|
|
243
|
+
*/
|
|
244
|
+
ip: string;
|
|
245
|
+
/**
|
|
246
|
+
* Client User-Agent header value
|
|
247
|
+
*/
|
|
248
|
+
userAgent: string;
|
|
249
|
+
/**
|
|
250
|
+
* Response status code
|
|
251
|
+
* Should be 403 if Netacea mitigated
|
|
252
|
+
*/
|
|
253
|
+
status: string;
|
|
254
|
+
/**
|
|
255
|
+
* Request method
|
|
256
|
+
*/
|
|
257
|
+
method: string;
|
|
258
|
+
/**
|
|
259
|
+
* Request path
|
|
260
|
+
*/
|
|
261
|
+
path: string;
|
|
262
|
+
/**
|
|
263
|
+
* Request protocol
|
|
264
|
+
*/
|
|
265
|
+
protocol: string | null;
|
|
266
|
+
/**
|
|
267
|
+
* Request referer header value
|
|
268
|
+
*/
|
|
269
|
+
referer: string;
|
|
270
|
+
/**
|
|
271
|
+
* Request content-length header, or body size
|
|
272
|
+
*/
|
|
273
|
+
bytesSent: string | number;
|
|
274
|
+
/**
|
|
275
|
+
* Time taken to serve request
|
|
276
|
+
*/
|
|
277
|
+
requestTime: string | number;
|
|
278
|
+
/**
|
|
279
|
+
* Netacea mitata cookie value.
|
|
280
|
+
* Should be request's cookie value if Netacea was not called.
|
|
281
|
+
*/
|
|
282
|
+
mitataCookie?: string;
|
|
283
|
+
/**
|
|
284
|
+
* Session status from `ComposeResultResponse`
|
|
285
|
+
*/
|
|
286
|
+
sessionStatus?: string;
|
|
287
|
+
/**
|
|
288
|
+
* Type of the integration, for example "Cloudflare" or "Cloudfront"
|
|
289
|
+
*/
|
|
290
|
+
integrationType?: string;
|
|
291
|
+
/**
|
|
292
|
+
* SEMVER string indicating the version of the integration
|
|
293
|
+
* Example: 1.2.3
|
|
294
|
+
*/
|
|
295
|
+
integrationVersion?: string;
|
|
296
|
+
/**
|
|
297
|
+
* IP values set by a CDN under "x-fowarded-for" header
|
|
298
|
+
*/
|
|
299
|
+
xForwardedFor?: string;
|
|
300
|
+
headerFingerprint?: string;
|
|
301
|
+
cookieFingerprint?: string;
|
|
302
|
+
}
|
|
303
|
+
interface WebLog {
|
|
304
|
+
Request: string;
|
|
305
|
+
TimeLocal: string;
|
|
306
|
+
RealIp: string;
|
|
307
|
+
UserAgent: string;
|
|
308
|
+
Status: string;
|
|
309
|
+
RequestTime: string;
|
|
310
|
+
BytesSent: string;
|
|
311
|
+
Referer: string;
|
|
312
|
+
NetaceaUserIdCookie: string;
|
|
313
|
+
NetaceaMitigationApplied: string;
|
|
314
|
+
IntegrationType?: string;
|
|
315
|
+
IntegrationVersion?: string;
|
|
316
|
+
XForwardedFor?: string;
|
|
317
|
+
optional?: Record<string, unknown>;
|
|
318
|
+
}
|
|
319
|
+
interface V2WebLog {
|
|
320
|
+
'@timestamp': string;
|
|
321
|
+
bc_type?: string;
|
|
322
|
+
bytes_sent: number;
|
|
323
|
+
client: string;
|
|
324
|
+
domain_name?: string;
|
|
325
|
+
domain_name_orig?: string;
|
|
326
|
+
hour: number;
|
|
327
|
+
integration_type?: string;
|
|
328
|
+
integration_version?: string;
|
|
329
|
+
method: string;
|
|
330
|
+
minute: number;
|
|
331
|
+
path: string;
|
|
332
|
+
protocol: string | null;
|
|
333
|
+
query?: string;
|
|
334
|
+
referrer?: string;
|
|
335
|
+
request: string;
|
|
336
|
+
request_time: number;
|
|
337
|
+
status: string;
|
|
338
|
+
user_agent: string;
|
|
339
|
+
user_id?: string;
|
|
340
|
+
x_forwarded_for?: string;
|
|
341
|
+
optional?: Record<string, unknown>;
|
|
342
|
+
}
|
|
343
|
+
interface NetaceaResponseBase {
|
|
344
|
+
/**
|
|
345
|
+
* Cookies that should be set back to the user.
|
|
346
|
+
*/
|
|
347
|
+
setCookie?: string[];
|
|
348
|
+
/**
|
|
349
|
+
* Netacea session status string
|
|
350
|
+
*/
|
|
351
|
+
sessionStatus: string;
|
|
352
|
+
}
|
|
353
|
+
interface MitigateResponse<T = any> extends NetaceaResponseBase {
|
|
354
|
+
/**
|
|
355
|
+
* Response value, using Response generic
|
|
356
|
+
*/
|
|
357
|
+
response?: T;
|
|
358
|
+
}
|
|
359
|
+
interface InjectResponse<T = any> extends MitigateResponse<T> {
|
|
360
|
+
/**
|
|
361
|
+
* Headers to be sent to the origin server
|
|
362
|
+
* X-Netacea-Match
|
|
363
|
+
* X-Netacea-Mitigate
|
|
364
|
+
* X-Netacea-Captcha
|
|
365
|
+
* X-Netacea-Event-ID (Only sent when CAPTCHA is served)
|
|
366
|
+
*/
|
|
367
|
+
injectHeaders: InjectHeaders | undefined;
|
|
368
|
+
/**
|
|
369
|
+
* Response value, using Response generic
|
|
370
|
+
*/
|
|
371
|
+
response?: T | undefined;
|
|
372
|
+
}
|
|
373
|
+
type NetaceaMitigationResponse<T> = MitigateResponse<T> | InjectResponse<T> | undefined;
|
|
374
|
+
interface FindBestMitigationResponse {
|
|
375
|
+
sessionStatus: string;
|
|
376
|
+
mitigation: string;
|
|
377
|
+
parts: NetaceaParts;
|
|
378
|
+
}
|
|
379
|
+
interface NetaceaParts {
|
|
380
|
+
match: number;
|
|
381
|
+
mitigate: number;
|
|
382
|
+
captcha: number;
|
|
383
|
+
}
|
|
384
|
+
interface APICallResponse {
|
|
385
|
+
status: number;
|
|
386
|
+
body?: string;
|
|
387
|
+
}
|
|
388
|
+
interface ProcessMitigateRequestArgs {
|
|
389
|
+
url: string;
|
|
390
|
+
method: string;
|
|
391
|
+
mitata: string | undefined;
|
|
392
|
+
mitataCaptcha: string | undefined;
|
|
393
|
+
clientIp: string;
|
|
394
|
+
userAgent: string;
|
|
395
|
+
getBodyFn: () => Promise<string>;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
interface CloudfrontConstructorArgs extends NetaceaBaseArgs, KinesisIngestArgs {
|
|
399
|
+
ingestEnabled?: boolean;
|
|
400
|
+
cookieEncryptionKey?: string;
|
|
401
|
+
netaceaCaptchaPath?: string;
|
|
402
|
+
captchaHeader?: CustomHeader;
|
|
403
|
+
dynamicCaptchaContentType?: boolean;
|
|
404
|
+
netaceaCookieAttributes?: string;
|
|
405
|
+
netaceaCaptchaCookieAttributes?: string;
|
|
406
|
+
/**
|
|
407
|
+
* The name of the header from which to retrieve the client's IP address.
|
|
408
|
+
*/
|
|
409
|
+
ipHeaderName?: string;
|
|
410
|
+
}
|
|
411
|
+
interface CustomHeader {
|
|
412
|
+
name: string;
|
|
413
|
+
value: string;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
interface MakeRequestResponse {
|
|
417
|
+
status: number;
|
|
418
|
+
headers: Record<string, string | string[]>;
|
|
419
|
+
body?: any;
|
|
420
|
+
}
|
|
421
|
+
declare class Cloudfront {
|
|
422
|
+
static NetaceaCookieHeader: string;
|
|
423
|
+
static NetaceaTrueUserAgentHeader: string;
|
|
424
|
+
private readonly cookieEncryptionKey;
|
|
425
|
+
ingestEnabled: boolean;
|
|
426
|
+
private readonly netaceaCaptchaPath?;
|
|
427
|
+
private readonly captchaHeader?;
|
|
428
|
+
private readonly dynamicCaptchaContentType;
|
|
429
|
+
private readonly ipHeaderName?;
|
|
430
|
+
protected mitataCookieExpirySeconds: number;
|
|
431
|
+
protected apiKey: string;
|
|
432
|
+
protected secretKey?: string;
|
|
433
|
+
protected mitigationServiceUrl: string;
|
|
434
|
+
protected ingestServiceUrl: string;
|
|
435
|
+
protected readonly timeout: number;
|
|
436
|
+
protected readonly captchaSiteKey?: string;
|
|
437
|
+
protected readonly captchaSecretKey?: string;
|
|
438
|
+
protected readonly ingestType: NetaceaIngestType;
|
|
439
|
+
protected readonly logVersion: NetaceaLogVersion;
|
|
440
|
+
protected readonly kinesis?: KinesisIngest;
|
|
441
|
+
protected readonly mitigationType: NetaceaMitigationType;
|
|
442
|
+
protected readonly encryptedCookies: string[];
|
|
443
|
+
protected readonly netaceaCookieName: string;
|
|
444
|
+
protected readonly netaceaCaptchaCookieName: string;
|
|
445
|
+
protected readonly netaceaCookieAttributes: string;
|
|
446
|
+
protected readonly netaceaCaptchaCookieAttributes: string;
|
|
447
|
+
constructor(options: CloudfrontConstructorArgs);
|
|
448
|
+
run(cloudfrontEvent: CloudFrontRequestEvent): Promise<{
|
|
449
|
+
respondWith?: CloudFrontResultResponse;
|
|
450
|
+
}>;
|
|
451
|
+
protected makeRequest({ host, path, method, body, headers, timeout }: MakeRequestArgs): Promise<MakeRequestResponse>;
|
|
452
|
+
protected mitigate(cfRequest: CloudFrontRequest): Promise<MitigateResponse<CloudFrontResultResponse>>;
|
|
453
|
+
protected inject(request: CloudFrontRequest): Promise<InjectResponse>;
|
|
454
|
+
ingest(requestOrEvent: CloudFrontRequestEvent | CloudFrontResponseEvent | CloudFrontRequest, response?: CloudFrontResultResponse | CloudFrontResponse | undefined): Promise<any>;
|
|
455
|
+
addNetaceaCookiesToResponse(cloudfrontEvent: CloudFrontResponseEvent): void;
|
|
456
|
+
private setInjectHeaders;
|
|
457
|
+
private getValueFromHeaderOrDefault;
|
|
458
|
+
private getMitataValueFromHeaderOrDefault;
|
|
459
|
+
private getRequestResponseFromEvent;
|
|
460
|
+
private getMitigationResponse;
|
|
461
|
+
private addNetaceaCookiesToRequest;
|
|
462
|
+
getCookieHeader(request: CloudFrontRequest): string | null;
|
|
463
|
+
protected encryptCookieValue(cookieValue: string): Promise<string>;
|
|
464
|
+
protected decryptCookieValue(encryptedCookieValue: string): Promise<string>;
|
|
465
|
+
/**
|
|
466
|
+
* START -- NETACEA BASE METHODS
|
|
467
|
+
*/
|
|
468
|
+
runMitigation(request: CloudFrontRequest): Promise<NetaceaMitigationResponse<CloudFrontResultResponse>>;
|
|
469
|
+
/**
|
|
470
|
+
* Returns the value of the cookie with the given name from a string or list of cookies.
|
|
471
|
+
* If the cookie name is included in the encryptedCookies class property,
|
|
472
|
+
* then the cookie value will be decrypted automatically.
|
|
473
|
+
* The method may operate of either the HTTP Cookie or Set-Cookie headers.
|
|
474
|
+
* @param cookieName the name of the cookie to find.
|
|
475
|
+
* @param cookies the full list of cookies, either as a string or an array of strings.
|
|
476
|
+
* @returns the value of the cookie, if found.
|
|
477
|
+
*/
|
|
478
|
+
protected readCookie(cookieName: string, cookies: string | string[] | null | undefined): Promise<string | undefined>;
|
|
479
|
+
protected processMitigateRequest(args: ProcessMitigateRequestArgs & {
|
|
480
|
+
accept: string;
|
|
481
|
+
host: string;
|
|
482
|
+
}): Promise<ComposeResultResponse>;
|
|
483
|
+
protected isUrlCaptchaPost(url: string, method: string): boolean;
|
|
484
|
+
protected isUrlCaptchaGet(url: string, method: string): boolean;
|
|
485
|
+
protected shouldSetCaptchaPass(request: CloudFrontRequest, response: CloudFrontResponse | CloudFrontResultResponse): boolean;
|
|
486
|
+
private processCaptcha;
|
|
487
|
+
private makeCaptchaAPICall;
|
|
488
|
+
private getApiCallResponseFromResponse;
|
|
489
|
+
protected APIError(response: APICallResponse): Error;
|
|
490
|
+
protected createMitata(clientIP: string, userId: string | undefined, match: number, mitigate: number, captcha: number, maxAge?: number, expiry?: number | undefined): Promise<string>;
|
|
491
|
+
protected createMitataCaptcha(headers: Record<string, string | string[]>): Promise<string | undefined>;
|
|
492
|
+
private buildCookieFromValues;
|
|
493
|
+
protected callIngest(args: IngestArgs): Promise<void>;
|
|
494
|
+
protected constructWebLog(args: IngestArgs): WebLog | V2WebLog;
|
|
495
|
+
private constructV2WebLog;
|
|
496
|
+
private constructV1WebLog;
|
|
497
|
+
private makeIngestApiCall;
|
|
498
|
+
protected processIngest(request: CloudFrontRequest): Promise<NetaceaResponseBase>;
|
|
499
|
+
protected setIngestOnlyMitataCookie(userId: string | undefined): Promise<NetaceaResponseBase>;
|
|
500
|
+
protected findBestMitigation(match: number, mitigate: number, captcha: number, isCaptchaPost: boolean): FindBestMitigationResponse;
|
|
501
|
+
private adjustCaptchaCode;
|
|
502
|
+
protected check(netaceaCookie: string | undefined, clientIP: string, userAgent: string, accept: string, host: string, captchaCookie?: string): Promise<ComposeResultResponse>;
|
|
503
|
+
private makeMitigateAPICall;
|
|
504
|
+
private buildCookieHeader;
|
|
505
|
+
private composeResult;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export { Cloudfront, type CloudfrontConstructorArgs };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("node:crypto"),t=require("node:buffer"),a=require("axios"),i=require("aws4");function s(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(a){if("default"!==a){var i=Object.getOwnPropertyDescriptor(e,a);Object.defineProperty(t,a,i.get?i:{enumerable:!0,get:function(){return e[a]}})}})),t.default=e,Object.freeze(t)}var o,r,n,c,h=s(require("jose"));!function(e){e.ORIGIN="ORIGIN",e.HTTP="HTTP",e.KINESIS="KINESIS",e.NATIVE="NATIVE"}(o||(o={})),function(e){e.V1="V1",e.V2="V2"}(r||(r={})),function(e){e.MITIGATE="MITIGATE",e.INJECT="INJECT",e.INGEST="INGEST"}(n||(n={})),function(e){e.CAPTCHA_GET="captcha_get",e.CAPTCHA_POST="captcha_post",e.EXPIRED_SESSION="expired_session",e.FORCED_REVALIDATION="forced_revalidation",e.INVALID_SESSION="invalid_session",e.IP_CHANGE="ip_change",e.NO_SESSION="no_session"}(c||(c={}));function u(e,t=0){return isNaN(e)?t:parseInt(e)}const d=3e3;const p="_/@#/",l={none:"",block:"block",captcha:"captcha",allow:"allow",captchaPass:"captchapass"},m={0:l.none,1:l.block,2:l.none,3:l.block,4:l.block},g={1:l.captcha,2:l.captchaPass,3:l.captcha,4:l.allow,5:l.captcha};var y=Object.freeze({__proto__:null,COOKIEDELIMITER:p,bestMitigationCaptchaMap:g,bestMitigationMap:m,captchaMap:{0:"",1:"captcha_serve",2:"captcha_pass",3:"captcha_fail",4:"captcha_cookiepass",5:"captcha_cookiefail"},captchaStatusCodes:{"":0,captchaServe:1,captchaPass:2,captchaFail:3,captchaCookiePass:4,captchaCookieFail:5},matchMap:{0:"",1:"ua_",2:"ip_",3:"visitor_",4:"datacenter_",5:"sev_",6:"organisation_",7:"asn_",8:"country_",9:"combination_"},mitigateMap:{0:"",1:"blocked",2:"allow",3:"hardblocked",4:"block"},mitigationTypes:l,netaceaCookieV3KeyMap:{clientIP:"cip",userId:"uid",gracePeriod:"grp",cookieId:"cid",match:"mat",mitigate:"mit",captcha:"cap",issueTimestamp:"ist",issueReason:"isr"},netaceaCookieV3OptionalKeyMap:{checkAllPostRequests:"fCAPR"},netaceaHeaders:{match:"x-netacea-match",mitigate:"x-netacea-mitigate",captcha:"x-netacea-captcha",mitata:"x-netacea-mitata-value",mitataExpiry:"x-netacea-mitata-expiry",mitataCaptcha:"x-netacea-mitatacaptcha-value",mitataCaptchaExpiry:"x-netacea-mitatacaptcha-expiry",eventId:"x-netacea-event-id"},netaceaSettingsMap:{checkAllPostRequests:"checkAllPostRequests"}});const k="ignored",C="1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".split(""),f=/^(.*)_\/@#\/(.*)_\/@#\/(.*)_\/@#\/(.*)_\/@#\/((\d)(\d)(\d))$/;function v(e){if(void 0===e)return;const t=e.match(f);if(null!=t){const[,e,a,i,s,o,r,n,c]=t;return{signature:e,expiry:a,userId:i,ipHash:s,mitigationType:o,match:parseInt(r),mitigate:parseInt(n),captcha:parseInt(c)}}}function S(t,a,i,s,o="000"){void 0===a&&(a=function(t=16,a=C){const i=e.randomBytes(t-1);return`c${Array.from(i).map((e=>a[e%a.length])).join("")}`}());const r=[i,a,N(t+"|"+String(i),s),o].join(p);return`${N(r,s)}${p}${r}`}function N(a,i){const s=e.createHmac("sha256",i);return s.update(a),t.Buffer.from(s.digest("hex")).toString("base64")}function I(e,t,a){const i={mitata:void 0,requiresReissue:!1,isExpired:!1,shouldExpire:!1,isSameIP:!1,isPrimaryHashValid:!1,captcha:0,match:0,mitigate:0};if("string"!=typeof e||""===e)return i;const s=v(e);if(void 0!==s){const e=[s.expiry,s.userId,s.ipHash,s.mitigationType].join(p),i=Math.floor(Date.now()/1e3),o=parseInt(s.expiry)<i,r=[1,3,5].includes(s.captcha),n=3===s.mitigate,c=r||n,h=N(t+"|"+s.expiry,a),u=s.ipHash===h;return{mitata:s,requiresReissue:o||!u,isExpired:o,shouldExpire:c,isSameIP:u,isPrimaryHashValid:s.signature===N(e,a),match:s.match,mitigate:s.mitigate,captcha:s.captcha}}return i}function A(e,t){const a=e.split(";").map((e=>e.trim())).filter((e=>e.toLowerCase().startsWith(t.toLowerCase())))[0];return void 0!==a&&a.length>0?a?.replace(`${t}=`,""):void 0}function b(e,t=!1){return"string"!=typeof e&&(e=e.join("; ")),""===e?"":w(e.split(";"),t).join("; ")}function w(e,t=!1){if(t)return w(e.reverse()).reverse();const a=new Set,i=[];for(let t of e){if(t=t.trimStart(),""===t.trim())continue;const e=t.split("=")[0].toUpperCase();a.has(e)||(a.add(e),i.push(t))}return i}var E=Object.freeze({__proto__:null,configureCookiesDomain:function(e,t){let a=e=b(e??"",!0),i=t=b(t??"",!0);if(void 0!==e&&void 0!==t){const s=A(e,"Domain"),o=A(t,"Domain");void 0!==s&&void 0!==o?i=t.replace(o,s):void 0!==s&&void 0===o?i=t+(""!==t?`; Domain=${s}`:`Domain=${s}`):void 0===s&&void 0!==o&&(a=e+(""!==e?`; Domain=${o}`:`Domain=${o}`))}else if(void 0!==e&&void 0===t){const t=A(e,"Domain");void 0!==t&&(i=`Domain=${t}`)}else if(void 0===e&&void 0!==t){const e=A(t,"Domain");void 0!==e&&(a=`Domain=${e}`)}return{cookieAttributes:""!==a?a:void 0,captchaCookieAttributes:""!==i?i:void 0}},extractAndRemoveCookieAttr:function(e,t){const a=A(e,t);if(void 0!==a){return{extractedAttribute:a,cookieAttributes:e.replace(/ /g,"").replace(`${t}=${a}`,"").split(";").filter((e=>e.length>0)).join("; ")}}return{extractedAttribute:void 0,cookieAttributes:e}},extractCookieAttr:A,removeDuplicateAttrs:b});function T(e){const t=b([e.otherAttributes??"",`Max-Age=${e.maxAgeAttribute??86400}`,"Path=/"].join("; "));return`${e.cookieName}=${e.cookieValue}; ${t}`}var K=Object.freeze({__proto__:null,createNetaceaCaptchaSetCookieString:function(e){return T({...e,cookieName:e.cookieName??"_mitatacaptcha"})},createNetaceaSetCookieString:function(e){return T({...e,cookieName:e.cookieName??"_mitata"})},createSetCookieString:T});var x=Object.freeze({__proto__:null,parseSetCookie:function(e){const t=e.indexOf("=");if(t<0)throw new Error("Could not parse the given set-cookie value.");const a=e.slice(0,t),i=e.slice(t+1),s=i.indexOf(";");return{name:a,value:i.slice(0,s),attributes:i.slice(s).trimStart()}}});const R={cookie:{parse:x,attributes:E,netaceaSession:K}};var P={},_={},O={},H={};Object.defineProperty(H,"__esModule",{value:!0}),H.API_VERSION=H.REGION=H.PAYLOAD_TYPE=H.STATE=void 0,H.STATE={ACTIVE:"ACTIVE",UPDATING:"UPDATING",CREATING:"CREATING",DELETING:"DELETING"},H.PAYLOAD_TYPE="string",H.REGION="eu-west-1",H.API_VERSION="2013-12-02",Object.defineProperty(O,"__esModule",{value:!0}),O.signRequest=void 0;const M=i,q=H;function V(e,t){const a=[];for(let i=0;i<e.length;i+=t){const s=e.slice(i,i+t);a.push({Data:Buffer.from(JSON.stringify(s)).toString("base64"),PartitionKey:Date.now().toString()})}return a}O.signRequest=function(e,t,a){const{accessKeyId:i,secretAccessKey:s}=e,o={Records:V(t,a),PartitionKey:Date.now().toString(),StreamName:e.streamName};return M.sign({service:"kinesis",body:JSON.stringify(o),headers:{"Content-Type":"application/x-amz-json-1.1","X-Amz-Target":"Kinesis_20131202.PutRecords"},region:q.REGION},{accessKeyId:i,secretAccessKey:s})},Object.defineProperty(_,"__esModule",{value:!0});const D=O;_.default=class{constructor({kinesisStreamName:e,kinesisAccessKey:t,kinesisSecretKey:a,maxLogAgeSeconds:i,logBatchSize:s}){this.logBatchSize=20,this.maxLogAgeSeconds=10,this.logCache=[],this.intervalSet=!1,this.kinesisStreamName=e,this.kinesisAccessKey=t,this.kinesisSecretKey=a,void 0!==i&&i<this.maxLogAgeSeconds&&i>0&&(this.maxLogAgeSeconds=i),void 0!==s&&(this.logBatchSize=s)}async putToKinesis(e){if(0===this.logCache.length)return;const t=[...this.logCache];this.logCache=[];try{const a=(0,D.signRequest)({streamName:this.kinesisStreamName,accessKeyId:this.kinesisAccessKey,secretAccessKey:this.kinesisSecretKey},t,this.logBatchSize);await e({headers:a.headers,host:`https://${a.hostname}`,method:a.method,path:a.path,body:a.body})}catch(e){this.logCache.push(...t),console.error(e)}}async ingest(e,t){this.logCache.push(e),this.intervalSet||(this.intervalSet=!0,await async function(e){await new Promise((t=>{setTimeout(t,e)}))}(1e3*this.maxLogAgeSeconds),await this.putToKinesis(t),this.intervalSet=!1),this.logCache.length>=this.logBatchSize&&await this.putToKinesis(t)}},Object.defineProperty(P,"__esModule",{value:!0});const F=_;var $=P.default=F.default;async function j(e,t){const a=h.base64url.decode(t),{plaintext:i}=await h.compactDecrypt(e,a,{keyManagementAlgorithms:["dir"],contentEncryptionAlgorithms:["A256GCM"]});return(new TextDecoder).decode(i)}function U(e,t){const{clientIp:a}=e;if(void 0===t||""===t)return a;const i=e.headers[t]?.[0]?.value;return void 0===i||""===i?a:"x-forwarded-for"===t?i.split(/, ?/).pop()??a:i}async function L(e,t,a){const i=t.cookie?.[0].value.split(";"),s=i?.find((t=>t.includes(`${e}=`)))?.trimStart()?.replace(`${e}=`,"");if(void 0!==s){if(void 0!==a)try{return await j(s,a)}catch(e){return}return s}}function G(e){const t={"set-cookie":[]};for(const a of e)t["set-cookie"]?.push({key:"set-cookie",value:a});return t}function B(e,t){const a=e[t];return"string"==typeof a?a:a?.[0]}function X(e,t){const a=B(e,t);if(void 0!==a)return parseInt(a)}function J(e,t,a){"/"!==t[0]&&(t=`/${t}`);const i=t.split("?"),s=i[0],o=i.length>1?`?${i[1]}`:void 0;return{path:s,query:o,request:`${e} ${s}${o??""}${""!==(a??"")?` ${a}`:""}`}}const{extractCookieAttr:z,extractAndRemoveCookieAttr:W,removeDuplicateAttrs:Y}=R.cookie.attributes,Z=R.cookie.parse.parseSetCookie,{configureCookiesDomain:Q}=R.cookie.attributes,{matchMap:ee,mitigationTypes:te,mitigateMap:ae,bestMitigationMap:ie,bestMitigationCaptchaMap:se,captchaMap:oe,netaceaHeaders:re}=y;class ne{static NetaceaCookieHeader="x-netacea-cloudfront-mitata-cookie";static NetaceaTrueUserAgentHeader="x-netacea-true-useragent-header";cookieEncryptionKey;ingestEnabled=!0;netaceaCaptchaPath;captchaHeader;dynamicCaptchaContentType;ipHeaderName;mitataCookieExpirySeconds;apiKey;secretKey;mitigationServiceUrl;ingestServiceUrl;timeout;captchaSiteKey;captchaSecretKey;ingestType;logVersion;kinesis;mitigationType;encryptedCookies=[];netaceaCookieName;netaceaCaptchaCookieName;netaceaCookieAttributes;netaceaCaptchaCookieAttributes;constructor(e){if(e.ingestType=o.KINESIS,void 0===e.kinesis&&(console.warn(['NETACEA :: Please move kinesis params to "kinesis" object in config.',"Backwards compatibility will soon be removed."].join(" ")),e.kinesis={kinesisStreamName:e.kinesisStreamName,kinesisAccessKey:e.kinesisAccessKey,kinesisSecretKey:e.kinesisSecretKey,maxLogAgeSeconds:1}),null===e.apiKey||void 0===e.apiKey)throw new Error("apiKey is a required parameter");var t;this.apiKey=e.apiKey,this.secretKey=e.secretKey,this.mitigationServiceUrl=e.mitigationServiceUrl??"https://mitigations.netacea.net",this.ingestServiceUrl=e.ingestServiceUrl??"https://ingest.netacea.net",this.mitigationType=e.mitigationType??n.INGEST,this.ingestType=e.ingestType??o.HTTP,this.logVersion=e.logVersion??r.V1,this.ingestType===o.KINESIS&&(void 0===e.kinesis?console.warn(`NETACEA WARN: no kinesis args provided, when ingestType is ${this.ingestType}`):this.kinesis=new $({...e.kinesis,apiKey:this.apiKey})),void 0===e.captchaSiteKey&&void 0===e.captchaSecretKey||(this.captchaSiteKey=e.captchaSiteKey,this.captchaSecretKey=e.captchaSecretKey),this.timeout=(t=e.timeout??3e3)<=0?d:t,this.netaceaCookieName=e.netaceaCookieName??"_mitata",this.netaceaCaptchaCookieName=e.netaceaCaptchaCookieName??"_mitatacaptcha",this.netaceaCaptchaPath=e.netaceaCaptchaPath,this.dynamicCaptchaContentType=e.dynamicCaptchaContentType??!1;const a=Q(e.netaceaCookieAttributes??"",e.netaceaCaptchaCookieAttributes??"");var i,s;this.netaceaCookieAttributes=a.cookieAttributes??"",this.netaceaCaptchaCookieAttributes=a.captchaCookieAttributes??"",this.captchaHeader=e.captchaHeader,this.ipHeaderName=e.ipHeaderName?.toLowerCase()?.trim(),this.encryptedCookies=[this.netaceaCookieName,this.netaceaCaptchaCookieName],this.mitataCookieExpirySeconds=(i=this.mitigationType,void 0===(s=e.netaceaCookieExpirySeconds??e.mitataCookieExpirySeconds)?i===n.INGEST?3600:60:s),this.ingestEnabled=e.ingestEnabled??!0,this.cookieEncryptionKey=e.cookieEncryptionKey}async run(e){try{const{request:t}=this.getRequestResponseFromEvent(e),{uri:a,method:i}=t;if(this.isUrlCaptchaGet(a,i)){const a=await async function({request:e,secretKey:t,mitigationCallFn:a,composeResultFn:i,cookieEncryptionKey:s,netaceaCookieName:o,netaceaCaptchaCookieName:r,ipHeaderName:n}){const{querystring:c}=e,h=U(e,n),u=e.headers["user-agent"]?.[0].value??"",d=e.headers.accept?.[0].value??"text/html",p=e.headers.host?.[0].value??"";if(void 0===t)throw new Error("Secret key needs to be defined to make mitigation calls.");const l=c.split("&").find((e=>e.includes("trackingId=")))?.replace("trackingId=",""),{headers:m}=e,g=await L(o,m,s),y=await L(r,m,s),{userId:k}=v(g)??{},C=await async function({userId:e,clientIp:t,userAgent:a,trackingId:i,accept:s,host:o,captchaCookie:r,mitigationCallFn:n,composeResultFn:c}){const h={match:0,mitigate:0,captcha:1},u=await n({userId:e,clientIP:t,userAgent:a,captchaCookie:r,accept:s,host:o,isCaptchaGet:!0,defaultMitataCodes:h,trackingId:i});return c(u.body,u.setCookie,u.status,u.match,u.mitigate,u.captcha,!0)}({userId:k,clientIp:h,userAgent:u,captchaCookie:y,accept:d,host:p,trackingId:l,mitigationCallFn:a,composeResultFn:i});return{headers:G(C.setCookie),status:"403",body:C.body,statusDescription:"Forbidden"}}({request:t,secretKey:this.secretKey,mitigationCallFn:this.makeMitigateAPICall.bind(this),composeResultFn:this.composeResult.bind(this),cookieEncryptionKey:this.cookieEncryptionKey,netaceaCookieName:this.netaceaCookieName,netaceaCaptchaCookieName:this.netaceaCaptchaCookieName,ipHeaderName:this.ipHeaderName});return this.ingest(e,a),{respondWith:a}}const s=await this.runMitigation(t);return this.addNetaceaCookiesToRequest(t,s),t.headers[ne.NetaceaTrueUserAgentHeader]=[{key:ne.NetaceaTrueUserAgentHeader,value:this.getValueFromHeaderOrDefault(t.headers,"user-agent","-")}],{respondWith:s?.response}}catch(e){return console.error("Netacea FailOpen - ",e.message),{}}}async makeRequest({host:e,path:t,method:i,body:s,headers:o,timeout:r}){const n=`${e}${t}`,c=await a.request({url:n,data:s,headers:o,method:i,timeout:r,transformResponse:e=>e});return{headers:c.headers,status:c.status,body:c.data}}async mitigate(e){try{const{netaceaResult:a,request:i}=await this.getMitigationResponse(e);let s;if(a.mitigated){const e={"set-cookie":[]};for(const t of a.setCookie)e["set-cookie"]=e["set-cookie"]??[],e["set-cookie"].push({key:"set-cookie",value:t});"captcha"===a.mitigation&&void 0!==this.captchaHeader&&(e[this.captchaHeader.name]=[{key:this.captchaHeader.name,value:this.captchaHeader.value}]);s={headers:e,...this.isUrlCaptchaPost(i.uri,i.method)?{status:"200",statusDescription:"OK",body:""}:{status:"403",statusDescription:"Forbidden",body:"Forbidden"}};let o=0;if(void 0!==a.body&&a.body.length>0){o=a.body.length;const e=(t=a.body).includes("captchaRelativeURL")&&t.includes("captchaAbsoluteURL");s.status=e?"403":"200",s.statusDescription=e?"Forbidden":"OK",s.body=a.body,s.bodyEncoding="text"}const r={status:s.status,statusDescription:s.statusDescription??"",headers:{"content-length":[{key:"content-length",value:o.toString()}],"set-cookie":a.setCookie.map((e=>({key:"set-cookie",value:e})))}};this.ingest(i,r)}return this.addNetaceaCookiesToRequest(i,a),{response:s,sessionStatus:a.sessionStatus,setCookie:a.setCookie}}catch(e){return console.error("Netacea FailOpen Error: ",e),{sessionStatus:""}}var t}async inject(e){try{const{netaceaResult:t}=await this.getMitigationResponse(e);return{injectHeaders:t.injectHeaders,sessionStatus:t.sessionStatus,setCookie:t.setCookie}}catch(e){return console.error("Netacea FailOpen Error: ",e),{sessionStatus:"",injectHeaders:void 0,setCookie:void 0}}}async ingest(e,t=void 0){let a;if(Object.prototype.hasOwnProperty.call(e,"Records")){const i=this.getRequestResponseFromEvent(e);a=i.request,void 0===t&&(t=i.response)}else a=e;if(!this.ingestEnabled)return;if(null==t)throw new Error("Cloudfront response is required to ingest");const i=this.getMitataValueFromHeaderOrDefault(t.headers,"set-cookie"),s=""!==i?i:this.getMitataValueFromHeaderOrDefault(a.headers,"cookie");let o=await this.readCookie(this.netaceaCookieName,s)??"";if(void 0===o||""===o){const e=this.getMitataValueFromHeaderOrDefault(a.headers,"cookie");o=await this.readCookie(this.netaceaCookieName,e)??""}let r=0,n=0,c=0;const h=v(o);void 0!==h&&(r=h.match,n=h.mitigate,c=h.captcha);const u=this.shouldSetCaptchaPass(a,t),{sessionStatus:d}=this.findBestMitigation(r,n,c,u);await this.callIngest({bytesSent:this.getValueFromHeaderOrDefault(t.headers,"content-length","0"),ip:U(a,this.ipHeaderName),method:a.method,path:a.uri,protocol:null,referer:this.getValueFromHeaderOrDefault(a.headers,"referer"),requestTime:"0",status:t.status,userAgent:this.getValueFromHeaderOrDefault(a.headers,ne.NetaceaTrueUserAgentHeader,this.getValueFromHeaderOrDefault(a.headers,"user-agent")),mitataCookie:o,sessionStatus:d,integrationType:"@netacea/cloudfront".replace("@netacea/",""),integrationVersion:"6.0.1",xForwardedFor:this.getValueFromHeaderOrDefault(a.headers,"x-forwarded-for")})}addNetaceaCookiesToResponse(e){const{response:t,request:a}=this.getRequestResponseFromEvent(e);if(void 0===t)throw new Error("Response required to add cookies to response");const i=a.headers[ne.NetaceaCookieHeader];if(null!=i&&null!=t.headers){let e=!1;if(void 0===t.headers["set-cookie"]?t.headers["set-cookie"]=[]:e=void 0!==t.headers["set-cookie"].find((e=>e.value.includes(this.netaceaCookieName)||e.value.includes(this.netaceaCaptchaCookieName))),!e)for(const e of i)t.headers["set-cookie"].push({key:"set-cookie",value:e.value})}this.setInjectHeaders(e)}setInjectHeaders(e){const{response:t,request:a}=this.getRequestResponseFromEvent(e);if(null!=t?.headers){const e=a.headers["x-netacea-match"],i=a.headers["x-netacea-mitigate"],s=a.headers["x-netacea-captcha"],o=a.headers["x-netacea-event-id"];void 0!==e&&void 0!==i&&void 0!==s&&(t.headers["x-netacea-match"]=e,t.headers["x-netacea-mitigate"]=i,t.headers["x-netacea-captcha"]=this.shouldSetCaptchaPass(a,t)?[{key:"x-netacea-captcha",value:"2"}]:s),void 0!==o&&(t.headers["x-netacea-event-id"]=o)}}getValueFromHeaderOrDefault(e,t,a=""){if(void 0!==e?.[t]){const a=e[t];if(void 0!==a)return a[0].value}return a}getMitataValueFromHeaderOrDefault(e,t,a=""){if(void 0!==e?.[t]){const a=e[t];if(void 0!==a){const e=a.find((e=>e.value.includes(this.netaceaCookieName)));if(void 0!==e)return e.value}}return a}getRequestResponseFromEvent(e){return e.Records[0].cf}async getMitigationResponse(e){const t=this.getMitataValueFromHeaderOrDefault(e.headers,"cookie"),a=await this.readCookie(this.netaceaCookieName,t),i=await this.readCookie(this.netaceaCaptchaCookieName,t),s=U(e,this.ipHeaderName),o=this.getValueFromHeaderOrDefault(e.headers,"user-agent"),r=this.getValueFromHeaderOrDefault(e.headers,"accept","text/html"),n=this.getValueFromHeaderOrDefault(e.headers,"host");return{netaceaResult:await this.processMitigateRequest({getBodyFn:async()=>await Promise.resolve(Buffer.from(e.body?.data??"","base64").toString()),clientIp:s,method:e.method,url:e.uri,userAgent:o,accept:r,host:n,mitata:a,mitataCaptcha:i}),request:e}}addNetaceaCookiesToRequest(e,t){if(void 0===t)return e;if(e.headers[ne.NetaceaCookieHeader]=[],void 0!==t.setCookie)for(const a of t.setCookie){const t=e.headers[ne.NetaceaCookieHeader]??[];t.push({key:ne.NetaceaCookieHeader,value:a}),e.headers[ne.NetaceaCookieHeader]=t}if(this.mitigationType===n.INJECT)for(const[a,i]of Object.entries(t.injectHeaders??{}))e.headers[a]=[{key:a,value:i}];return e}getCookieHeader(e){return this.getMitataValueFromHeaderOrDefault(e.headers,"cookie")}async encryptCookieValue(e){return void 0!==this.cookieEncryptionKey?await async function(e,t){const a=h.base64url.decode(t),i=(new TextEncoder).encode(e);return await new h.CompactEncrypt(i).setProtectedHeader({alg:"dir",enc:"A256GCM"}).encrypt(a)}(e,this.cookieEncryptionKey):e}async decryptCookieValue(e){return void 0!==this.cookieEncryptionKey?await j(e,this.cookieEncryptionKey):e}async runMitigation(e){try{switch(this.mitigationType){case n.MITIGATE:return await this.mitigate(e);case n.INJECT:return await this.inject(e);case n.INGEST:return await this.processIngest(e);default:throw new Error(`Netacea Error: Mitigation type ${this.mitigationType} not recognised`)}}catch(e){return console.error("Netacea FAILOPEN Error:",e),{injectHeaders:{"x-netacea-captcha":"0","x-netacea-match":"0","x-netacea-mitigate":"0"},sessionStatus:""}}}async readCookie(e,t){if(null==t)return;if("string"==typeof t)return await this.readCookie(e,t.split(";"));const a=`${e}=`;for(const i of t){const t=i.split(";")[0].trimStart();if(t.startsWith(a)){const i=t.slice(a.length);if(this.encryptedCookies.includes(e))try{return await this.decryptCookieValue(i)}catch(e){return}return i}}}async processMitigateRequest(e){const t=this.isUrlCaptchaPost(e.url,e.method);return await(t?this.processCaptcha(e.mitata,e.clientIp,e.userAgent,await e.getBodyFn()):this.check(e.mitata,e.clientIp,e.userAgent,e.accept,e.host,e.mitataCaptcha))}isUrlCaptchaPost(e,t){return e.includes("/AtaVerifyCaptcha")&&"post"===t.toLowerCase()}isUrlCaptchaGet(e,t){return e.toLowerCase()===this.netaceaCaptchaPath?.toLowerCase()&&"get"===t.toLowerCase()}shouldSetCaptchaPass(e,t){if(this.isUrlCaptchaPost(e.uri,e.method))return!0;if(void 0===t)return!1;const a=null!=t.headers?t.headers["set-cookie"]:void 0,i=a?.find((e=>e.value.split("=")[0]===this.netaceaCaptchaCookieName)),s=void 0!==i;return this.mitigationType===n.INJECT&&s}async processCaptcha(e,t,a,i){const{status:s,match:o,mitigate:r,captcha:n,body:c,setCookie:h}=await this.makeCaptchaAPICall(e,t,a,i);return this.composeResult(c,h,s,o,r,n,!0)}async makeCaptchaAPICall(e,t,a,i){const s={"X-Netacea-API-Key":this.apiKey,"X-Netacea-Client-IP":t,"user-agent":a,"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"},o=v(e);void 0!==o&&(s["X-Netacea-UserId"]=o.userId),void 0!==this.captchaSiteKey&&void 0!==this.captchaSecretKey&&(s["X-Netacea-Captcha-Site-Key"]=this.captchaSiteKey,s["X-Netacea-Captcha-Secret-Key"]=this.captchaSecretKey);const r=await this.makeRequest({host:this.mitigationServiceUrl,path:"/AtaVerifyCaptcha",headers:s,method:"POST",body:i,timeout:this.timeout});return await this.getApiCallResponseFromResponse(r,o?.userId,t)}async getApiCallResponseFromResponse(e,t,a,i){if(200!==e.status)throw this.APIError(e);let s=X(e.headers,re.match)??NaN,o=X(e.headers,re.mitigate)??NaN,r=X(e.headers,re.captcha)??NaN;isNaN(s)&&(s=i?.match??0),isNaN(o)&&(o=i?.mitigate??0),isNaN(r)&&(r=i?.captcha??0);let n=X(e.headers,re.mitataExpiry)??NaN;isNaN(n)&&(n=86400);const c=[await this.createMitata(a,t,s,o,r),await this.createMitataCaptcha(e.headers)].filter((e=>void 0!==e)),h=B(e.headers,re.eventId);return{status:e.status,match:s,mitigate:o,captcha:r,setCookie:c,body:e.body,eventId:h,mitataMaxAge:n}}APIError(e){let t="Unknown error";switch(e.status){case 403:t="Invalid credentials";break;case 500:t="Server error";break;case 502:t="Bad Gateway";break;case 503:t="Service Unavailable";break;case 400:t="Invalid request"}return new Error(`Error reaching Netacea API (${t}), status: ${e.status}`)}async createMitata(e,t,a,i,s,o=86400,r=void 0){const n=[1,3,5].includes(s)||3===i?-60:this.mitataCookieExpirySeconds,c=r??Math.floor(Date.now()/1e3)+n;if(void 0===this.secretKey)throw new Error("Cannot build cookie without secret key.");const h=[a,i,s].join(""),u=S(e,t,c,this.secretKey,h);let d,p,l=o;if(""!==this.netaceaCookieAttributes){const{extractedAttribute:e,cookieAttributes:t}=W(this.netaceaCookieAttributes,"Max-Age");l=void 0!==e?Number(e):o;const{extractedAttribute:a,cookieAttributes:i}=W(t,"Path");d=a??"/",p=i??void 0}return await this.buildCookieFromValues(this.netaceaCookieName,u,l,p,d)}async createMitataCaptcha(e){let t=e["set-cookie"]??[];t="string"==typeof t?[t]:t;const a=t.find((e=>e.startsWith("_mitatacaptcha=")));let i,s="86400";if(void 0!==a&&""!==a)try{const e=Z(a);i=e.value,s=z(e.attributes,"Max-Age")??"86400"}catch(e){return}if(""===i||void 0===i)return;const o=Y([this.netaceaCaptchaCookieAttributes,"Path=/",`Max-Age=${s}`]);return i=this.encryptedCookies.includes(this.netaceaCaptchaCookieName)?await this.encryptCookieValue(i):i,`${this.netaceaCaptchaCookieName}=${i}; ${o}`}async buildCookieFromValues(e,t,a,i,s="/"){const o=`${e}=${this.encryptedCookies.includes(e)?await this.encryptCookieValue(t):t}; Max-Age=${a}; Path=${s}`;return void 0!==i&&""!==i?`${o}; ${i}`:o}async callIngest(e){const t=this.constructWebLog(e);if(this.ingestType===o.KINESIS){if(void 0===this.kinesis)return void console.error("Netacea Error: Unable to log as Kinesis has not been defined.");try{await this.kinesis.ingest({...t,apiKey:this.apiKey},this.makeRequest.bind(this))}catch(e){console.error("NETACEA Error: ",e.message)}}else{const e={"X-Netacea-API-Key":this.apiKey,"content-type":"application/json"},a=await this.makeIngestApiCall(e,t);if(200!==a.status)throw this.APIError(a)}}constructWebLog(e){return e.bytesSent=""===e.bytesSent?"0":e.bytesSent,this.logVersion===r.V2?this.constructV2WebLog(e):this.constructV1WebLog(e)}constructV2WebLog({ip:e,userAgent:t,status:a,method:i,path:s,protocol:o,referer:r,bytesSent:n,requestTime:c,mitataCookie:h,sessionStatus:d,integrationType:p,integrationVersion:l,xForwardedFor:m}){const g=new Date,{path:y,query:k,request:C}=J(i,s,o),f=v(h)?.userId;return{status:a,method:i,bytes_sent:u(n),referrer:""===r?void 0:r,request:C,request_time:u(c),integration_type:p,integration_version:l,client:e,user_agent:t,bc_type:""===d?void 0:d,hour:g.getUTCHours(),minute:g.getUTCMinutes(),"@timestamp":g.toISOString().replace("Z","+00:00"),path:y,protocol:o,query:k,user_id:f,x_forwarded_for:m}}constructV1WebLog({ip:e,userAgent:t,status:a,method:i,path:s,protocol:o,referer:r,bytesSent:n,requestTime:c,mitataCookie:h,sessionStatus:u,integrationType:d,integrationVersion:p,xForwardedFor:l}){const m=(new Date).toUTCString(),{request:g}=J(i,s,o);return{Request:g,TimeLocal:m,RealIp:e,UserAgent:t,Status:a,RequestTime:c?.toString(),BytesSent:n?.toString(),Referer:""===r?"-":r,NetaceaUserIdCookie:h??"",NetaceaMitigationApplied:u??"",IntegrationType:d??"",IntegrationVersion:p??"",XForwardedFor:l}}async makeIngestApiCall(e,t){return await this.makeRequest({host:this.ingestServiceUrl,method:"POST",path:"/",headers:e,body:JSON.stringify(t),timeout:this.timeout})}async processIngest(e){if(void 0===this.secretKey)throw new Error("Secret key is required for ingest");const t=this.getCookieHeader(e),a=I(await this.readCookie(this.netaceaCookieName,t),k,this.secretKey);return a.isPrimaryHashValid?a.requiresReissue?await this.setIngestOnlyMitataCookie(a.mitata?.userId):{sessionStatus:"",setCookie:[]}:await this.setIngestOnlyMitataCookie(void 0)}async setIngestOnlyMitataCookie(e){return{sessionStatus:"",setCookie:[await this.createMitata(k,e,0,0,0,86400)]}}findBestMitigation(e,t,a,i){const s="unknown";a=this.adjustCaptchaCode(a,i);let o=ee[e]??s+"_";o+=ae[t]??s;let r=ie[t];if(0!==a){o+=","+(oe[a]??s);const e=se[a];void 0!==e&&(r=e)}return this.mitigationType===n.INJECT&&(r=te.none),{sessionStatus:o,mitigation:r,parts:{match:e,mitigate:t,captcha:a}}}adjustCaptchaCode(e,t){let a=e;return t||(2===e?a=4:3===e&&(a=5)),a}async check(e,t,a,i,s,o){let r,n,c,h,u,d,p;if(void 0===this.secretKey)throw new Error("Secret key is required to mitigate");const l=I(e,t,this.secretKey);if(!l.isPrimaryHashValid||l.requiresReissue){const e=await this.makeMitigateAPICall({userId:l.mitata?.userId,clientIP:t,userAgent:a,captchaCookie:o,accept:i,host:s});r=e.status,n=e.match,c=e.mitigate,h=e.captcha,u=e.body,d=[await this.createMitata(t,l.mitata?.userId,n,c,h,e.mitataMaxAge)],p=e.eventId}else r=-1,n=l.match,c=l.mitigate,h=l.captcha,u=void 0,d=[];return this.composeResult(u,d,r,n,c,h,!1,p)}async makeMitigateAPICall({userId:e,clientIP:t,userAgent:a,captchaCookie:i,accept:s,host:o,isCaptchaGet:r=!1,defaultMitataCodes:n,trackingId:c}){const h={"X-Netacea-API-Key":this.apiKey,"X-Netacea-Client-IP":t,"user-agent":a,cookie:this.buildCookieHeader({_mitatacaptcha:i})};void 0!==e&&(h["X-Netacea-UserId"]=e),void 0!==this.captchaSiteKey&&void 0!==this.captchaSecretKey&&(h["X-Netacea-Captcha-Site-Key"]=this.captchaSiteKey,h["X-Netacea-Captcha-Secret-Key"]=this.captchaSecretKey),this.dynamicCaptchaContentType&&void 0!==this.netaceaCaptchaPath&&(h["X-Netacea-Captcha-Content-Type"]=function(e){const t=e?.toLowerCase()??"text/html",a=t?.includes("text/html")||t?.includes("application/html"),i=t?.includes("application/json");return i&&!a?"application/json":"text/html"}(s));const u="application/json"===h["X-Netacea-Captcha-Content-Type"],d=void 0!==c?`?trackingId=${c}`:"",p=await this.makeRequest({host:this.mitigationServiceUrl,path:r?`/captcha${d}`:"/",headers:h,method:"GET",timeout:this.timeout});return u&&void 0!==this.netaceaCaptchaPath&&(p.body=function(e,t,a){let i;if(void 0===e||""===e)return"";if("string"==typeof e&&(i=JSON.parse(e)),!function(e){if(null==e)return!1;const t=e;return void 0!==t?.captchaSiteKey&&void 0!==t?.trackingId&&void 0!==t?.captchaURL}(i))throw new Error("Body is not a Mitigation Service JSON response!");const s=`${a}?trackingId=${i.trackingId}`,o=`https://${t}${s}`;return JSON.stringify({captchaRelativeURL:s,captchaAbsoluteURL:o})}(p.body,o,this.netaceaCaptchaPath)),await this.getApiCallResponseFromResponse(p,e,t,n)}buildCookieHeader(e){let t="",a="";for(const i in e){const s=e[i];void 0!==s&&(t=`${t}${a}${i}=${s}`,a="; ")}return t}composeResult(e,t,a,i,s,o,r,c){const h=this.findBestMitigation(i,s,o,r),u={body:e,apiCallStatus:a,setCookie:t,sessionStatus:h.sessionStatus,mitigation:h.mitigation,mitigated:[te.block,te.captcha,te.captchaPass].includes(h.mitigation)};if(this.mitigationType===n.INJECT){const e={"x-netacea-match":h.parts.match.toString(),"x-netacea-mitigate":h.parts.mitigate.toString(),"x-netacea-captcha":h.parts.captcha.toString()};void 0!==c&&(e["x-netacea-event-id"]=c),u.injectHeaders=e}return u}}exports.Cloudfront=ne;
|
|
2
|
+
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netacea/cloudfront",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Netacea Cloudfront CDN integration",
|
|
5
|
-
"
|
|
6
|
-
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/index.js",
|
|
7
|
+
"dist/index.d.ts"
|
|
8
|
+
],
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
7
11
|
"scripts": {
|
|
8
12
|
"prepack": "npx netacea-bundler prepack",
|
|
9
13
|
"postpack": "npx netacea-bundler postpack"
|
|
@@ -14,9 +18,10 @@
|
|
|
14
18
|
},
|
|
15
19
|
"license": "ISC",
|
|
16
20
|
"dependencies": {
|
|
17
|
-
"@
|
|
21
|
+
"@types/aws-lambda": "^8.10.138",
|
|
22
|
+
"aws4": "1.11.0",
|
|
18
23
|
"axios": "^0.21.0",
|
|
19
24
|
"jose": "^4.11.2"
|
|
20
25
|
},
|
|
21
|
-
"gitHead": "
|
|
26
|
+
"gitHead": "e7403c128b50009211ff7af59995e380ac7ef6fc"
|
|
22
27
|
}
|
package/.vscode/launch.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "0.2.0",
|
|
3
|
-
"configurations": [
|
|
4
|
-
{
|
|
5
|
-
"type": "node",
|
|
6
|
-
"request": "launch",
|
|
7
|
-
"name": "Debug Tests",
|
|
8
|
-
"program": "${workspaceFolder}/node_modules/.bin/mocha",
|
|
9
|
-
"args": [
|
|
10
|
-
"-r",
|
|
11
|
-
"ts-node/register",
|
|
12
|
-
"./tests/mocha/*.test.ts"
|
|
13
|
-
],
|
|
14
|
-
"console": "internalConsole",
|
|
15
|
-
"skipFiles": [
|
|
16
|
-
"<node_internals>/**",
|
|
17
|
-
"${workspaceFolder}/node_modules/**"
|
|
18
|
-
],
|
|
19
|
-
}
|
|
20
|
-
]
|
|
21
|
-
}
|