nextjs-secure 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,679 @@
1
+ import { NextRequest } from 'next/server';
2
+
3
+ /**
4
+ * Log severity levels
5
+ */
6
+ type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'critical';
7
+ /**
8
+ * Security event types
9
+ */
10
+ type SecurityEventType = 'auth.login' | 'auth.logout' | 'auth.failed' | 'auth.token_expired' | 'auth.token_invalid' | 'auth.permission_denied' | 'ratelimit.exceeded' | 'ratelimit.warning' | 'csrf.invalid' | 'csrf.missing' | 'xss.detected' | 'sqli.detected' | 'validation.failed' | 'file.rejected' | 'ip.blocked' | 'ip.suspicious' | 'request.malformed' | 'request.timeout' | 'error.unhandled' | 'error.internal' | 'custom';
11
+ /**
12
+ * Log entry base
13
+ */
14
+ interface LogEntry {
15
+ id: string;
16
+ timestamp: Date;
17
+ level: LogLevel;
18
+ message: string;
19
+ category?: string;
20
+ metadata?: Record<string, unknown>;
21
+ }
22
+ /**
23
+ * Request log entry
24
+ */
25
+ interface RequestLogEntry extends LogEntry {
26
+ type: 'request';
27
+ request: {
28
+ id: string;
29
+ method: string;
30
+ url: string;
31
+ path: string;
32
+ query?: Record<string, string>;
33
+ headers?: Record<string, string>;
34
+ ip?: string;
35
+ userAgent?: string;
36
+ contentType?: string;
37
+ contentLength?: number;
38
+ };
39
+ response?: {
40
+ status: number;
41
+ headers?: Record<string, string>;
42
+ duration: number;
43
+ contentLength?: number;
44
+ };
45
+ user?: {
46
+ id?: string;
47
+ email?: string;
48
+ role?: string;
49
+ };
50
+ error?: {
51
+ name: string;
52
+ message: string;
53
+ stack?: string;
54
+ };
55
+ }
56
+ /**
57
+ * Security event entry
58
+ */
59
+ interface SecurityEventEntry extends LogEntry {
60
+ type: 'security';
61
+ event: SecurityEventType;
62
+ severity: 'low' | 'medium' | 'high' | 'critical';
63
+ source: {
64
+ ip?: string;
65
+ userAgent?: string;
66
+ userId?: string;
67
+ };
68
+ target?: {
69
+ resource?: string;
70
+ action?: string;
71
+ userId?: string;
72
+ };
73
+ details?: Record<string, unknown>;
74
+ mitigated?: boolean;
75
+ }
76
+ /**
77
+ * Audit log entry (union type)
78
+ */
79
+ type AuditLogEntry = RequestLogEntry | SecurityEventEntry;
80
+ /**
81
+ * Log store interface
82
+ */
83
+ interface LogStore {
84
+ write(entry: AuditLogEntry): Promise<void>;
85
+ query?(options: LogQueryOptions): Promise<AuditLogEntry[]>;
86
+ flush?(): Promise<void>;
87
+ close?(): Promise<void>;
88
+ }
89
+ /**
90
+ * Log query options
91
+ */
92
+ interface LogQueryOptions {
93
+ level?: LogLevel | LogLevel[];
94
+ type?: 'request' | 'security';
95
+ event?: SecurityEventType | SecurityEventType[];
96
+ startTime?: Date;
97
+ endTime?: Date;
98
+ ip?: string;
99
+ userId?: string;
100
+ limit?: number;
101
+ offset?: number;
102
+ }
103
+ /**
104
+ * Log formatter interface
105
+ */
106
+ interface LogFormatter {
107
+ format(entry: AuditLogEntry): string;
108
+ }
109
+ /**
110
+ * PII field configuration
111
+ */
112
+ interface PIIConfig {
113
+ fields: string[];
114
+ mode: 'mask' | 'hash' | 'remove';
115
+ maskChar?: string;
116
+ maskLength?: number;
117
+ preserveLength?: boolean;
118
+ customRedactor?: (value: string, field: string) => string;
119
+ }
120
+ /**
121
+ * Audit middleware configuration
122
+ */
123
+ interface AuditConfig {
124
+ enabled?: boolean;
125
+ store: LogStore;
126
+ formatter?: LogFormatter;
127
+ level?: LogLevel;
128
+ include?: {
129
+ ip?: boolean;
130
+ userAgent?: boolean;
131
+ headers?: boolean | string[];
132
+ query?: boolean;
133
+ body?: boolean | string[];
134
+ response?: boolean;
135
+ responseBody?: boolean;
136
+ duration?: boolean;
137
+ user?: boolean;
138
+ };
139
+ exclude?: {
140
+ paths?: string[];
141
+ methods?: string[];
142
+ statusCodes?: number[];
143
+ };
144
+ pii?: PIIConfig;
145
+ getUser?: (req: NextRequest) => Promise<{
146
+ id?: string;
147
+ email?: string;
148
+ role?: string;
149
+ } | null>;
150
+ requestIdHeader?: string;
151
+ generateRequestId?: () => string;
152
+ onError?: (error: Error, entry: Partial<AuditLogEntry>) => void;
153
+ skip?: (req: NextRequest) => boolean | Promise<boolean>;
154
+ }
155
+ /**
156
+ * Security event configuration
157
+ */
158
+ interface SecurityEventConfig {
159
+ store: LogStore;
160
+ formatter?: LogFormatter;
161
+ defaultSeverity?: 'low' | 'medium' | 'high' | 'critical';
162
+ onEvent?: (event: SecurityEventEntry) => void | Promise<void>;
163
+ }
164
+ /**
165
+ * Console store options
166
+ */
167
+ interface ConsoleStoreOptions {
168
+ colorize?: boolean;
169
+ timestamp?: boolean;
170
+ pretty?: boolean;
171
+ level?: LogLevel;
172
+ }
173
+ /**
174
+ * File store options
175
+ */
176
+ interface FileStoreOptions {
177
+ path: string;
178
+ maxSize?: number;
179
+ maxFiles?: number;
180
+ compress?: boolean;
181
+ rotationInterval?: 'hourly' | 'daily' | 'weekly';
182
+ }
183
+ /**
184
+ * Memory store options
185
+ */
186
+ interface MemoryStoreOptions {
187
+ maxEntries?: number;
188
+ ttl?: number;
189
+ }
190
+ /**
191
+ * External service store options
192
+ */
193
+ interface ExternalStoreOptions {
194
+ endpoint: string;
195
+ apiKey?: string;
196
+ headers?: Record<string, string>;
197
+ batchSize?: number;
198
+ flushInterval?: number;
199
+ retryAttempts?: number;
200
+ timeout?: number;
201
+ }
202
+
203
+ /**
204
+ * In-memory log store with LRU eviction
205
+ * Useful for development and testing
206
+ */
207
+ declare class MemoryStore implements LogStore {
208
+ private entries;
209
+ private readonly maxEntries;
210
+ private readonly ttl;
211
+ constructor(options?: MemoryStoreOptions);
212
+ write(entry: AuditLogEntry): Promise<void>;
213
+ query(options?: LogQueryOptions): Promise<AuditLogEntry[]>;
214
+ flush(): Promise<void>;
215
+ close(): Promise<void>;
216
+ /**
217
+ * Get all entries (for testing)
218
+ */
219
+ getEntries(): AuditLogEntry[];
220
+ /**
221
+ * Clear all entries
222
+ */
223
+ clear(): void;
224
+ /**
225
+ * Get entry count
226
+ */
227
+ size(): number;
228
+ /**
229
+ * Clean expired entries
230
+ */
231
+ private cleanExpired;
232
+ }
233
+ /**
234
+ * Create a memory store
235
+ */
236
+ declare function createMemoryStore(options?: MemoryStoreOptions): MemoryStore;
237
+
238
+ /**
239
+ * Console log store
240
+ * Outputs formatted logs to console
241
+ */
242
+ declare class ConsoleStore implements LogStore {
243
+ private readonly colorize;
244
+ private readonly showTimestamp;
245
+ private readonly pretty;
246
+ private readonly minLevel;
247
+ constructor(options?: ConsoleStoreOptions);
248
+ write(entry: AuditLogEntry): Promise<void>;
249
+ flush(): Promise<void>;
250
+ close(): Promise<void>;
251
+ /**
252
+ * Format entry in compact single-line format
253
+ */
254
+ private formatCompact;
255
+ /**
256
+ * Format entry in pretty multi-line format
257
+ */
258
+ private formatPretty;
259
+ /**
260
+ * Apply color if enabled
261
+ */
262
+ private color;
263
+ /**
264
+ * Color log level
265
+ */
266
+ private colorLevel;
267
+ /**
268
+ * Color HTTP status
269
+ */
270
+ private colorStatus;
271
+ /**
272
+ * Color severity
273
+ */
274
+ private colorSeverity;
275
+ }
276
+ /**
277
+ * Create a console store
278
+ */
279
+ declare function createConsoleStore(options?: ConsoleStoreOptions): ConsoleStore;
280
+
281
+ /**
282
+ * External HTTP log store
283
+ * Sends logs to external services (Datadog, Sentry, custom endpoints)
284
+ */
285
+ declare class ExternalStore implements LogStore {
286
+ private readonly endpoint;
287
+ private readonly headers;
288
+ private readonly batchSize;
289
+ private readonly flushInterval;
290
+ private readonly retryAttempts;
291
+ private readonly timeout;
292
+ private buffer;
293
+ private flushTimer;
294
+ private isFlushing;
295
+ constructor(options: ExternalStoreOptions);
296
+ write(entry: AuditLogEntry): Promise<void>;
297
+ flush(): Promise<void>;
298
+ close(): Promise<void>;
299
+ /**
300
+ * Send entries to external endpoint
301
+ */
302
+ private send;
303
+ /**
304
+ * Serialize entry for transmission
305
+ */
306
+ private serialize;
307
+ /**
308
+ * Sleep helper
309
+ */
310
+ private sleep;
311
+ /**
312
+ * Get buffer size (for monitoring)
313
+ */
314
+ getBufferSize(): number;
315
+ }
316
+ /**
317
+ * Create an external store
318
+ */
319
+ declare function createExternalStore(options: ExternalStoreOptions): ExternalStore;
320
+ /**
321
+ * Create a Datadog store
322
+ */
323
+ declare function createDatadogStore(options: {
324
+ apiKey: string;
325
+ site?: 'datadoghq.com' | 'datadoghq.eu' | 'us3.datadoghq.com' | 'us5.datadoghq.com';
326
+ service?: string;
327
+ source?: string;
328
+ tags?: string[];
329
+ batchSize?: number;
330
+ flushInterval?: number;
331
+ }): ExternalStore;
332
+ /**
333
+ * Create a multi-store that writes to multiple stores
334
+ */
335
+ declare class MultiStore implements LogStore {
336
+ private stores;
337
+ constructor(stores: LogStore[]);
338
+ write(entry: AuditLogEntry): Promise<void>;
339
+ query(options: Parameters<NonNullable<LogStore['query']>>[0]): Promise<AuditLogEntry[]>;
340
+ flush(): Promise<void>;
341
+ close(): Promise<void>;
342
+ }
343
+ /**
344
+ * Create a multi-store
345
+ */
346
+ declare function createMultiStore(stores: LogStore[]): MultiStore;
347
+
348
+ /**
349
+ * JSON formatter - outputs logs as JSON strings
350
+ */
351
+ declare class JSONFormatter implements LogFormatter {
352
+ private readonly pretty;
353
+ private readonly includeTimestamp;
354
+ constructor(options?: {
355
+ pretty?: boolean;
356
+ includeTimestamp?: boolean;
357
+ });
358
+ format(entry: AuditLogEntry): string;
359
+ }
360
+ /**
361
+ * Text formatter - outputs logs as human-readable text
362
+ */
363
+ declare class TextFormatter implements LogFormatter {
364
+ private readonly template;
365
+ private readonly dateFormat;
366
+ constructor(options?: {
367
+ template?: string;
368
+ dateFormat?: 'iso' | 'utc' | 'local';
369
+ });
370
+ format(entry: AuditLogEntry): string;
371
+ private formatDate;
372
+ }
373
+ /**
374
+ * CLF (Common Log Format) formatter
375
+ * Apache/Nginx style: host ident authuser date request status bytes
376
+ */
377
+ declare class CLFFormatter implements LogFormatter {
378
+ format(entry: AuditLogEntry): string;
379
+ private formatCLFDate;
380
+ }
381
+ /**
382
+ * Structured formatter for ELK/Splunk
383
+ * Outputs key=value pairs
384
+ */
385
+ declare class StructuredFormatter implements LogFormatter {
386
+ private readonly delimiter;
387
+ private readonly kvSeparator;
388
+ constructor(options?: {
389
+ delimiter?: string;
390
+ kvSeparator?: string;
391
+ });
392
+ format(entry: AuditLogEntry): string;
393
+ private pair;
394
+ private escape;
395
+ }
396
+ /**
397
+ * Create a JSON formatter
398
+ */
399
+ declare function createJSONFormatter(options?: {
400
+ pretty?: boolean;
401
+ }): JSONFormatter;
402
+ /**
403
+ * Create a text formatter
404
+ */
405
+ declare function createTextFormatter(options?: {
406
+ template?: string;
407
+ dateFormat?: 'iso' | 'utc' | 'local';
408
+ }): TextFormatter;
409
+ /**
410
+ * Create a CLF formatter
411
+ */
412
+ declare function createCLFFormatter(): CLFFormatter;
413
+ /**
414
+ * Create a structured formatter
415
+ */
416
+ declare function createStructuredFormatter(options?: {
417
+ delimiter?: string;
418
+ kvSeparator?: string;
419
+ }): StructuredFormatter;
420
+
421
+ /**
422
+ * Default PII fields to redact
423
+ */
424
+ declare const DEFAULT_PII_FIELDS: string[];
425
+ /**
426
+ * Mask a value with asterisks
427
+ */
428
+ declare function mask(value: string, options?: {
429
+ char?: string;
430
+ preserveLength?: boolean;
431
+ showFirst?: number;
432
+ showLast?: number;
433
+ }): string;
434
+ /**
435
+ * Simple hash function for Edge Runtime compatibility
436
+ * Uses a fast, deterministic string hash (not cryptographic)
437
+ */
438
+ declare function hash(value: string, salt?: string): string;
439
+ /**
440
+ * Redact a single value
441
+ */
442
+ declare function redactValue(value: unknown, field: string, config: PIIConfig): unknown;
443
+ /**
444
+ * Redact PII from an object recursively
445
+ */
446
+ declare function redactObject<T>(obj: T, config: PIIConfig, path?: string): T;
447
+ /**
448
+ * Create a redactor function with preset config
449
+ */
450
+ declare function createRedactor(config?: Partial<PIIConfig>): <T>(obj: T) => T;
451
+ /**
452
+ * Redact sensitive headers
453
+ */
454
+ declare function redactHeaders(headers: Record<string, string>, sensitiveHeaders?: string[]): Record<string, string>;
455
+ /**
456
+ * Redact query parameters
457
+ */
458
+ declare function redactQuery(query: Record<string, string>, sensitiveParams?: string[]): Record<string, string>;
459
+ /**
460
+ * Redact email (show only domain)
461
+ */
462
+ declare function redactEmail(email: string): string;
463
+ /**
464
+ * Redact credit card number (show last 4 digits)
465
+ */
466
+ declare function redactCreditCard(cardNumber: string): string;
467
+ /**
468
+ * Redact phone number
469
+ */
470
+ declare function redactPhone(phone: string): string;
471
+ /**
472
+ * Redact IP address (show only first two octets for IPv4)
473
+ */
474
+ declare function redactIP(ip: string): string;
475
+
476
+ /**
477
+ * Security event tracker
478
+ */
479
+ declare class SecurityEventTracker {
480
+ private store;
481
+ private defaultSeverity;
482
+ private onEvent?;
483
+ constructor(config: SecurityEventConfig);
484
+ /**
485
+ * Track a security event
486
+ */
487
+ track(options: {
488
+ event: SecurityEventType;
489
+ message: string;
490
+ severity?: 'low' | 'medium' | 'high' | 'critical';
491
+ source?: {
492
+ ip?: string;
493
+ userAgent?: string;
494
+ userId?: string;
495
+ };
496
+ target?: {
497
+ resource?: string;
498
+ action?: string;
499
+ userId?: string;
500
+ };
501
+ details?: Record<string, unknown>;
502
+ mitigated?: boolean;
503
+ metadata?: Record<string, unknown>;
504
+ }): Promise<SecurityEventEntry>;
505
+ /**
506
+ * Track failed authentication
507
+ */
508
+ authFailed(options: {
509
+ ip?: string;
510
+ userAgent?: string;
511
+ email?: string;
512
+ reason?: string;
513
+ metadata?: Record<string, unknown>;
514
+ }): Promise<SecurityEventEntry>;
515
+ /**
516
+ * Track successful login
517
+ */
518
+ authLogin(options: {
519
+ userId: string;
520
+ ip?: string;
521
+ userAgent?: string;
522
+ method?: string;
523
+ metadata?: Record<string, unknown>;
524
+ }): Promise<SecurityEventEntry>;
525
+ /**
526
+ * Track logout
527
+ */
528
+ authLogout(options: {
529
+ userId: string;
530
+ ip?: string;
531
+ reason?: 'user' | 'timeout' | 'forced';
532
+ metadata?: Record<string, unknown>;
533
+ }): Promise<SecurityEventEntry>;
534
+ /**
535
+ * Track permission denied
536
+ */
537
+ permissionDenied(options: {
538
+ userId?: string;
539
+ ip?: string;
540
+ resource: string;
541
+ action: string;
542
+ requiredRole?: string;
543
+ metadata?: Record<string, unknown>;
544
+ }): Promise<SecurityEventEntry>;
545
+ /**
546
+ * Track rate limit exceeded
547
+ */
548
+ rateLimitExceeded(options: {
549
+ ip?: string;
550
+ userId?: string;
551
+ endpoint: string;
552
+ limit: number;
553
+ window: string;
554
+ metadata?: Record<string, unknown>;
555
+ }): Promise<SecurityEventEntry>;
556
+ /**
557
+ * Track CSRF validation failure
558
+ */
559
+ csrfInvalid(options: {
560
+ ip?: string;
561
+ userId?: string;
562
+ endpoint: string;
563
+ reason?: string;
564
+ metadata?: Record<string, unknown>;
565
+ }): Promise<SecurityEventEntry>;
566
+ /**
567
+ * Track XSS detection
568
+ */
569
+ xssDetected(options: {
570
+ ip?: string;
571
+ userId?: string;
572
+ field: string;
573
+ payload?: string;
574
+ endpoint: string;
575
+ metadata?: Record<string, unknown>;
576
+ }): Promise<SecurityEventEntry>;
577
+ /**
578
+ * Track SQL injection detection
579
+ */
580
+ sqliDetected(options: {
581
+ ip?: string;
582
+ userId?: string;
583
+ field: string;
584
+ pattern: string;
585
+ severity?: 'low' | 'medium' | 'high';
586
+ endpoint: string;
587
+ metadata?: Record<string, unknown>;
588
+ }): Promise<SecurityEventEntry>;
589
+ /**
590
+ * Track IP block
591
+ */
592
+ ipBlocked(options: {
593
+ ip: string;
594
+ reason: string;
595
+ duration?: number;
596
+ metadata?: Record<string, unknown>;
597
+ }): Promise<SecurityEventEntry>;
598
+ /**
599
+ * Track suspicious activity
600
+ */
601
+ suspicious(options: {
602
+ ip?: string;
603
+ userId?: string;
604
+ activity: string;
605
+ severity?: 'low' | 'medium' | 'high' | 'critical';
606
+ details?: Record<string, unknown>;
607
+ metadata?: Record<string, unknown>;
608
+ }): Promise<SecurityEventEntry>;
609
+ /**
610
+ * Track custom event
611
+ */
612
+ custom(options: {
613
+ message: string;
614
+ severity?: 'low' | 'medium' | 'high' | 'critical';
615
+ source?: {
616
+ ip?: string;
617
+ userAgent?: string;
618
+ userId?: string;
619
+ };
620
+ target?: {
621
+ resource?: string;
622
+ action?: string;
623
+ };
624
+ details?: Record<string, unknown>;
625
+ metadata?: Record<string, unknown>;
626
+ }): Promise<SecurityEventEntry>;
627
+ }
628
+ /**
629
+ * Create a security event tracker
630
+ */
631
+ declare function createSecurityTracker(config: SecurityEventConfig): SecurityEventTracker;
632
+ /**
633
+ * Standalone function to track security events
634
+ */
635
+ declare function trackSecurityEvent(store: LogStore, options: {
636
+ event: SecurityEventType;
637
+ message: string;
638
+ severity?: 'low' | 'medium' | 'high' | 'critical';
639
+ source?: {
640
+ ip?: string;
641
+ userAgent?: string;
642
+ userId?: string;
643
+ };
644
+ target?: {
645
+ resource?: string;
646
+ action?: string;
647
+ userId?: string;
648
+ };
649
+ details?: Record<string, unknown>;
650
+ metadata?: Record<string, unknown>;
651
+ }): Promise<SecurityEventEntry>;
652
+
653
+ type RouteHandler = (req: NextRequest) => Response | Promise<Response>;
654
+ /**
655
+ * Audit logging middleware
656
+ */
657
+ declare function withAuditLog(handler: RouteHandler, config: AuditConfig): RouteHandler;
658
+ /**
659
+ * Create audit middleware with default console logging
660
+ */
661
+ declare function createAuditMiddleware(config: Partial<AuditConfig> & {
662
+ store: AuditConfig['store'];
663
+ }): (handler: RouteHandler) => RouteHandler;
664
+ /**
665
+ * Request logger that adds request ID to response headers
666
+ */
667
+ declare function withRequestId(handler: RouteHandler, options?: {
668
+ headerName?: string;
669
+ generateId?: () => string;
670
+ }): RouteHandler;
671
+ /**
672
+ * Simple request timing middleware
673
+ */
674
+ declare function withTiming(handler: RouteHandler, options?: {
675
+ headerName?: string;
676
+ log?: boolean;
677
+ }): RouteHandler;
678
+
679
+ export { type AuditConfig, type AuditLogEntry, CLFFormatter, ConsoleStore, type ConsoleStoreOptions, DEFAULT_PII_FIELDS, ExternalStore, type ExternalStoreOptions, type FileStoreOptions, JSONFormatter, type LogEntry, type LogFormatter, type LogLevel, type LogQueryOptions, type LogStore, MemoryStore, type MemoryStoreOptions, MultiStore, type PIIConfig, type RequestLogEntry, type SecurityEventConfig, type SecurityEventEntry, SecurityEventTracker, type SecurityEventType, StructuredFormatter, TextFormatter, createAuditMiddleware, createCLFFormatter, createConsoleStore, createDatadogStore, createExternalStore, createJSONFormatter, createMemoryStore, createMultiStore, createRedactor, createSecurityTracker, createStructuredFormatter, createTextFormatter, hash, mask, redactCreditCard, redactEmail, redactHeaders, redactIP, redactObject, redactPhone, redactQuery, redactValue, trackSecurityEvent, withAuditLog, withRequestId, withTiming };